replace GoDep by Glide
This commit is contained in:
parent
951efed1ff
commit
d9d5b058e5
152
Godeps/Godeps.json
generated
152
Godeps/Godeps.json
generated
@ -1,152 +0,0 @@
|
||||
{
|
||||
"ImportPath": "github.com/coreos/clair",
|
||||
"GoVersion": "go1.5",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "bitbucket.org/liamstask/goose/lib/goose",
|
||||
"Rev": "8488cc47d90c8a502b1c41a462a6d9cc8ee0a895"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/beorn7/perks/quantile",
|
||||
"Rev": "b965b613227fddccbfffe13eae360ed3fa822f8d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/codegangsta/negroni",
|
||||
"Comment": "v0.1-70-gc7477ad",
|
||||
"Rev": "c7477ad8e330bef55bf1ebe300cf8aa67c492d1b"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/go-systemd/journal",
|
||||
"Comment": "v4-34-g4f14f6d",
|
||||
"Rev": "4f14f6deef2da87e4aa59e6c1c1f3e02ba44c5e1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/pkg/capnslog",
|
||||
"Rev": "2c77715c4df99b5420ffcae14ead08f52104065d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/pkg/timeutil",
|
||||
"Rev": "2c77715c4df99b5420ffcae14ead08f52104065d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/davecgh/go-spew/spew",
|
||||
"Rev": "5215b55f46b2b919f50a1df0eaa5886afe4e3b3d"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/fernet/fernet-go",
|
||||
"Rev": "1b2437bc582b3cfbb341ee5a29f8ef5b42912ff2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-sql-driver/mysql",
|
||||
"Comment": "v1.2-125-gd512f20",
|
||||
"Rev": "d512f204a577a4ab037a1816604c48c9c13210be"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/protobuf/proto",
|
||||
"Rev": "5fc2294e655b78ed8a02082d37808d46c17d7e64"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/guregu/null/zero",
|
||||
"Comment": "v3-3-g79c5bd3",
|
||||
"Rev": "79c5bd36b615db4c06132321189f579c8a5fca98"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/hashicorp/golang-lru",
|
||||
"Rev": "5c7531c003d8bf158b0fe5063649a2f41a822146"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/julienschmidt/httprouter",
|
||||
"Comment": "v1.1-14-g21439ef",
|
||||
"Rev": "21439ef4d70ba4f3e2a5ed9249e7b03af4019b40"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/kylelemons/go-gypsy/yaml",
|
||||
"Comment": "go.weekly.2011-11-02-19-g42fc2c7",
|
||||
"Rev": "42fc2c7ee9b8bd0ff636cd2d7a8c0a49491044c5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/lib/pq",
|
||||
"Comment": "go1.0-cutoff-63-g11fc39a",
|
||||
"Rev": "11fc39a580a008f1f39bb3d11d984fb34ed778d9"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/mattn/go-sqlite3",
|
||||
"Comment": "v1.1.0-30-g5510da3",
|
||||
"Rev": "5510da399572b4962c020184bb291120c0a412e2"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
||||
"Rev": "d0c3fe89de86839aecf2e0579c40ba3bb336a453"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/pborman/uuid",
|
||||
"Rev": "dee7705ef7b324f27ceb85a121c61f2c2e8ce988"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/pmezard/go-difflib/difflib",
|
||||
"Rev": "e8554b8641db39598be7f6342874b958f12ae1d4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/prometheus/client_golang/prometheus",
|
||||
"Comment": "0.7.0-68-g67994f1",
|
||||
"Rev": "67994f177195311c3ea3d4407ed0175e34a4256f"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/prometheus/client_model/go",
|
||||
"Comment": "model-0.0.2-12-gfa8ad6f",
|
||||
"Rev": "fa8ad6fec33561be4280a8f0514318c79d7f6cb6"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/prometheus/common/expfmt",
|
||||
"Rev": "dba5e39d4516169e840def50e507ef5f21b985f9"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/prometheus/common/internal/bitbucket.org/ww/goautoneg",
|
||||
"Rev": "dba5e39d4516169e840def50e507ef5f21b985f9"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/prometheus/common/model",
|
||||
"Rev": "dba5e39d4516169e840def50e507ef5f21b985f9"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/prometheus/procfs",
|
||||
"Rev": "406e5b7bfd8201a36e2bb5f7bdae0b03380c2ce8"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/stretchr/testify/assert",
|
||||
"Comment": "v1.0-91-g5b9da39",
|
||||
"Rev": "5b9da39b66e8e994455c2525c4421c8cc00a7f93"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/tylerb/graceful",
|
||||
"Comment": "v1.2.3",
|
||||
"Rev": "48afeb21e2fcbcff0f30bd5ad6b97747b0fae38e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ziutek/mymysql/godrv",
|
||||
"Comment": "v1.5.4-13-g75ce5fb",
|
||||
"Rev": "75ce5fbba34b1912a3641adbd58cf317d7315821"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ziutek/mymysql/mysql",
|
||||
"Comment": "v1.5.4-13-g75ce5fb",
|
||||
"Rev": "75ce5fbba34b1912a3641adbd58cf317d7315821"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/ziutek/mymysql/native",
|
||||
"Comment": "v1.5.4-13-g75ce5fb",
|
||||
"Rev": "75ce5fbba34b1912a3641adbd58cf317d7315821"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/netutil",
|
||||
"Rev": "1d7a0b2100da090d8b02afcfb42f97e2c77e71a4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "gopkg.in/yaml.v2",
|
||||
"Rev": "f7716cbe52baa25d2e9b0d0da546fcf909fc16b4"
|
||||
}
|
||||
]
|
||||
}
|
5
Godeps/Readme
generated
5
Godeps/Readme
generated
@ -1,5 +0,0 @@
|
||||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
105
glide.lock
generated
Normal file
105
glide.lock
generated
Normal file
@ -0,0 +1,105 @@
|
||||
hash: 6787deaa403181a20c0c0ba7e3135ba0f300281e7b3f98294593aa9f60563627
|
||||
updated: 2016-06-07T06:56:38.153128536+02:00
|
||||
imports:
|
||||
- name: bitbucket.org/liamstask/goose
|
||||
version: 8488cc47d90c8a502b1c41a462a6d9cc8ee0a895
|
||||
subpackages:
|
||||
- lib/goose
|
||||
- name: github.com/beorn7/perks
|
||||
version: b965b613227fddccbfffe13eae360ed3fa822f8d
|
||||
subpackages:
|
||||
- quantile
|
||||
- name: github.com/codegangsta/negroni
|
||||
version: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b
|
||||
- name: github.com/coreos/go-systemd
|
||||
version: 4f14f6deef2da87e4aa59e6c1c1f3e02ba44c5e1
|
||||
subpackages:
|
||||
- journal
|
||||
- name: github.com/coreos/pkg
|
||||
version: 2c77715c4df99b5420ffcae14ead08f52104065d
|
||||
subpackages:
|
||||
- capnslog
|
||||
- timeutil
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/fatih/color
|
||||
version: 1b35f289c47d5c73c398cea8e006b7bcb6234a96
|
||||
- name: github.com/fernet/fernet-go
|
||||
version: 1b2437bc582b3cfbb341ee5a29f8ef5b42912ff2
|
||||
- name: github.com/go-sql-driver/mysql
|
||||
version: d512f204a577a4ab037a1816604c48c9c13210be
|
||||
- name: github.com/golang/protobuf
|
||||
version: 5fc2294e655b78ed8a02082d37808d46c17d7e64
|
||||
subpackages:
|
||||
- proto
|
||||
- name: github.com/guregu/null
|
||||
version: 79c5bd36b615db4c06132321189f579c8a5fca98
|
||||
subpackages:
|
||||
- zero
|
||||
- name: github.com/hashicorp/golang-lru
|
||||
version: 5c7531c003d8bf158b0fe5063649a2f41a822146
|
||||
subpackages:
|
||||
- simplelru
|
||||
- name: github.com/julienschmidt/httprouter
|
||||
version: 21439ef4d70ba4f3e2a5ed9249e7b03af4019b40
|
||||
- name: github.com/kr/text
|
||||
version: 7cafcd837844e784b526369c9bce262804aebc60
|
||||
- name: github.com/kylelemons/go-gypsy
|
||||
version: 42fc2c7ee9b8bd0ff636cd2d7a8c0a49491044c5
|
||||
subpackages:
|
||||
- yaml
|
||||
- name: github.com/lib/pq
|
||||
version: 11fc39a580a008f1f39bb3d11d984fb34ed778d9
|
||||
subpackages:
|
||||
- oid
|
||||
- name: github.com/mattn/go-sqlite3
|
||||
version: 5510da399572b4962c020184bb291120c0a412e2
|
||||
- name: github.com/matttproud/golang_protobuf_extensions
|
||||
version: d0c3fe89de86839aecf2e0579c40ba3bb336a453
|
||||
subpackages:
|
||||
- pbutil
|
||||
- name: github.com/pborman/uuid
|
||||
version: dee7705ef7b324f27ceb85a121c61f2c2e8ce988
|
||||
- name: github.com/pmezard/go-difflib
|
||||
version: e8554b8641db39598be7f6342874b958f12ae1d4
|
||||
subpackages:
|
||||
- difflib
|
||||
- name: github.com/prometheus/client_golang
|
||||
version: 67994f177195311c3ea3d4407ed0175e34a4256f
|
||||
subpackages:
|
||||
- prometheus
|
||||
- name: github.com/prometheus/client_model
|
||||
version: fa8ad6fec33561be4280a8f0514318c79d7f6cb6
|
||||
subpackages:
|
||||
- go
|
||||
- name: github.com/prometheus/common
|
||||
version: dba5e39d4516169e840def50e507ef5f21b985f9
|
||||
subpackages:
|
||||
- expfmt
|
||||
- internal/bitbucket.org/ww/goautoneg
|
||||
- model
|
||||
- name: github.com/prometheus/procfs
|
||||
version: 406e5b7bfd8201a36e2bb5f7bdae0b03380c2ce8
|
||||
- name: github.com/shiena/ansicolor
|
||||
version: a422bbe96644373c5753384a59d678f7d261ff10
|
||||
- name: github.com/stretchr/testify
|
||||
version: 5b9da39b66e8e994455c2525c4421c8cc00a7f93
|
||||
subpackages:
|
||||
- assert
|
||||
- name: github.com/tylerb/graceful
|
||||
version: 84177357ab104029f9237abcb52339a7b80760ef
|
||||
- name: github.com/ziutek/mymysql
|
||||
version: 75ce5fbba34b1912a3641adbd58cf317d7315821
|
||||
subpackages:
|
||||
- godrv
|
||||
- mysql
|
||||
- native
|
||||
- name: golang.org/x/net
|
||||
version: 1d7a0b2100da090d8b02afcfb42f97e2c77e71a4
|
||||
subpackages:
|
||||
- netutil
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
|
||||
devImports: []
|
96
glide.yaml
Normal file
96
glide.yaml
Normal file
@ -0,0 +1,96 @@
|
||||
package: github.com/coreos/clair
|
||||
import:
|
||||
- package: bitbucket.org/liamstask/goose
|
||||
version: 8488cc47d90c8a502b1c41a462a6d9cc8ee0a895
|
||||
subpackages:
|
||||
- lib/goose
|
||||
- package: github.com/beorn7/perks
|
||||
version: b965b613227fddccbfffe13eae360ed3fa822f8d
|
||||
subpackages:
|
||||
- quantile
|
||||
- package: github.com/codegangsta/negroni
|
||||
version: c7477ad8e330bef55bf1ebe300cf8aa67c492d1b
|
||||
- package: github.com/coreos/go-systemd
|
||||
version: 4f14f6deef2da87e4aa59e6c1c1f3e02ba44c5e1
|
||||
subpackages:
|
||||
- journal
|
||||
- package: github.com/coreos/pkg
|
||||
version: 2c77715c4df99b5420ffcae14ead08f52104065d
|
||||
subpackages:
|
||||
- capnslog
|
||||
- timeutil
|
||||
- package: github.com/davecgh/go-spew
|
||||
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||
subpackages:
|
||||
- spew
|
||||
- package: github.com/fernet/fernet-go
|
||||
version: 1b2437bc582b3cfbb341ee5a29f8ef5b42912ff2
|
||||
- package: github.com/go-sql-driver/mysql
|
||||
version: d512f204a577a4ab037a1816604c48c9c13210be
|
||||
- package: github.com/golang/protobuf
|
||||
version: 5fc2294e655b78ed8a02082d37808d46c17d7e64
|
||||
subpackages:
|
||||
- proto
|
||||
- package: github.com/guregu/null
|
||||
version: 79c5bd36b615db4c06132321189f579c8a5fca98
|
||||
subpackages:
|
||||
- zero
|
||||
- package: github.com/hashicorp/golang-lru
|
||||
version: 5c7531c003d8bf158b0fe5063649a2f41a822146
|
||||
- package: github.com/julienschmidt/httprouter
|
||||
version: 21439ef4d70ba4f3e2a5ed9249e7b03af4019b40
|
||||
- package: github.com/kylelemons/go-gypsy
|
||||
version: 42fc2c7ee9b8bd0ff636cd2d7a8c0a49491044c5
|
||||
subpackages:
|
||||
- yaml
|
||||
- package: github.com/lib/pq
|
||||
version: 11fc39a580a008f1f39bb3d11d984fb34ed778d9
|
||||
- package: github.com/mattn/go-sqlite3
|
||||
version: 5510da399572b4962c020184bb291120c0a412e2
|
||||
- package: github.com/matttproud/golang_protobuf_extensions
|
||||
version: d0c3fe89de86839aecf2e0579c40ba3bb336a453
|
||||
subpackages:
|
||||
- pbutil
|
||||
- package: github.com/pborman/uuid
|
||||
version: dee7705ef7b324f27ceb85a121c61f2c2e8ce988
|
||||
- package: github.com/pmezard/go-difflib
|
||||
version: e8554b8641db39598be7f6342874b958f12ae1d4
|
||||
subpackages:
|
||||
- difflib
|
||||
- package: github.com/prometheus/client_golang
|
||||
version: 67994f177195311c3ea3d4407ed0175e34a4256f
|
||||
subpackages:
|
||||
- prometheus
|
||||
- package: github.com/prometheus/client_model
|
||||
version: fa8ad6fec33561be4280a8f0514318c79d7f6cb6
|
||||
subpackages:
|
||||
- go
|
||||
- package: github.com/prometheus/common
|
||||
version: dba5e39d4516169e840def50e507ef5f21b985f9
|
||||
subpackages:
|
||||
- expfmt
|
||||
- internal/bitbucket.org/ww/goautoneg
|
||||
- model
|
||||
- package: github.com/prometheus/procfs
|
||||
version: 406e5b7bfd8201a36e2bb5f7bdae0b03380c2ce8
|
||||
- package: github.com/stretchr/testify
|
||||
version: 5b9da39b66e8e994455c2525c4421c8cc00a7f93
|
||||
subpackages:
|
||||
- assert
|
||||
- package: github.com/tylerb/graceful
|
||||
version: ^1.2.3
|
||||
- package: github.com/ziutek/mymysql
|
||||
version: 75ce5fbba34b1912a3641adbd58cf317d7315821
|
||||
subpackages:
|
||||
- godrv
|
||||
- mysql
|
||||
- native
|
||||
- package: golang.org/x/net
|
||||
version: 1d7a0b2100da090d8b02afcfb42f97e2c77e71a4
|
||||
subpackages:
|
||||
- netutil
|
||||
- package: gopkg.in/yaml.v2
|
||||
version: f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
|
||||
- package: github.com/fatih/color
|
||||
version: ^0.1.0
|
||||
- package: github.com/kr/text
|
4
vendor/bitbucket.org/liamstask/goose/.gitignore
generated
vendored
Normal file
4
vendor/bitbucket.org/liamstask/goose/.gitignore
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.DS_Store
|
||||
cmd/goose/goose*
|
||||
*.swp
|
||||
*.test
|
7
vendor/bitbucket.org/liamstask/goose/MIT-License.md
generated
vendored
Normal file
7
vendor/bitbucket.org/liamstask/goose/MIT-License.md
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
Copyright (c) <2012> <Liam Staskawicz>
|
||||
|
||||
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.
|
265
vendor/bitbucket.org/liamstask/goose/README.md
generated
vendored
Normal file
265
vendor/bitbucket.org/liamstask/goose/README.md
generated
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
# goose
|
||||
|
||||
goose is a database migration tool.
|
||||
|
||||
You can manage your database's evolution by creating incremental SQL or Go scripts.
|
||||
|
||||
[![Build Status](https://drone.io/bitbucket.org/liamstask/goose/status.png)](https://drone.io/bitbucket.org/liamstask/goose/latest)
|
||||
|
||||
# Install
|
||||
|
||||
$ go get bitbucket.org/liamstask/goose/cmd/goose
|
||||
|
||||
This will install the `goose` binary to your `$GOPATH/bin` directory.
|
||||
|
||||
You can also build goose into your own applications by importing `bitbucket.org/liamstask/goose/lib/goose`. Documentation is available at [godoc.org](http://godoc.org/bitbucket.org/liamstask/goose/lib/goose).
|
||||
|
||||
NOTE: the API is still new, and may undergo some changes.
|
||||
|
||||
# Usage
|
||||
|
||||
goose provides several commands to help manage your database schema.
|
||||
|
||||
## create
|
||||
|
||||
Create a new Go migration.
|
||||
|
||||
$ goose create AddSomeColumns
|
||||
$ goose: created db/migrations/20130106093224_AddSomeColumns.go
|
||||
|
||||
Edit the newly created script to define the behavior of your migration.
|
||||
|
||||
You can also create an SQL migration:
|
||||
|
||||
$ goose create AddSomeColumns sql
|
||||
$ goose: created db/migrations/20130106093224_AddSomeColumns.sql
|
||||
|
||||
## up
|
||||
|
||||
Apply all available migrations.
|
||||
|
||||
$ goose up
|
||||
$ goose: migrating db environment 'development', current version: 0, target: 3
|
||||
$ OK 001_basics.sql
|
||||
$ OK 002_next.sql
|
||||
$ OK 003_and_again.go
|
||||
|
||||
### option: pgschema
|
||||
|
||||
Use the `pgschema` flag with the `up` command specify a postgres schema.
|
||||
|
||||
$ goose -pgschema=my_schema_name up
|
||||
$ goose: migrating db environment 'development', current version: 0, target: 3
|
||||
$ OK 001_basics.sql
|
||||
$ OK 002_next.sql
|
||||
$ OK 003_and_again.go
|
||||
|
||||
## down
|
||||
|
||||
Roll back a single migration from the current version.
|
||||
|
||||
$ goose down
|
||||
$ goose: migrating db environment 'development', current version: 3, target: 2
|
||||
$ OK 003_and_again.go
|
||||
|
||||
## redo
|
||||
|
||||
Roll back the most recently applied migration, then run it again.
|
||||
|
||||
$ goose redo
|
||||
$ goose: migrating db environment 'development', current version: 3, target: 2
|
||||
$ OK 003_and_again.go
|
||||
$ goose: migrating db environment 'development', current version: 2, target: 3
|
||||
$ OK 003_and_again.go
|
||||
|
||||
## status
|
||||
|
||||
Print the status of all migrations:
|
||||
|
||||
$ goose status
|
||||
$ goose: status for environment 'development'
|
||||
$ Applied At Migration
|
||||
$ =======================================
|
||||
$ Sun Jan 6 11:25:03 2013 -- 001_basics.sql
|
||||
$ Sun Jan 6 11:25:03 2013 -- 002_next.sql
|
||||
$ Pending -- 003_and_again.go
|
||||
|
||||
## dbversion
|
||||
|
||||
Print the current version of the database:
|
||||
|
||||
$ goose dbversion
|
||||
$ goose: dbversion 002
|
||||
|
||||
|
||||
`goose -h` provides more detailed info on each command.
|
||||
|
||||
|
||||
# Migrations
|
||||
|
||||
goose supports migrations written in SQL or in Go - see the `goose create` command above for details on how to generate them.
|
||||
|
||||
## SQL Migrations
|
||||
|
||||
A sample SQL migration looks like:
|
||||
|
||||
```sql
|
||||
-- +goose Up
|
||||
CREATE TABLE post (
|
||||
id int NOT NULL,
|
||||
title text,
|
||||
body text,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE post;
|
||||
```
|
||||
|
||||
Notice the annotations in the comments. Any statements following `-- +goose Up` will be executed as part of a forward migration, and any statements following `-- +goose Down` will be executed as part of a rollback.
|
||||
|
||||
By default, SQL statements are delimited by semicolons - in fact, query statements must end with a semicolon to be properly recognized by goose.
|
||||
|
||||
More complex statements (PL/pgSQL) that have semicolons within them must be annotated with `-- +goose StatementBegin` and `-- +goose StatementEnd` to be properly recognized. For example:
|
||||
|
||||
```sql
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
CREATE OR REPLACE FUNCTION histories_partition_creation( DATE, DATE )
|
||||
returns void AS $$
|
||||
DECLARE
|
||||
create_query text;
|
||||
BEGIN
|
||||
FOR create_query IN SELECT
|
||||
'CREATE TABLE IF NOT EXISTS histories_'
|
||||
|| TO_CHAR( d, 'YYYY_MM' )
|
||||
|| ' ( CHECK( created_at >= timestamp '''
|
||||
|| TO_CHAR( d, 'YYYY-MM-DD 00:00:00' )
|
||||
|| ''' AND created_at < timestamp '''
|
||||
|| TO_CHAR( d + INTERVAL '1 month', 'YYYY-MM-DD 00:00:00' )
|
||||
|| ''' ) ) inherits ( histories );'
|
||||
FROM generate_series( $1, $2, '1 month' ) AS d
|
||||
LOOP
|
||||
EXECUTE create_query;
|
||||
END LOOP; -- LOOP END
|
||||
END; -- FUNCTION END
|
||||
$$
|
||||
language plpgsql;
|
||||
-- +goose StatementEnd
|
||||
```
|
||||
|
||||
## Go Migrations
|
||||
|
||||
A sample Go migration looks like:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Up_20130106222315(txn *sql.Tx) {
|
||||
fmt.Println("Hello from migration 20130106222315 Up!")
|
||||
}
|
||||
|
||||
func Down_20130106222315(txn *sql.Tx) {
|
||||
fmt.Println("Hello from migration 20130106222315 Down!")
|
||||
}
|
||||
```
|
||||
|
||||
`Up_20130106222315()` will be executed as part of a forward migration, and `Down_20130106222315()` will be executed as part of a rollback.
|
||||
|
||||
The numeric portion of the function name (`20130106222315`) must be the leading portion of migration's filename, such as `20130106222315_descriptive_name.go`. `goose create` does this by default.
|
||||
|
||||
A transaction is provided, rather than the DB instance directly, since goose also needs to record the schema version within the same transaction. Each migration should run as a single transaction to ensure DB integrity, so it's good practice anyway.
|
||||
|
||||
|
||||
# Configuration
|
||||
|
||||
goose expects you to maintain a folder (typically called "db"), which contains the following:
|
||||
|
||||
* a `dbconf.yml` file that describes the database configurations you'd like to use
|
||||
* a folder called "migrations" which contains `.sql` and/or `.go` scripts that implement your migrations
|
||||
|
||||
You may use the `-path` option to specify an alternate location for the folder containing your config and migrations.
|
||||
|
||||
A sample `dbconf.yml` looks like
|
||||
|
||||
```yml
|
||||
development:
|
||||
driver: postgres
|
||||
open: user=liam dbname=tester sslmode=disable
|
||||
```
|
||||
|
||||
Here, `development` specifies the name of the environment, and the `driver` and `open` elements are passed directly to database/sql to access the specified database.
|
||||
|
||||
You may include as many environments as you like, and you can use the `-env` command line option to specify which one to use. goose defaults to using an environment called `development`.
|
||||
|
||||
goose will expand environment variables in the `open` element. For an example, see the Heroku section below.
|
||||
|
||||
## Other Drivers
|
||||
goose knows about some common SQL drivers, but it can still be used to run Go-based migrations with any driver supported by `database/sql`. An import path and known dialect are required.
|
||||
|
||||
Currently, available dialects are: "postgres", "mysql", or "sqlite3"
|
||||
|
||||
To run Go-based migrations with another driver, specify its import path and dialect, as shown below.
|
||||
|
||||
```yml
|
||||
customdriver:
|
||||
driver: custom
|
||||
open: custom open string
|
||||
import: github.com/custom/driver
|
||||
dialect: mysql
|
||||
```
|
||||
|
||||
NOTE: Because migrations written in SQL are executed directly by the goose binary, only drivers compiled into goose may be used for these migrations.
|
||||
|
||||
## Using goose with Heroku
|
||||
|
||||
These instructions assume that you're using [Keith Rarick's Heroku Go buildpack](https://github.com/kr/heroku-buildpack-go). First, add a file to your project called (e.g.) `install_goose.go` to trigger building of the goose executable during deployment, with these contents:
|
||||
|
||||
```go
|
||||
// use build constraints to work around http://code.google.com/p/go/issues/detail?id=4210
|
||||
// +build heroku
|
||||
|
||||
// note: need at least one blank line after build constraint
|
||||
package main
|
||||
|
||||
import _ "bitbucket.org/liamstask/goose/cmd/goose"
|
||||
```
|
||||
|
||||
[Set up your Heroku database(s) as usual.](https://devcenter.heroku.com/articles/heroku-postgresql)
|
||||
|
||||
Then make use of environment variable expansion in your `dbconf.yml`:
|
||||
|
||||
```yml
|
||||
production:
|
||||
driver: postgres
|
||||
open: $DATABASE_URL
|
||||
```
|
||||
|
||||
To run goose in production, use `heroku run`:
|
||||
|
||||
heroku run goose -env production up
|
||||
|
||||
# Contributors
|
||||
|
||||
Thank you!
|
||||
|
||||
* Josh Bleecher Snyder (josharian)
|
||||
* Abigail Walthall (ghthor)
|
||||
* Daniel Heath (danielrheath)
|
||||
* Chris Baynes (chris_baynes)
|
||||
* Michael Gerow (gerow)
|
||||
* Vytautas Šaltenis (rtfb)
|
||||
* James Cooper (coopernurse)
|
||||
* Gyepi Sam (gyepisam)
|
||||
* Matt Sherman (clipperhouse)
|
||||
* runner_mei
|
||||
* John Luebs (jkl1337)
|
||||
* Luke Hutton (lukehutton)
|
||||
* Kevin Gorjan (kevingorjan)
|
||||
* Brendan Fosberry (Fozz)
|
||||
* Nate Guerin (gusennan)
|
27
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd.go
generated
vendored
Normal file
27
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
)
|
||||
|
||||
// shamelessly snagged from the go tool
|
||||
// each command gets its own set of args,
|
||||
// defines its own entry point, and provides its own help
|
||||
type Command struct {
|
||||
Run func(cmd *Command, args ...string)
|
||||
Flag flag.FlagSet
|
||||
|
||||
Name string
|
||||
Usage string
|
||||
|
||||
Summary string
|
||||
Help string
|
||||
}
|
||||
|
||||
func (c *Command) Exec(args []string) {
|
||||
c.Flag.Usage = func() {
|
||||
// helpFunc(c, c.Name)
|
||||
}
|
||||
c.Flag.Parse(args)
|
||||
c.Run(c, c.Flag.Args()...)
|
||||
}
|
51
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd_create.go
generated
vendored
Normal file
51
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd_create.go
generated
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/liamstask/goose/lib/goose"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
var createCmd = &Command{
|
||||
Name: "create",
|
||||
Usage: "",
|
||||
Summary: "Create the scaffolding for a new migration",
|
||||
Help: `create extended help here...`,
|
||||
Run: createRun,
|
||||
}
|
||||
|
||||
func createRun(cmd *Command, args ...string) {
|
||||
|
||||
if len(args) < 1 {
|
||||
log.Fatal("goose create: migration name required")
|
||||
}
|
||||
|
||||
migrationType := "go" // default to Go migrations
|
||||
if len(args) >= 2 {
|
||||
migrationType = args[1]
|
||||
}
|
||||
|
||||
conf, err := dbConfFromFlags()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(conf.MigrationsDir, 0777); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
n, err := goose.CreateMigration(args[0], migrationType, conf.MigrationsDir, time.Now())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
a, e := filepath.Abs(n)
|
||||
if e != nil {
|
||||
log.Fatal(e)
|
||||
}
|
||||
|
||||
fmt.Println("goose: created", a)
|
||||
}
|
29
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd_dbversion.go
generated
vendored
Normal file
29
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd_dbversion.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/liamstask/goose/lib/goose"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
var dbVersionCmd = &Command{
|
||||
Name: "dbversion",
|
||||
Usage: "",
|
||||
Summary: "Print the current version of the database",
|
||||
Help: `dbversion extended help here...`,
|
||||
Run: dbVersionRun,
|
||||
}
|
||||
|
||||
func dbVersionRun(cmd *Command, args ...string) {
|
||||
conf, err := dbConfFromFlags()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
current, err := goose.GetDBVersion(conf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("goose: dbversion %v\n", current)
|
||||
}
|
36
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd_down.go
generated
vendored
Normal file
36
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd_down.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/liamstask/goose/lib/goose"
|
||||
"log"
|
||||
)
|
||||
|
||||
var downCmd = &Command{
|
||||
Name: "down",
|
||||
Usage: "",
|
||||
Summary: "Roll back the version by 1",
|
||||
Help: `down extended help here...`,
|
||||
Run: downRun,
|
||||
}
|
||||
|
||||
func downRun(cmd *Command, args ...string) {
|
||||
|
||||
conf, err := dbConfFromFlags()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
current, err := goose.GetDBVersion(conf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
previous, err := goose.GetPreviousDBVersion(conf.MigrationsDir, current)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err = goose.RunMigrations(conf, conf.MigrationsDir, previous); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
39
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd_redo.go
generated
vendored
Normal file
39
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd_redo.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/liamstask/goose/lib/goose"
|
||||
"log"
|
||||
)
|
||||
|
||||
var redoCmd = &Command{
|
||||
Name: "redo",
|
||||
Usage: "",
|
||||
Summary: "Re-run the latest migration",
|
||||
Help: `redo extended help here...`,
|
||||
Run: redoRun,
|
||||
}
|
||||
|
||||
func redoRun(cmd *Command, args ...string) {
|
||||
conf, err := dbConfFromFlags()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
current, err := goose.GetDBVersion(conf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
previous, err := goose.GetPreviousDBVersion(conf.MigrationsDir, current)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := goose.RunMigrations(conf, conf.MigrationsDir, previous); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := goose.RunMigrations(conf, conf.MigrationsDir, current); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
77
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd_status.go
generated
vendored
Normal file
77
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd_status.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/liamstask/goose/lib/goose"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
var statusCmd = &Command{
|
||||
Name: "status",
|
||||
Usage: "",
|
||||
Summary: "dump the migration status for the current DB",
|
||||
Help: `status extended help here...`,
|
||||
Run: statusRun,
|
||||
}
|
||||
|
||||
type StatusData struct {
|
||||
Source string
|
||||
Status string
|
||||
}
|
||||
|
||||
func statusRun(cmd *Command, args ...string) {
|
||||
|
||||
conf, err := dbConfFromFlags()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// collect all migrations
|
||||
min := int64(0)
|
||||
max := int64((1 << 63) - 1)
|
||||
migrations, e := goose.CollectMigrations(conf.MigrationsDir, min, max)
|
||||
if e != nil {
|
||||
log.Fatal(e)
|
||||
}
|
||||
|
||||
db, e := goose.OpenDBFromDBConf(conf)
|
||||
if e != nil {
|
||||
log.Fatal("couldn't open DB:", e)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// must ensure that the version table exists if we're running on a pristine DB
|
||||
if _, e := goose.EnsureDBVersion(conf, db); e != nil {
|
||||
log.Fatal(e)
|
||||
}
|
||||
|
||||
fmt.Printf("goose: status for environment '%v'\n", conf.Env)
|
||||
fmt.Println(" Applied At Migration")
|
||||
fmt.Println(" =======================================")
|
||||
for _, m := range migrations {
|
||||
printMigrationStatus(db, m.Version, filepath.Base(m.Source))
|
||||
}
|
||||
}
|
||||
|
||||
func printMigrationStatus(db *sql.DB, version int64, script string) {
|
||||
var row goose.MigrationRecord
|
||||
q := fmt.Sprintf("SELECT tstamp, is_applied FROM goose_db_version WHERE version_id=%d ORDER BY tstamp DESC LIMIT 1", version)
|
||||
e := db.QueryRow(q).Scan(&row.TStamp, &row.IsApplied)
|
||||
|
||||
if e != nil && e != sql.ErrNoRows {
|
||||
log.Fatal(e)
|
||||
}
|
||||
|
||||
var appliedAt string
|
||||
|
||||
if row.IsApplied {
|
||||
appliedAt = row.TStamp.Format(time.ANSIC)
|
||||
} else {
|
||||
appliedAt = "Pending"
|
||||
}
|
||||
|
||||
fmt.Printf(" %-24s -- %v\n", appliedAt, script)
|
||||
}
|
31
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd_up.go
generated
vendored
Normal file
31
vendor/bitbucket.org/liamstask/goose/cmd/goose/cmd_up.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/liamstask/goose/lib/goose"
|
||||
"log"
|
||||
)
|
||||
|
||||
var upCmd = &Command{
|
||||
Name: "up",
|
||||
Usage: "",
|
||||
Summary: "Migrate the DB to the most recent version available",
|
||||
Help: `up extended help here...`,
|
||||
Run: upRun,
|
||||
}
|
||||
|
||||
func upRun(cmd *Command, args ...string) {
|
||||
|
||||
conf, err := dbConfFromFlags()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
target, err := goose.GetMostRecentDBVersion(conf.MigrationsDir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := goose.RunMigrations(conf, conf.MigrationsDir, target); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
78
vendor/bitbucket.org/liamstask/goose/cmd/goose/main.go
generated
vendored
Normal file
78
vendor/bitbucket.org/liamstask/goose/cmd/goose/main.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bitbucket.org/liamstask/goose/lib/goose"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// global options. available to any subcommands.
|
||||
var flagPath = flag.String("path", "db", "folder containing db info")
|
||||
var flagEnv = flag.String("env", "development", "which DB environment to use")
|
||||
var flagPgSchema = flag.String("pgschema", "", "which postgres-schema to migrate (default = none)")
|
||||
|
||||
// helper to create a DBConf from the given flags
|
||||
func dbConfFromFlags() (dbconf *goose.DBConf, err error) {
|
||||
return goose.NewDBConf(*flagPath, *flagEnv, *flagPgSchema)
|
||||
}
|
||||
|
||||
var commands = []*Command{
|
||||
upCmd,
|
||||
downCmd,
|
||||
redoCmd,
|
||||
statusCmd,
|
||||
createCmd,
|
||||
dbVersionCmd,
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
|
||||
args := flag.Args()
|
||||
if len(args) == 0 || args[0] == "-h" {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
var cmd *Command
|
||||
name := args[0]
|
||||
for _, c := range commands {
|
||||
if strings.HasPrefix(c.Name, name) {
|
||||
cmd = c
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if cmd == nil {
|
||||
fmt.Printf("error: unknown command %q\n", name)
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cmd.Exec(args[1:])
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Print(usagePrefix)
|
||||
flag.PrintDefaults()
|
||||
usageTmpl.Execute(os.Stdout, commands)
|
||||
}
|
||||
|
||||
var usagePrefix = `
|
||||
goose is a database migration management system for Go projects.
|
||||
|
||||
Usage:
|
||||
goose [options] <subcommand> [subcommand options]
|
||||
|
||||
Options:
|
||||
`
|
||||
var usageTmpl = template.Must(template.New("usage").Parse(
|
||||
`
|
||||
Commands:{{range .}}
|
||||
{{.Name | printf "%-10s"}} {{.Summary}}{{end}}
|
||||
`))
|
22
vendor/bitbucket.org/liamstask/goose/db-sample/dbconf.yml
generated
vendored
Normal file
22
vendor/bitbucket.org/liamstask/goose/db-sample/dbconf.yml
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
test:
|
||||
driver: postgres
|
||||
open: user=liam dbname=tester sslmode=disable
|
||||
|
||||
development:
|
||||
driver: postgres
|
||||
open: user=liam dbname=tester sslmode=disable
|
||||
|
||||
production:
|
||||
driver: postgres
|
||||
open: user=liam dbname=tester sslmode=verify-full
|
||||
|
||||
customimport:
|
||||
driver: customdriver
|
||||
open: customdriver open
|
||||
import: github.com/custom/driver
|
||||
dialect: mysql
|
||||
|
||||
environment_variable_config:
|
||||
driver: $DB_DRIVER
|
||||
open: $DATABASE_URL
|
11
vendor/bitbucket.org/liamstask/goose/db-sample/migrations/001_basics.sql
generated
vendored
Normal file
11
vendor/bitbucket.org/liamstask/goose/db-sample/migrations/001_basics.sql
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
-- +goose Up
|
||||
CREATE TABLE post (
|
||||
id int NOT NULL,
|
||||
title text,
|
||||
body text,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE post;
|
12
vendor/bitbucket.org/liamstask/goose/db-sample/migrations/002_next.sql
generated
vendored
Normal file
12
vendor/bitbucket.org/liamstask/goose/db-sample/migrations/002_next.sql
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
-- +goose Up
|
||||
CREATE TABLE fancier_post (
|
||||
id int NOT NULL,
|
||||
title text,
|
||||
body text,
|
||||
created_on timestamp without time zone,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE fancier_post;
|
15
vendor/bitbucket.org/liamstask/goose/db-sample/migrations/20130106222315_and_again.go
generated
vendored
Normal file
15
vendor/bitbucket.org/liamstask/goose/db-sample/migrations/20130106222315_and_again.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Up_20130106222315(txn *sql.Tx) {
|
||||
fmt.Println("Hello from migration 20130106222315 Up!")
|
||||
}
|
||||
|
||||
func Down_20130106222315(txn *sql.Tx) {
|
||||
fmt.Println("Hello from migration 20130106222315 Down!")
|
||||
}
|
70
vendor/bitbucket.org/liamstask/goose/lib/goose/dbconf_test.go
generated
vendored
Normal file
70
vendor/bitbucket.org/liamstask/goose/lib/goose/dbconf_test.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBasics(t *testing.T) {
|
||||
|
||||
dbconf, err := NewDBConf("../../db-sample", "test", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got := []string{dbconf.MigrationsDir, dbconf.Env, dbconf.Driver.Name, dbconf.Driver.OpenStr}
|
||||
want := []string{"../../db-sample/migrations", "test", "postgres", "user=liam dbname=tester sslmode=disable"}
|
||||
|
||||
for i, s := range got {
|
||||
if s != want[i] {
|
||||
t.Errorf("Unexpected DBConf value. got %v, want %v", s, want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportOverride(t *testing.T) {
|
||||
|
||||
dbconf, err := NewDBConf("../../db-sample", "customimport", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got := dbconf.Driver.Import
|
||||
want := "github.com/custom/driver"
|
||||
if got != want {
|
||||
t.Errorf("bad custom import. got %v want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDriverSetFromEnvironmentVariable(t *testing.T) {
|
||||
|
||||
databaseUrlEnvVariableKey := "DB_DRIVER"
|
||||
databaseUrlEnvVariableVal := "sqlite3"
|
||||
databaseOpenStringKey := "DATABASE_URL"
|
||||
databaseOpenStringVal := "db.db"
|
||||
|
||||
os.Setenv(databaseUrlEnvVariableKey, databaseUrlEnvVariableVal)
|
||||
os.Setenv(databaseOpenStringKey, databaseOpenStringVal)
|
||||
|
||||
dbconf, err := NewDBConf("../../db-sample", "environment_variable_config", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
got := reflect.TypeOf(dbconf.Driver.Dialect)
|
||||
want := reflect.TypeOf(&Sqlite3Dialect{})
|
||||
|
||||
if got != want {
|
||||
t.Errorf("Not able to read the driver type from environment variable."+
|
||||
"got %v want %v", got, want)
|
||||
}
|
||||
|
||||
gotOpenString := dbconf.Driver.OpenStr
|
||||
wantOpenString := databaseOpenStringVal
|
||||
|
||||
if gotOpenString != wantOpenString {
|
||||
t.Errorf("Not able to read the open string from the environment."+
|
||||
"got %v want %v", gotOpenString, wantOpenString)
|
||||
}
|
||||
}
|
71
vendor/bitbucket.org/liamstask/goose/lib/goose/migrate_test.go
generated
vendored
Normal file
71
vendor/bitbucket.org/liamstask/goose/lib/goose/migrate_test.go
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMigrationMapSortUp(t *testing.T) {
|
||||
|
||||
ms := migrationSorter{}
|
||||
|
||||
// insert in any order
|
||||
ms = append(ms, newMigration(20120000, "test"))
|
||||
ms = append(ms, newMigration(20128000, "test"))
|
||||
ms = append(ms, newMigration(20129000, "test"))
|
||||
ms = append(ms, newMigration(20127000, "test"))
|
||||
|
||||
ms.Sort(true) // sort Upwards
|
||||
|
||||
sorted := []int64{20120000, 20127000, 20128000, 20129000}
|
||||
|
||||
validateMigrationSort(t, ms, sorted)
|
||||
}
|
||||
|
||||
func TestMigrationMapSortDown(t *testing.T) {
|
||||
|
||||
ms := migrationSorter{}
|
||||
|
||||
// insert in any order
|
||||
ms = append(ms, newMigration(20120000, "test"))
|
||||
ms = append(ms, newMigration(20128000, "test"))
|
||||
ms = append(ms, newMigration(20129000, "test"))
|
||||
ms = append(ms, newMigration(20127000, "test"))
|
||||
|
||||
ms.Sort(false) // sort Downwards
|
||||
|
||||
sorted := []int64{20129000, 20128000, 20127000, 20120000}
|
||||
|
||||
validateMigrationSort(t, ms, sorted)
|
||||
}
|
||||
|
||||
func validateMigrationSort(t *testing.T, ms migrationSorter, sorted []int64) {
|
||||
|
||||
for i, m := range ms {
|
||||
if sorted[i] != m.Version {
|
||||
t.Error("incorrect sorted version")
|
||||
}
|
||||
|
||||
var next, prev int64
|
||||
|
||||
if i == 0 {
|
||||
prev = -1
|
||||
next = ms[i+1].Version
|
||||
} else if i == len(ms)-1 {
|
||||
prev = ms[i-1].Version
|
||||
next = -1
|
||||
} else {
|
||||
prev = ms[i-1].Version
|
||||
next = ms[i+1].Version
|
||||
}
|
||||
|
||||
if m.Next != next {
|
||||
t.Errorf("mismatched Next. v: %v, got %v, wanted %v\n", m, m.Next, next)
|
||||
}
|
||||
|
||||
if m.Previous != prev {
|
||||
t.Errorf("mismatched Previous v: %v, got %v, wanted %v\n", m, m.Previous, prev)
|
||||
}
|
||||
}
|
||||
|
||||
t.Log(ms)
|
||||
}
|
147
vendor/bitbucket.org/liamstask/goose/lib/goose/migration_sql_test.go
generated
vendored
Normal file
147
vendor/bitbucket.org/liamstask/goose/lib/goose/migration_sql_test.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
package goose
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSemicolons(t *testing.T) {
|
||||
|
||||
type testData struct {
|
||||
line string
|
||||
result bool
|
||||
}
|
||||
|
||||
tests := []testData{
|
||||
{
|
||||
line: "END;",
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
line: "END; -- comment",
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
line: "END ; -- comment",
|
||||
result: true,
|
||||
},
|
||||
{
|
||||
line: "END -- comment",
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
line: "END -- comment ;",
|
||||
result: false,
|
||||
},
|
||||
{
|
||||
line: "END \" ; \" -- comment",
|
||||
result: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
r := endsWithSemicolon(test.line)
|
||||
if r != test.result {
|
||||
t.Errorf("incorrect semicolon. got %v, want %v", r, test.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitStatements(t *testing.T) {
|
||||
|
||||
type testData struct {
|
||||
sql string
|
||||
direction bool
|
||||
count int
|
||||
}
|
||||
|
||||
tests := []testData{
|
||||
{
|
||||
sql: functxt,
|
||||
direction: true,
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
sql: functxt,
|
||||
direction: false,
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
sql: multitxt,
|
||||
direction: true,
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
sql: multitxt,
|
||||
direction: false,
|
||||
count: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
stmts := splitSQLStatements(strings.NewReader(test.sql), test.direction)
|
||||
if len(stmts) != test.count {
|
||||
t.Errorf("incorrect number of stmts. got %v, want %v", len(stmts), test.count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var functxt = `-- +goose Up
|
||||
CREATE TABLE IF NOT EXISTS histories (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
current_value varchar(2000) NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
-- +goose StatementBegin
|
||||
CREATE OR REPLACE FUNCTION histories_partition_creation( DATE, DATE )
|
||||
returns void AS $$
|
||||
DECLARE
|
||||
create_query text;
|
||||
BEGIN
|
||||
FOR create_query IN SELECT
|
||||
'CREATE TABLE IF NOT EXISTS histories_'
|
||||
|| TO_CHAR( d, 'YYYY_MM' )
|
||||
|| ' ( CHECK( created_at >= timestamp '''
|
||||
|| TO_CHAR( d, 'YYYY-MM-DD 00:00:00' )
|
||||
|| ''' AND created_at < timestamp '''
|
||||
|| TO_CHAR( d + INTERVAL '1 month', 'YYYY-MM-DD 00:00:00' )
|
||||
|| ''' ) ) inherits ( histories );'
|
||||
FROM generate_series( $1, $2, '1 month' ) AS d
|
||||
LOOP
|
||||
EXECUTE create_query;
|
||||
END LOOP; -- LOOP END
|
||||
END; -- FUNCTION END
|
||||
$$
|
||||
language plpgsql;
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
drop function histories_partition_creation(DATE, DATE);
|
||||
drop TABLE histories;
|
||||
`
|
||||
|
||||
// test multiple up/down transitions in a single script
|
||||
var multitxt = `-- +goose Up
|
||||
CREATE TABLE post (
|
||||
id int NOT NULL,
|
||||
title text,
|
||||
body text,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE post;
|
||||
|
||||
-- +goose Up
|
||||
CREATE TABLE fancier_post (
|
||||
id int NOT NULL,
|
||||
title text,
|
||||
body text,
|
||||
created_on timestamp without time zone,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE fancier_post;
|
||||
`
|
2
vendor/github.com/beorn7/perks/.gitignore
generated
vendored
Normal file
2
vendor/github.com/beorn7/perks/.gitignore
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.test
|
||||
*.prof
|
31
vendor/github.com/beorn7/perks/README.md
generated
vendored
Normal file
31
vendor/github.com/beorn7/perks/README.md
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
# Perks for Go (golang.org)
|
||||
|
||||
Perks contains the Go package quantile that computes approximate quantiles over
|
||||
an unbounded data stream within low memory and CPU bounds.
|
||||
|
||||
For more information and examples, see:
|
||||
http://godoc.org/github.com/bmizerany/perks
|
||||
|
||||
A very special thank you and shout out to Graham Cormode (Rutgers University),
|
||||
Flip Korn (AT&T Labs–Research), S. Muthukrishnan (Rutgers University), and
|
||||
Divesh Srivastava (AT&T Labs–Research) for their research and publication of
|
||||
[Effective Computation of Biased Quantiles over Data Streams](http://www.cs.rutgers.edu/~muthu/bquant.pdf)
|
||||
|
||||
Thank you, also:
|
||||
* Armon Dadgar (@armon)
|
||||
* Andrew Gerrand (@nf)
|
||||
* Brad Fitzpatrick (@bradfitz)
|
||||
* Keith Rarick (@kr)
|
||||
|
||||
FAQ:
|
||||
|
||||
Q: Why not move the quantile package into the project root?
|
||||
A: I want to add more packages to perks later.
|
||||
|
||||
Copyright (C) 2013 Blake Mizerany
|
||||
|
||||
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.
|
26
vendor/github.com/beorn7/perks/histogram/bench_test.go
generated
vendored
Normal file
26
vendor/github.com/beorn7/perks/histogram/bench_test.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
package histogram
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkInsert10Bins(b *testing.B) {
|
||||
b.StopTimer()
|
||||
h := New(10)
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
f := rand.ExpFloat64()
|
||||
h.Insert(f)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInsert100Bins(b *testing.B) {
|
||||
b.StopTimer()
|
||||
h := New(100)
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
f := rand.ExpFloat64()
|
||||
h.Insert(f)
|
||||
}
|
||||
}
|
108
vendor/github.com/beorn7/perks/histogram/histogram.go
generated
vendored
Normal file
108
vendor/github.com/beorn7/perks/histogram/histogram.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
// Package histogram provides a Go implementation of BigML's histogram package
|
||||
// for Clojure/Java. It is currently experimental.
|
||||
package histogram
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type Bin struct {
|
||||
Count int
|
||||
Sum float64
|
||||
}
|
||||
|
||||
func (b *Bin) Update(x *Bin) {
|
||||
b.Count += x.Count
|
||||
b.Sum += x.Sum
|
||||
}
|
||||
|
||||
func (b *Bin) Mean() float64 {
|
||||
return b.Sum / float64(b.Count)
|
||||
}
|
||||
|
||||
type Bins []*Bin
|
||||
|
||||
func (bs Bins) Len() int { return len(bs) }
|
||||
func (bs Bins) Less(i, j int) bool { return bs[i].Mean() < bs[j].Mean() }
|
||||
func (bs Bins) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] }
|
||||
|
||||
func (bs *Bins) Push(x interface{}) {
|
||||
*bs = append(*bs, x.(*Bin))
|
||||
}
|
||||
|
||||
func (bs *Bins) Pop() interface{} {
|
||||
return bs.remove(len(*bs) - 1)
|
||||
}
|
||||
|
||||
func (bs *Bins) remove(n int) *Bin {
|
||||
if n < 0 || len(*bs) < n {
|
||||
return nil
|
||||
}
|
||||
x := (*bs)[n]
|
||||
*bs = append((*bs)[:n], (*bs)[n+1:]...)
|
||||
return x
|
||||
}
|
||||
|
||||
type Histogram struct {
|
||||
res *reservoir
|
||||
}
|
||||
|
||||
func New(maxBins int) *Histogram {
|
||||
return &Histogram{res: newReservoir(maxBins)}
|
||||
}
|
||||
|
||||
func (h *Histogram) Insert(f float64) {
|
||||
h.res.insert(&Bin{1, f})
|
||||
h.res.compress()
|
||||
}
|
||||
|
||||
func (h *Histogram) Bins() Bins {
|
||||
return h.res.bins
|
||||
}
|
||||
|
||||
type reservoir struct {
|
||||
n int
|
||||
maxBins int
|
||||
bins Bins
|
||||
}
|
||||
|
||||
func newReservoir(maxBins int) *reservoir {
|
||||
return &reservoir{maxBins: maxBins}
|
||||
}
|
||||
|
||||
func (r *reservoir) insert(bin *Bin) {
|
||||
r.n += bin.Count
|
||||
i := sort.Search(len(r.bins), func(i int) bool {
|
||||
return r.bins[i].Mean() >= bin.Mean()
|
||||
})
|
||||
if i < 0 || i == r.bins.Len() {
|
||||
// TODO(blake): Maybe use an .insert(i, bin) instead of
|
||||
// performing the extra work of a heap.Push.
|
||||
heap.Push(&r.bins, bin)
|
||||
return
|
||||
}
|
||||
r.bins[i].Update(bin)
|
||||
}
|
||||
|
||||
func (r *reservoir) compress() {
|
||||
for r.bins.Len() > r.maxBins {
|
||||
minGapIndex := -1
|
||||
minGap := math.MaxFloat64
|
||||
for i := 0; i < r.bins.Len()-1; i++ {
|
||||
gap := gapWeight(r.bins[i], r.bins[i+1])
|
||||
if minGap > gap {
|
||||
minGap = gap
|
||||
minGapIndex = i
|
||||
}
|
||||
}
|
||||
prev := r.bins[minGapIndex]
|
||||
next := r.bins.remove(minGapIndex + 1)
|
||||
prev.Update(next)
|
||||
}
|
||||
}
|
||||
|
||||
func gapWeight(prev, next *Bin) float64 {
|
||||
return next.Mean() - prev.Mean()
|
||||
}
|
38
vendor/github.com/beorn7/perks/histogram/histogram_test.go
generated
vendored
Normal file
38
vendor/github.com/beorn7/perks/histogram/histogram_test.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
package histogram
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHistogram(t *testing.T) {
|
||||
const numPoints = 1e6
|
||||
const maxBins = 3
|
||||
|
||||
h := New(maxBins)
|
||||
for i := 0; i < numPoints; i++ {
|
||||
f := rand.ExpFloat64()
|
||||
h.Insert(f)
|
||||
}
|
||||
|
||||
bins := h.Bins()
|
||||
if g := len(bins); g > maxBins {
|
||||
t.Fatalf("got %d bins, wanted <= %d", g, maxBins)
|
||||
}
|
||||
|
||||
for _, b := range bins {
|
||||
t.Logf("%+v", b)
|
||||
}
|
||||
|
||||
if g := count(h.Bins()); g != numPoints {
|
||||
t.Fatalf("binned %d points, wanted %d", g, numPoints)
|
||||
}
|
||||
}
|
||||
|
||||
func count(bins Bins) int {
|
||||
binCounts := 0
|
||||
for _, b := range bins {
|
||||
binCounts += b.Count
|
||||
}
|
||||
return binCounts
|
||||
}
|
63
vendor/github.com/beorn7/perks/quantile/bench_test.go
generated
vendored
Normal file
63
vendor/github.com/beorn7/perks/quantile/bench_test.go
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
package quantile
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkInsertTargeted(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
s := NewTargeted(Targets)
|
||||
b.ResetTimer()
|
||||
for i := float64(0); i < float64(b.N); i++ {
|
||||
s.Insert(i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInsertTargetedSmallEpsilon(b *testing.B) {
|
||||
s := NewTargeted(TargetsSmallEpsilon)
|
||||
b.ResetTimer()
|
||||
for i := float64(0); i < float64(b.N); i++ {
|
||||
s.Insert(i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInsertBiased(b *testing.B) {
|
||||
s := NewLowBiased(0.01)
|
||||
b.ResetTimer()
|
||||
for i := float64(0); i < float64(b.N); i++ {
|
||||
s.Insert(i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInsertBiasedSmallEpsilon(b *testing.B) {
|
||||
s := NewLowBiased(0.0001)
|
||||
b.ResetTimer()
|
||||
for i := float64(0); i < float64(b.N); i++ {
|
||||
s.Insert(i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkQuery(b *testing.B) {
|
||||
s := NewTargeted(Targets)
|
||||
for i := float64(0); i < 1e6; i++ {
|
||||
s.Insert(i)
|
||||
}
|
||||
b.ResetTimer()
|
||||
n := float64(b.N)
|
||||
for i := float64(0); i < n; i++ {
|
||||
s.Query(i / n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkQuerySmallEpsilon(b *testing.B) {
|
||||
s := NewTargeted(TargetsSmallEpsilon)
|
||||
for i := float64(0); i < 1e6; i++ {
|
||||
s.Insert(i)
|
||||
}
|
||||
b.ResetTimer()
|
||||
n := float64(b.N)
|
||||
for i := float64(0); i < n; i++ {
|
||||
s.Query(i / n)
|
||||
}
|
||||
}
|
121
vendor/github.com/beorn7/perks/quantile/example_test.go
generated
vendored
Normal file
121
vendor/github.com/beorn7/perks/quantile/example_test.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
// +build go1.1
|
||||
|
||||
package quantile_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/beorn7/perks/quantile"
|
||||
)
|
||||
|
||||
func Example_simple() {
|
||||
ch := make(chan float64)
|
||||
go sendFloats(ch)
|
||||
|
||||
// Compute the 50th, 90th, and 99th percentile.
|
||||
q := quantile.NewTargeted(map[float64]float64{
|
||||
0.50: 0.005,
|
||||
0.90: 0.001,
|
||||
0.99: 0.0001,
|
||||
})
|
||||
for v := range ch {
|
||||
q.Insert(v)
|
||||
}
|
||||
|
||||
fmt.Println("perc50:", q.Query(0.50))
|
||||
fmt.Println("perc90:", q.Query(0.90))
|
||||
fmt.Println("perc99:", q.Query(0.99))
|
||||
fmt.Println("count:", q.Count())
|
||||
// Output:
|
||||
// perc50: 5
|
||||
// perc90: 16
|
||||
// perc99: 223
|
||||
// count: 2388
|
||||
}
|
||||
|
||||
func Example_mergeMultipleStreams() {
|
||||
// Scenario:
|
||||
// We have multiple database shards. On each shard, there is a process
|
||||
// collecting query response times from the database logs and inserting
|
||||
// them into a Stream (created via NewTargeted(0.90)), much like the
|
||||
// Simple example. These processes expose a network interface for us to
|
||||
// ask them to serialize and send us the results of their
|
||||
// Stream.Samples so we may Merge and Query them.
|
||||
//
|
||||
// NOTES:
|
||||
// * These sample sets are small, allowing us to get them
|
||||
// across the network much faster than sending the entire list of data
|
||||
// points.
|
||||
//
|
||||
// * For this to work correctly, we must supply the same quantiles
|
||||
// a priori the process collecting the samples supplied to NewTargeted,
|
||||
// even if we do not plan to query them all here.
|
||||
ch := make(chan quantile.Samples)
|
||||
getDBQuerySamples(ch)
|
||||
q := quantile.NewTargeted(map[float64]float64{0.90: 0.001})
|
||||
for samples := range ch {
|
||||
q.Merge(samples)
|
||||
}
|
||||
fmt.Println("perc90:", q.Query(0.90))
|
||||
}
|
||||
|
||||
func Example_window() {
|
||||
// Scenario: We want the 90th, 95th, and 99th percentiles for each
|
||||
// minute.
|
||||
|
||||
ch := make(chan float64)
|
||||
go sendStreamValues(ch)
|
||||
|
||||
tick := time.NewTicker(1 * time.Minute)
|
||||
q := quantile.NewTargeted(map[float64]float64{
|
||||
0.90: 0.001,
|
||||
0.95: 0.0005,
|
||||
0.99: 0.0001,
|
||||
})
|
||||
for {
|
||||
select {
|
||||
case t := <-tick.C:
|
||||
flushToDB(t, q.Samples())
|
||||
q.Reset()
|
||||
case v := <-ch:
|
||||
q.Insert(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendStreamValues(ch chan float64) {
|
||||
// Use your imagination
|
||||
}
|
||||
|
||||
func flushToDB(t time.Time, samples quantile.Samples) {
|
||||
// Use your imagination
|
||||
}
|
||||
|
||||
// This is a stub for the above example. In reality this would hit the remote
|
||||
// servers via http or something like it.
|
||||
func getDBQuerySamples(ch chan quantile.Samples) {}
|
||||
|
||||
func sendFloats(ch chan<- float64) {
|
||||
f, err := os.Open("exampledata.txt")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
sc := bufio.NewScanner(f)
|
||||
for sc.Scan() {
|
||||
b := sc.Bytes()
|
||||
v, err := strconv.ParseFloat(string(b), 64)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
ch <- v
|
||||
}
|
||||
if sc.Err() != nil {
|
||||
log.Fatal(sc.Err())
|
||||
}
|
||||
close(ch)
|
||||
}
|
188
vendor/github.com/beorn7/perks/quantile/stream_test.go
generated
vendored
Normal file
188
vendor/github.com/beorn7/perks/quantile/stream_test.go
generated
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
package quantile
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
Targets = map[float64]float64{
|
||||
0.01: 0.001,
|
||||
0.10: 0.01,
|
||||
0.50: 0.05,
|
||||
0.90: 0.01,
|
||||
0.99: 0.001,
|
||||
}
|
||||
TargetsSmallEpsilon = map[float64]float64{
|
||||
0.01: 0.0001,
|
||||
0.10: 0.001,
|
||||
0.50: 0.005,
|
||||
0.90: 0.001,
|
||||
0.99: 0.0001,
|
||||
}
|
||||
LowQuantiles = []float64{0.01, 0.1, 0.5}
|
||||
HighQuantiles = []float64{0.99, 0.9, 0.5}
|
||||
)
|
||||
|
||||
const RelativeEpsilon = 0.01
|
||||
|
||||
func verifyPercsWithAbsoluteEpsilon(t *testing.T, a []float64, s *Stream) {
|
||||
sort.Float64s(a)
|
||||
for quantile, epsilon := range Targets {
|
||||
n := float64(len(a))
|
||||
k := int(quantile * n)
|
||||
lower := int((quantile - epsilon) * n)
|
||||
if lower < 1 {
|
||||
lower = 1
|
||||
}
|
||||
upper := int(math.Ceil((quantile + epsilon) * n))
|
||||
if upper > len(a) {
|
||||
upper = len(a)
|
||||
}
|
||||
w, min, max := a[k-1], a[lower-1], a[upper-1]
|
||||
if g := s.Query(quantile); g < min || g > max {
|
||||
t.Errorf("q=%f: want %v [%f,%f], got %v", quantile, w, min, max, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyLowPercsWithRelativeEpsilon(t *testing.T, a []float64, s *Stream) {
|
||||
sort.Float64s(a)
|
||||
for _, qu := range LowQuantiles {
|
||||
n := float64(len(a))
|
||||
k := int(qu * n)
|
||||
|
||||
lowerRank := int((1 - RelativeEpsilon) * qu * n)
|
||||
upperRank := int(math.Ceil((1 + RelativeEpsilon) * qu * n))
|
||||
w, min, max := a[k-1], a[lowerRank-1], a[upperRank-1]
|
||||
if g := s.Query(qu); g < min || g > max {
|
||||
t.Errorf("q=%f: want %v [%f,%f], got %v", qu, w, min, max, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func verifyHighPercsWithRelativeEpsilon(t *testing.T, a []float64, s *Stream) {
|
||||
sort.Float64s(a)
|
||||
for _, qu := range HighQuantiles {
|
||||
n := float64(len(a))
|
||||
k := int(qu * n)
|
||||
|
||||
lowerRank := int((1 - (1+RelativeEpsilon)*(1-qu)) * n)
|
||||
upperRank := int(math.Ceil((1 - (1-RelativeEpsilon)*(1-qu)) * n))
|
||||
w, min, max := a[k-1], a[lowerRank-1], a[upperRank-1]
|
||||
if g := s.Query(qu); g < min || g > max {
|
||||
t.Errorf("q=%f: want %v [%f,%f], got %v", qu, w, min, max, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func populateStream(s *Stream) []float64 {
|
||||
a := make([]float64, 0, 1e5+100)
|
||||
for i := 0; i < cap(a); i++ {
|
||||
v := rand.NormFloat64()
|
||||
// Add 5% asymmetric outliers.
|
||||
if i%20 == 0 {
|
||||
v = v*v + 1
|
||||
}
|
||||
s.Insert(v)
|
||||
a = append(a, v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func TestTargetedQuery(t *testing.T) {
|
||||
rand.Seed(42)
|
||||
s := NewTargeted(Targets)
|
||||
a := populateStream(s)
|
||||
verifyPercsWithAbsoluteEpsilon(t, a, s)
|
||||
}
|
||||
|
||||
func TestLowBiasedQuery(t *testing.T) {
|
||||
rand.Seed(42)
|
||||
s := NewLowBiased(RelativeEpsilon)
|
||||
a := populateStream(s)
|
||||
verifyLowPercsWithRelativeEpsilon(t, a, s)
|
||||
}
|
||||
|
||||
func TestHighBiasedQuery(t *testing.T) {
|
||||
rand.Seed(42)
|
||||
s := NewHighBiased(RelativeEpsilon)
|
||||
a := populateStream(s)
|
||||
verifyHighPercsWithRelativeEpsilon(t, a, s)
|
||||
}
|
||||
|
||||
// BrokenTestTargetedMerge is broken, see Merge doc comment.
|
||||
func BrokenTestTargetedMerge(t *testing.T) {
|
||||
rand.Seed(42)
|
||||
s1 := NewTargeted(Targets)
|
||||
s2 := NewTargeted(Targets)
|
||||
a := populateStream(s1)
|
||||
a = append(a, populateStream(s2)...)
|
||||
s1.Merge(s2.Samples())
|
||||
verifyPercsWithAbsoluteEpsilon(t, a, s1)
|
||||
}
|
||||
|
||||
// BrokenTestLowBiasedMerge is broken, see Merge doc comment.
|
||||
func BrokenTestLowBiasedMerge(t *testing.T) {
|
||||
rand.Seed(42)
|
||||
s1 := NewLowBiased(RelativeEpsilon)
|
||||
s2 := NewLowBiased(RelativeEpsilon)
|
||||
a := populateStream(s1)
|
||||
a = append(a, populateStream(s2)...)
|
||||
s1.Merge(s2.Samples())
|
||||
verifyLowPercsWithRelativeEpsilon(t, a, s2)
|
||||
}
|
||||
|
||||
// BrokenTestHighBiasedMerge is broken, see Merge doc comment.
|
||||
func BrokenTestHighBiasedMerge(t *testing.T) {
|
||||
rand.Seed(42)
|
||||
s1 := NewHighBiased(RelativeEpsilon)
|
||||
s2 := NewHighBiased(RelativeEpsilon)
|
||||
a := populateStream(s1)
|
||||
a = append(a, populateStream(s2)...)
|
||||
s1.Merge(s2.Samples())
|
||||
verifyHighPercsWithRelativeEpsilon(t, a, s2)
|
||||
}
|
||||
|
||||
func TestUncompressed(t *testing.T) {
|
||||
q := NewTargeted(Targets)
|
||||
for i := 100; i > 0; i-- {
|
||||
q.Insert(float64(i))
|
||||
}
|
||||
if g := q.Count(); g != 100 {
|
||||
t.Errorf("want count 100, got %d", g)
|
||||
}
|
||||
// Before compression, Query should have 100% accuracy.
|
||||
for quantile := range Targets {
|
||||
w := quantile * 100
|
||||
if g := q.Query(quantile); g != w {
|
||||
t.Errorf("want %f, got %f", w, g)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUncompressedSamples(t *testing.T) {
|
||||
q := NewTargeted(map[float64]float64{0.99: 0.001})
|
||||
for i := 1; i <= 100; i++ {
|
||||
q.Insert(float64(i))
|
||||
}
|
||||
if g := q.Samples().Len(); g != 100 {
|
||||
t.Errorf("want count 100, got %d", g)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUncompressedOne(t *testing.T) {
|
||||
q := NewTargeted(map[float64]float64{0.99: 0.01})
|
||||
q.Insert(3.14)
|
||||
if g := q.Query(0.90); g != 3.14 {
|
||||
t.Error("want PI, got", g)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaults(t *testing.T) {
|
||||
if g := NewTargeted(map[float64]float64{0.99: 0.001}).Query(0.99); g != 0 {
|
||||
t.Errorf("want 0, got %f", g)
|
||||
}
|
||||
}
|
90
vendor/github.com/beorn7/perks/topk/topk.go
generated
vendored
Normal file
90
vendor/github.com/beorn7/perks/topk/topk.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
package topk
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
// http://www.cs.ucsb.edu/research/tech_reports/reports/2005-23.pdf
|
||||
|
||||
type Element struct {
|
||||
Value string
|
||||
Count int
|
||||
}
|
||||
|
||||
type Samples []*Element
|
||||
|
||||
func (sm Samples) Len() int {
|
||||
return len(sm)
|
||||
}
|
||||
|
||||
func (sm Samples) Less(i, j int) bool {
|
||||
return sm[i].Count < sm[j].Count
|
||||
}
|
||||
|
||||
func (sm Samples) Swap(i, j int) {
|
||||
sm[i], sm[j] = sm[j], sm[i]
|
||||
}
|
||||
|
||||
type Stream struct {
|
||||
k int
|
||||
mon map[string]*Element
|
||||
|
||||
// the minimum Element
|
||||
min *Element
|
||||
}
|
||||
|
||||
func New(k int) *Stream {
|
||||
s := new(Stream)
|
||||
s.k = k
|
||||
s.mon = make(map[string]*Element)
|
||||
s.min = &Element{}
|
||||
|
||||
// Track k+1 so that less frequenet items contended for that spot,
|
||||
// resulting in k being more accurate.
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Stream) Insert(x string) {
|
||||
s.insert(&Element{x, 1})
|
||||
}
|
||||
|
||||
func (s *Stream) Merge(sm Samples) {
|
||||
for _, e := range sm {
|
||||
s.insert(e)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stream) insert(in *Element) {
|
||||
e := s.mon[in.Value]
|
||||
if e != nil {
|
||||
e.Count++
|
||||
} else {
|
||||
if len(s.mon) < s.k+1 {
|
||||
e = &Element{in.Value, in.Count}
|
||||
s.mon[in.Value] = e
|
||||
} else {
|
||||
e = s.min
|
||||
delete(s.mon, e.Value)
|
||||
e.Value = in.Value
|
||||
e.Count += in.Count
|
||||
s.min = e
|
||||
}
|
||||
}
|
||||
if e.Count < s.min.Count {
|
||||
s.min = e
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Stream) Query() Samples {
|
||||
var sm Samples
|
||||
for _, e := range s.mon {
|
||||
sm = append(sm, e)
|
||||
}
|
||||
sort.Sort(sort.Reverse(sm))
|
||||
|
||||
if len(sm) < s.k {
|
||||
return sm
|
||||
}
|
||||
|
||||
return sm[:s.k]
|
||||
}
|
57
vendor/github.com/beorn7/perks/topk/topk_test.go
generated
vendored
Normal file
57
vendor/github.com/beorn7/perks/topk/topk_test.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
package topk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTopK(t *testing.T) {
|
||||
stream := New(10)
|
||||
ss := []*Stream{New(10), New(10), New(10)}
|
||||
m := make(map[string]int)
|
||||
for _, s := range ss {
|
||||
for i := 0; i < 1e6; i++ {
|
||||
v := fmt.Sprintf("%x", int8(rand.ExpFloat64()))
|
||||
s.Insert(v)
|
||||
m[v]++
|
||||
}
|
||||
stream.Merge(s.Query())
|
||||
}
|
||||
|
||||
var sm Samples
|
||||
for x, s := range m {
|
||||
sm = append(sm, &Element{x, s})
|
||||
}
|
||||
sort.Sort(sort.Reverse(sm))
|
||||
|
||||
g := stream.Query()
|
||||
if len(g) != 10 {
|
||||
t.Fatalf("got %d, want 10", len(g))
|
||||
}
|
||||
for i, e := range g {
|
||||
if sm[i].Value != e.Value {
|
||||
t.Errorf("at %d: want %q, got %q", i, sm[i].Value, e.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuery(t *testing.T) {
|
||||
queryTests := []struct {
|
||||
value string
|
||||
expected int
|
||||
}{
|
||||
{"a", 1},
|
||||
{"b", 2},
|
||||
{"c", 2},
|
||||
}
|
||||
|
||||
stream := New(2)
|
||||
for _, tt := range queryTests {
|
||||
stream.Insert(tt.value)
|
||||
if n := len(stream.Query()); n != tt.expected {
|
||||
t.Errorf("want %d, got %d", tt.expected, n)
|
||||
}
|
||||
}
|
||||
}
|
33
vendor/github.com/codegangsta/negroni/logger_test.go
generated
vendored
Normal file
33
vendor/github.com/codegangsta/negroni/logger_test.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package negroni
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Logger(t *testing.T) {
|
||||
buff := bytes.NewBufferString("")
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
l := NewLogger()
|
||||
l.Logger = log.New(buff, "[negroni] ", 0)
|
||||
|
||||
n := New()
|
||||
// replace log for testing
|
||||
n.Use(l)
|
||||
n.UseHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
}))
|
||||
|
||||
req, err := http.NewRequest("GET", "http://localhost:3000/foobar", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
n.ServeHTTP(recorder, req)
|
||||
expect(t, recorder.Code, http.StatusNotFound)
|
||||
refute(t, len(buff.String()), 0)
|
||||
}
|
75
vendor/github.com/codegangsta/negroni/negroni_test.go
generated
vendored
Normal file
75
vendor/github.com/codegangsta/negroni/negroni_test.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
package negroni
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
/* Test Helpers */
|
||||
func expect(t *testing.T, a interface{}, b interface{}) {
|
||||
if a != b {
|
||||
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||
}
|
||||
}
|
||||
|
||||
func refute(t *testing.T, a interface{}, b interface{}) {
|
||||
if a == b {
|
||||
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||
}
|
||||
}
|
||||
|
||||
func TestNegroniRun(t *testing.T) {
|
||||
// just test that Run doesn't bomb
|
||||
go New().Run(":3000")
|
||||
}
|
||||
|
||||
func TestNegroniServeHTTP(t *testing.T) {
|
||||
result := ""
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
n := New()
|
||||
n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
result += "foo"
|
||||
next(rw, r)
|
||||
result += "ban"
|
||||
}))
|
||||
n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
result += "bar"
|
||||
next(rw, r)
|
||||
result += "baz"
|
||||
}))
|
||||
n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
result += "bat"
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
}))
|
||||
|
||||
n.ServeHTTP(response, (*http.Request)(nil))
|
||||
|
||||
expect(t, result, "foobarbatbazban")
|
||||
expect(t, response.Code, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Ensures that a Negroni middleware chain
|
||||
// can correctly return all of its handlers.
|
||||
func TestHandlers(t *testing.T) {
|
||||
response := httptest.NewRecorder()
|
||||
n := New()
|
||||
handlers := n.Handlers()
|
||||
expect(t, 0, len(handlers))
|
||||
|
||||
n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
// Expects the length of handlers to be exactly 1
|
||||
// after adding exactly one handler to the middleware chain
|
||||
handlers = n.Handlers()
|
||||
expect(t, 1, len(handlers))
|
||||
|
||||
// Ensures that the first handler that is in sequence behaves
|
||||
// exactly the same as the one that was registered earlier
|
||||
handlers[0].ServeHTTP(response, (*http.Request)(nil), nil)
|
||||
expect(t, response.Code, http.StatusOK)
|
||||
}
|
28
vendor/github.com/codegangsta/negroni/recovery_test.go
generated
vendored
Normal file
28
vendor/github.com/codegangsta/negroni/recovery_test.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package negroni
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRecovery(t *testing.T) {
|
||||
buff := bytes.NewBufferString("")
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
rec := NewRecovery()
|
||||
rec.Logger = log.New(buff, "[negroni] ", 0)
|
||||
|
||||
n := New()
|
||||
// replace log for testing
|
||||
n.Use(rec)
|
||||
n.UseHandler(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
|
||||
panic("here is a panic!")
|
||||
}))
|
||||
n.ServeHTTP(recorder, (*http.Request)(nil))
|
||||
expect(t, recorder.Code, http.StatusInternalServerError)
|
||||
refute(t, recorder.Body.Len(), 0)
|
||||
refute(t, len(buff.String()), 0)
|
||||
}
|
150
vendor/github.com/codegangsta/negroni/response_writer_test.go
generated
vendored
Normal file
150
vendor/github.com/codegangsta/negroni/response_writer_test.go
generated
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
package negroni
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type closeNotifyingRecorder struct {
|
||||
*httptest.ResponseRecorder
|
||||
closed chan bool
|
||||
}
|
||||
|
||||
func newCloseNotifyingRecorder() *closeNotifyingRecorder {
|
||||
return &closeNotifyingRecorder{
|
||||
httptest.NewRecorder(),
|
||||
make(chan bool, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *closeNotifyingRecorder) close() {
|
||||
c.closed <- true
|
||||
}
|
||||
|
||||
func (c *closeNotifyingRecorder) CloseNotify() <-chan bool {
|
||||
return c.closed
|
||||
}
|
||||
|
||||
type hijackableResponse struct {
|
||||
Hijacked bool
|
||||
}
|
||||
|
||||
func newHijackableResponse() *hijackableResponse {
|
||||
return &hijackableResponse{}
|
||||
}
|
||||
|
||||
func (h *hijackableResponse) Header() http.Header { return nil }
|
||||
func (h *hijackableResponse) Write(buf []byte) (int, error) { return 0, nil }
|
||||
func (h *hijackableResponse) WriteHeader(code int) {}
|
||||
func (h *hijackableResponse) Flush() {}
|
||||
func (h *hijackableResponse) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
h.Hijacked = true
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func TestResponseWriterWritingString(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
rw := NewResponseWriter(rec)
|
||||
|
||||
rw.Write([]byte("Hello world"))
|
||||
|
||||
expect(t, rec.Code, rw.Status())
|
||||
expect(t, rec.Body.String(), "Hello world")
|
||||
expect(t, rw.Status(), http.StatusOK)
|
||||
expect(t, rw.Size(), 11)
|
||||
expect(t, rw.Written(), true)
|
||||
}
|
||||
|
||||
func TestResponseWriterWritingStrings(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
rw := NewResponseWriter(rec)
|
||||
|
||||
rw.Write([]byte("Hello world"))
|
||||
rw.Write([]byte("foo bar bat baz"))
|
||||
|
||||
expect(t, rec.Code, rw.Status())
|
||||
expect(t, rec.Body.String(), "Hello worldfoo bar bat baz")
|
||||
expect(t, rw.Status(), http.StatusOK)
|
||||
expect(t, rw.Size(), 26)
|
||||
}
|
||||
|
||||
func TestResponseWriterWritingHeader(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
rw := NewResponseWriter(rec)
|
||||
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
|
||||
expect(t, rec.Code, rw.Status())
|
||||
expect(t, rec.Body.String(), "")
|
||||
expect(t, rw.Status(), http.StatusNotFound)
|
||||
expect(t, rw.Size(), 0)
|
||||
}
|
||||
|
||||
func TestResponseWriterBefore(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
rw := NewResponseWriter(rec)
|
||||
result := ""
|
||||
|
||||
rw.Before(func(ResponseWriter) {
|
||||
result += "foo"
|
||||
})
|
||||
rw.Before(func(ResponseWriter) {
|
||||
result += "bar"
|
||||
})
|
||||
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
|
||||
expect(t, rec.Code, rw.Status())
|
||||
expect(t, rec.Body.String(), "")
|
||||
expect(t, rw.Status(), http.StatusNotFound)
|
||||
expect(t, rw.Size(), 0)
|
||||
expect(t, result, "barfoo")
|
||||
}
|
||||
|
||||
func TestResponseWriterHijack(t *testing.T) {
|
||||
hijackable := newHijackableResponse()
|
||||
rw := NewResponseWriter(hijackable)
|
||||
hijacker, ok := rw.(http.Hijacker)
|
||||
expect(t, ok, true)
|
||||
_, _, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
expect(t, hijackable.Hijacked, true)
|
||||
}
|
||||
|
||||
func TestResponseWriteHijackNotOK(t *testing.T) {
|
||||
hijackable := new(http.ResponseWriter)
|
||||
rw := NewResponseWriter(*hijackable)
|
||||
hijacker, ok := rw.(http.Hijacker)
|
||||
expect(t, ok, true)
|
||||
_, _, err := hijacker.Hijack()
|
||||
|
||||
refute(t, err, nil)
|
||||
}
|
||||
|
||||
func TestResponseWriterCloseNotify(t *testing.T) {
|
||||
rec := newCloseNotifyingRecorder()
|
||||
rw := NewResponseWriter(rec)
|
||||
closed := false
|
||||
notifier := rw.(http.CloseNotifier).CloseNotify()
|
||||
rec.close()
|
||||
select {
|
||||
case <-notifier:
|
||||
closed = true
|
||||
case <-time.After(time.Second):
|
||||
}
|
||||
expect(t, closed, true)
|
||||
}
|
||||
|
||||
func TestResponseWriterFlusher(t *testing.T) {
|
||||
rec := httptest.NewRecorder()
|
||||
rw := NewResponseWriter(rec)
|
||||
|
||||
_, ok := rw.(http.Flusher)
|
||||
expect(t, ok, true)
|
||||
}
|
113
vendor/github.com/codegangsta/negroni/static_test.go
generated
vendored
Normal file
113
vendor/github.com/codegangsta/negroni/static_test.go
generated
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
package negroni
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStatic(t *testing.T) {
|
||||
response := httptest.NewRecorder()
|
||||
response.Body = new(bytes.Buffer)
|
||||
|
||||
n := New()
|
||||
n.Use(NewStatic(http.Dir(".")))
|
||||
|
||||
req, err := http.NewRequest("GET", "http://localhost:3000/negroni.go", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
n.ServeHTTP(response, req)
|
||||
expect(t, response.Code, http.StatusOK)
|
||||
expect(t, response.Header().Get("Expires"), "")
|
||||
if response.Body.Len() == 0 {
|
||||
t.Errorf("Got empty body for GET request")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStaticHead(t *testing.T) {
|
||||
response := httptest.NewRecorder()
|
||||
response.Body = new(bytes.Buffer)
|
||||
|
||||
n := New()
|
||||
n.Use(NewStatic(http.Dir(".")))
|
||||
n.UseHandler(http.NotFoundHandler())
|
||||
|
||||
req, err := http.NewRequest("HEAD", "http://localhost:3000/negroni.go", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
n.ServeHTTP(response, req)
|
||||
expect(t, response.Code, http.StatusOK)
|
||||
if response.Body.Len() != 0 {
|
||||
t.Errorf("Got non-empty body for HEAD request")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStaticAsPost(t *testing.T) {
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
n := New()
|
||||
n.Use(NewStatic(http.Dir(".")))
|
||||
n.UseHandler(http.NotFoundHandler())
|
||||
|
||||
req, err := http.NewRequest("POST", "http://localhost:3000/negroni.go", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
n.ServeHTTP(response, req)
|
||||
expect(t, response.Code, http.StatusNotFound)
|
||||
}
|
||||
|
||||
func TestStaticBadDir(t *testing.T) {
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
n := Classic()
|
||||
n.UseHandler(http.NotFoundHandler())
|
||||
|
||||
req, err := http.NewRequest("GET", "http://localhost:3000/negroni.go", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
n.ServeHTTP(response, req)
|
||||
refute(t, response.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
func TestStaticOptionsServeIndex(t *testing.T) {
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
n := New()
|
||||
s := NewStatic(http.Dir("."))
|
||||
s.IndexFile = "negroni.go"
|
||||
n.Use(s)
|
||||
|
||||
req, err := http.NewRequest("GET", "http://localhost:3000/", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
n.ServeHTTP(response, req)
|
||||
expect(t, response.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
func TestStaticOptionsPrefix(t *testing.T) {
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
n := New()
|
||||
s := NewStatic(http.Dir("."))
|
||||
s.Prefix = "/public"
|
||||
n.Use(s)
|
||||
|
||||
// Check file content behaviour
|
||||
req, err := http.NewRequest("GET", "http://localhost:3000/public/negroni.go", nil)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
n.ServeHTTP(response, req)
|
||||
expect(t, response.Code, http.StatusOK)
|
||||
}
|
8
vendor/github.com/coreos/go-systemd/.travis.yml
generated
vendored
Normal file
8
vendor/github.com/coreos/go-systemd/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
language: go
|
||||
go: 1.4
|
||||
|
||||
install:
|
||||
- go get github.com/godbus/dbus
|
||||
|
||||
script:
|
||||
- ./test
|
77
vendor/github.com/coreos/go-systemd/CONTRIBUTING.md
generated
vendored
Normal file
77
vendor/github.com/coreos/go-systemd/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
# How to Contribute
|
||||
|
||||
CoreOS projects are [Apache 2.0 licensed](LICENSE) and accept contributions via
|
||||
GitHub pull requests. This document outlines some of the conventions on
|
||||
development workflow, commit message formatting, contact points and other
|
||||
resources to make it easier to get your contribution accepted.
|
||||
|
||||
# Certificate of Origin
|
||||
|
||||
By contributing to this project you agree to the Developer Certificate of
|
||||
Origin (DCO). This document was created by the Linux Kernel community and is a
|
||||
simple statement that you, as a contributor, have the legal right to make the
|
||||
contribution. See the [DCO](DCO) file for details.
|
||||
|
||||
# Email and Chat
|
||||
|
||||
The project currently uses the general CoreOS email list and IRC channel:
|
||||
- Email: [coreos-dev](https://groups.google.com/forum/#!forum/coreos-dev)
|
||||
- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org
|
||||
|
||||
Please avoid emailing maintainers found in the MAINTAINERS file directly. They
|
||||
are very busy and read the mailing lists.
|
||||
|
||||
## Getting Started
|
||||
|
||||
- Fork the repository on GitHub
|
||||
- Read the [README](README.md) for build and test instructions
|
||||
- Play with the project, submit bugs, submit patches!
|
||||
|
||||
## Contribution Flow
|
||||
|
||||
This is a rough outline of what a contributor's workflow looks like:
|
||||
|
||||
- Create a topic branch from where you want to base your work (usually master).
|
||||
- Make commits of logical units.
|
||||
- Make sure your commit messages are in the proper format (see below).
|
||||
- Push your changes to a topic branch in your fork of the repository.
|
||||
- Make sure the tests pass, and add any new tests as appropriate.
|
||||
- Submit a pull request to the original repository.
|
||||
|
||||
Thanks for your contributions!
|
||||
|
||||
### Coding Style
|
||||
|
||||
CoreOS projects written in Go follow a set of style guidelines that we've documented
|
||||
[here](https://github.com/coreos/docs/tree/master/golang). Please follow them when
|
||||
working on your contributions.
|
||||
|
||||
### Format of the Commit Message
|
||||
|
||||
We follow a rough convention for commit messages that is designed to answer two
|
||||
questions: what changed and why. The subject line should feature the what and
|
||||
the body of the commit should describe the why.
|
||||
|
||||
```
|
||||
scripts: add the test-cluster command
|
||||
|
||||
this uses tmux to setup a test cluster that you can easily kill and
|
||||
start for debugging.
|
||||
|
||||
Fixes #38
|
||||
```
|
||||
|
||||
The format can be described more formally as follows:
|
||||
|
||||
```
|
||||
<subsystem>: <what changed>
|
||||
<BLANK LINE>
|
||||
<why this change was made>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
The first line is the subject and should be no longer than 70 characters, the
|
||||
second line is always blank, and other lines should be wrapped at 80 characters.
|
||||
This allows the message to be easier to read on GitHub as well as in various
|
||||
git tools.
|
36
vendor/github.com/coreos/go-systemd/DCO
generated
vendored
Normal file
36
vendor/github.com/coreos/go-systemd/DCO
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
660 York Street, Suite 102,
|
||||
San Francisco, CA 94110 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
54
vendor/github.com/coreos/go-systemd/README.md
generated
vendored
Normal file
54
vendor/github.com/coreos/go-systemd/README.md
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
# go-systemd
|
||||
|
||||
[![Build Status](https://travis-ci.org/coreos/go-systemd.png?branch=master)](https://travis-ci.org/coreos/go-systemd)
|
||||
[![godoc](https://godoc.org/github.com/coreos/go-systemd?status.svg)](http://godoc.org/github.com/coreos/go-systemd)
|
||||
|
||||
Go bindings to systemd. The project has several packages:
|
||||
|
||||
- `activation` - for writing and using socket activation from Go
|
||||
- `dbus` - for starting/stopping/inspecting running services and units
|
||||
- `journal` - for writing to systemd's logging service, journald
|
||||
- `sdjournal` - for reading from journald by wrapping its C API
|
||||
- `machine1` - for registering machines/containers with systemd
|
||||
- `unit` - for (de)serialization and comparison of unit files
|
||||
|
||||
## Socket Activation
|
||||
|
||||
An example HTTP server using socket activation can be quickly set up by following this README on a Linux machine running systemd:
|
||||
|
||||
https://github.com/coreos/go-systemd/tree/master/examples/activation/httpserver
|
||||
|
||||
## Journal
|
||||
|
||||
Using the pure-Go `journal` package you can submit journal entries directly to systemd's journal, taking advantage of features like indexed key/value pairs for each log entry.
|
||||
The `sdjournal` package provides read access to the journal by wrapping around journald's native C API; consequently it requires cgo and the journal headers to be available.
|
||||
|
||||
## D-Bus
|
||||
|
||||
The `dbus` package connects to the [systemd D-Bus API](http://www.freedesktop.org/wiki/Software/systemd/dbus/) and lets you start, stop and introspect systemd units. The API docs are here:
|
||||
|
||||
http://godoc.org/github.com/coreos/go-systemd/dbus
|
||||
|
||||
### Debugging
|
||||
|
||||
Create `/etc/dbus-1/system-local.conf` that looks like this:
|
||||
|
||||
```
|
||||
<!DOCTYPE busconfig PUBLIC
|
||||
"-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
<policy user="root">
|
||||
<allow eavesdrop="true"/>
|
||||
<allow eavesdrop="true" send_destination="*"/>
|
||||
</policy>
|
||||
</busconfig>
|
||||
```
|
||||
|
||||
## machined
|
||||
|
||||
The `machine1` package allows interaction with the [systemd machined D-Bus API](http://www.freedesktop.org/wiki/Software/systemd/machined/).
|
||||
|
||||
## Units
|
||||
|
||||
The `unit` package provides various functions for working with [systemd unit files](http://www.freedesktop.org/software/systemd/man/systemd.unit.html).
|
52
vendor/github.com/coreos/go-systemd/activation/files.go
generated
vendored
Normal file
52
vendor/github.com/coreos/go-systemd/activation/files.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 activation implements primitives for systemd socket activation.
|
||||
package activation
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// based on: https://gist.github.com/alberts/4640792
|
||||
const (
|
||||
listenFdsStart = 3
|
||||
)
|
||||
|
||||
func Files(unsetEnv bool) []*os.File {
|
||||
if unsetEnv {
|
||||
defer os.Unsetenv("LISTEN_PID")
|
||||
defer os.Unsetenv("LISTEN_FDS")
|
||||
}
|
||||
|
||||
pid, err := strconv.Atoi(os.Getenv("LISTEN_PID"))
|
||||
if err != nil || pid != os.Getpid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS"))
|
||||
if err != nil || nfds == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
files := make([]*os.File, 0, nfds)
|
||||
for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ {
|
||||
syscall.CloseOnExec(fd)
|
||||
files = append(files, os.NewFile(uintptr(fd), "LISTEN_FD_"+strconv.Itoa(fd)))
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
82
vendor/github.com/coreos/go-systemd/activation/files_test.go
generated
vendored
Normal file
82
vendor/github.com/coreos/go-systemd/activation/files_test.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 activation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// correctStringWritten fails the text if the correct string wasn't written
|
||||
// to the other side of the pipe.
|
||||
func correctStringWritten(t *testing.T, r *os.File, expected string) bool {
|
||||
bytes := make([]byte, len(expected))
|
||||
io.ReadAtLeast(r, bytes, len(expected))
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Unexpected string %s", string(bytes))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// TestActivation forks out a copy of activation.go example and reads back two
|
||||
// strings from the pipes that are passed in.
|
||||
func TestActivation(t *testing.T) {
|
||||
cmd := exec.Command("go", "run", "../examples/activation/activation.go")
|
||||
|
||||
r1, w1, _ := os.Pipe()
|
||||
r2, w2, _ := os.Pipe()
|
||||
cmd.ExtraFiles = []*os.File{
|
||||
w1,
|
||||
w2,
|
||||
}
|
||||
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1")
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
correctStringWritten(t, r1, "Hello world")
|
||||
correctStringWritten(t, r2, "Goodbye world")
|
||||
}
|
||||
|
||||
func TestActivationNoFix(t *testing.T) {
|
||||
cmd := exec.Command("go", "run", "../examples/activation/activation.go")
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=2")
|
||||
|
||||
out, _ := cmd.CombinedOutput()
|
||||
if bytes.Contains(out, []byte("No files")) == false {
|
||||
t.Fatalf("Child didn't error out as expected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestActivationNoFiles(t *testing.T) {
|
||||
cmd := exec.Command("go", "run", "../examples/activation/activation.go")
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=0", "FIX_LISTEN_PID=1")
|
||||
|
||||
out, _ := cmd.CombinedOutput()
|
||||
if bytes.Contains(out, []byte("No files")) == false {
|
||||
t.Fatalf("Child didn't error out as expected")
|
||||
}
|
||||
}
|
62
vendor/github.com/coreos/go-systemd/activation/listeners.go
generated
vendored
Normal file
62
vendor/github.com/coreos/go-systemd/activation/listeners.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 activation
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Listeners returns a slice containing a net.Listener for each matching socket type
|
||||
// passed to this process.
|
||||
//
|
||||
// The order of the file descriptors is preserved in the returned slice.
|
||||
// Nil values are used to fill any gaps. For example if systemd were to return file descriptors
|
||||
// corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener}
|
||||
func Listeners(unsetEnv bool) ([]net.Listener, error) {
|
||||
files := Files(unsetEnv)
|
||||
listeners := make([]net.Listener, len(files))
|
||||
|
||||
for i, f := range files {
|
||||
if pc, err := net.FileListener(f); err == nil {
|
||||
listeners[i] = pc
|
||||
}
|
||||
}
|
||||
return listeners, nil
|
||||
}
|
||||
|
||||
// TLSListeners returns a slice containing a net.listener for each matching TCP socket type
|
||||
// passed to this process.
|
||||
// It uses default Listeners func and forces TCP sockets handlers to use TLS based on tlsConfig.
|
||||
func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error) {
|
||||
listeners, err := Listeners(unsetEnv)
|
||||
|
||||
if listeners == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tlsConfig != nil && err == nil {
|
||||
tlsConfig.NextProtos = []string{"http/1.1"}
|
||||
|
||||
for i, l := range listeners {
|
||||
// Activate TLS only for TCP sockets
|
||||
if l.Addr().Network() == "tcp" {
|
||||
listeners[i] = tls.NewListener(l, tlsConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return listeners, err
|
||||
}
|
86
vendor/github.com/coreos/go-systemd/activation/listeners_test.go
generated
vendored
Normal file
86
vendor/github.com/coreos/go-systemd/activation/listeners_test.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 activation
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// correctStringWritten fails the text if the correct string wasn't written
|
||||
// to the other side of the pipe.
|
||||
func correctStringWrittenNet(t *testing.T, r net.Conn, expected string) bool {
|
||||
bytes := make([]byte, len(expected))
|
||||
io.ReadAtLeast(r, bytes, len(expected))
|
||||
|
||||
if string(bytes) != expected {
|
||||
t.Fatalf("Unexpected string %s", string(bytes))
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// TestActivation forks out a copy of activation.go example and reads back two
|
||||
// strings from the pipes that are passed in.
|
||||
func TestListeners(t *testing.T) {
|
||||
cmd := exec.Command("go", "run", "../examples/activation/listen.go")
|
||||
|
||||
l1, err := net.Listen("tcp", ":9999")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
l2, err := net.Listen("tcp", ":1234")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
t1 := l1.(*net.TCPListener)
|
||||
t2 := l2.(*net.TCPListener)
|
||||
|
||||
f1, _ := t1.File()
|
||||
f2, _ := t2.File()
|
||||
|
||||
cmd.ExtraFiles = []*os.File{
|
||||
f1,
|
||||
f2,
|
||||
}
|
||||
|
||||
r1, err := net.Dial("tcp", "127.0.0.1:9999")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
r1.Write([]byte("Hi"))
|
||||
|
||||
r2, err := net.Dial("tcp", "127.0.0.1:1234")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
r2.Write([]byte("Hi"))
|
||||
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1")
|
||||
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
println(string(out))
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
correctStringWrittenNet(t, r1, "Hello world")
|
||||
correctStringWrittenNet(t, r2, "Goodbye world")
|
||||
}
|
37
vendor/github.com/coreos/go-systemd/activation/packetconns.go
generated
vendored
Normal file
37
vendor/github.com/coreos/go-systemd/activation/packetconns.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 activation
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// PacketConns returns a slice containing a net.PacketConn for each matching socket type
|
||||
// passed to this process.
|
||||
//
|
||||
// The order of the file descriptors is preserved in the returned slice.
|
||||
// Nil values are used to fill any gaps. For example if systemd were to return file descriptors
|
||||
// corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn}
|
||||
func PacketConns(unsetEnv bool) ([]net.PacketConn, error) {
|
||||
files := Files(unsetEnv)
|
||||
conns := make([]net.PacketConn, len(files))
|
||||
|
||||
for i, f := range files {
|
||||
if pc, err := net.FilePacketConn(f); err == nil {
|
||||
conns[i] = pc
|
||||
}
|
||||
}
|
||||
return conns, nil
|
||||
}
|
68
vendor/github.com/coreos/go-systemd/activation/packetconns_test.go
generated
vendored
Normal file
68
vendor/github.com/coreos/go-systemd/activation/packetconns_test.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 activation
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestActivation forks out a copy of activation.go example and reads back two
|
||||
// strings from the pipes that are passed in.
|
||||
func TestPacketConns(t *testing.T) {
|
||||
cmd := exec.Command("go", "run", "../examples/activation/udpconn.go")
|
||||
|
||||
u1, err := net.ListenUDP("udp", &net.UDPAddr{Port: 9999})
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
u2, err := net.ListenUDP("udp", &net.UDPAddr{Port: 1234})
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
f1, _ := u1.File()
|
||||
f2, _ := u2.File()
|
||||
|
||||
cmd.ExtraFiles = []*os.File{
|
||||
f1,
|
||||
f2,
|
||||
}
|
||||
|
||||
r1, err := net.Dial("udp", "127.0.0.1:9999")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
r1.Write([]byte("Hi"))
|
||||
|
||||
r2, err := net.Dial("udp", "127.0.0.1:1234")
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
r2.Write([]byte("Hi"))
|
||||
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "FIX_LISTEN_PID=1")
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("Cmd output '%s', err: '%s'\n", out, err)
|
||||
}
|
||||
|
||||
correctStringWrittenNet(t, r1, "Hello world")
|
||||
correctStringWrittenNet(t, r2, "Goodbye world")
|
||||
}
|
31
vendor/github.com/coreos/go-systemd/daemon/sdnotify.go
generated
vendored
Normal file
31
vendor/github.com/coreos/go-systemd/daemon/sdnotify.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
// Code forked from Docker project
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
var SdNotifyNoSocket = errors.New("No socket")
|
||||
|
||||
// SdNotify sends a message to the init daemon. It is common to ignore the error.
|
||||
func SdNotify(state string) error {
|
||||
socketAddr := &net.UnixAddr{
|
||||
Name: os.Getenv("NOTIFY_SOCKET"),
|
||||
Net: "unixgram",
|
||||
}
|
||||
|
||||
if socketAddr.Name == "" {
|
||||
return SdNotifyNoSocket
|
||||
}
|
||||
|
||||
conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
_, err = conn.Write([]byte(state))
|
||||
return err
|
||||
}
|
198
vendor/github.com/coreos/go-systemd/dbus/dbus.go
generated
vendored
Normal file
198
vendor/github.com/coreos/go-systemd/dbus/dbus.go
generated
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Integration with the systemd D-Bus API. See http://www.freedesktop.org/wiki/Software/systemd/dbus/
|
||||
package dbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
const (
|
||||
alpha = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`
|
||||
num = `0123456789`
|
||||
alphanum = alpha + num
|
||||
signalBuffer = 100
|
||||
)
|
||||
|
||||
// needsEscape checks whether a byte in a potential dbus ObjectPath needs to be escaped
|
||||
func needsEscape(i int, b byte) bool {
|
||||
// Escape everything that is not a-z-A-Z-0-9
|
||||
// Also escape 0-9 if it's the first character
|
||||
return strings.IndexByte(alphanum, b) == -1 ||
|
||||
(i == 0 && strings.IndexByte(num, b) != -1)
|
||||
}
|
||||
|
||||
// PathBusEscape sanitizes a constituent string of a dbus ObjectPath using the
|
||||
// rules that systemd uses for serializing special characters.
|
||||
func PathBusEscape(path string) string {
|
||||
// Special case the empty string
|
||||
if len(path) == 0 {
|
||||
return "_"
|
||||
}
|
||||
n := []byte{}
|
||||
for i := 0; i < len(path); i++ {
|
||||
c := path[i]
|
||||
if needsEscape(i, c) {
|
||||
e := fmt.Sprintf("_%x", c)
|
||||
n = append(n, []byte(e)...)
|
||||
} else {
|
||||
n = append(n, c)
|
||||
}
|
||||
}
|
||||
return string(n)
|
||||
}
|
||||
|
||||
// Conn is a connection to systemd's dbus endpoint.
|
||||
type Conn struct {
|
||||
// sysconn/sysobj are only used to call dbus methods
|
||||
sysconn *dbus.Conn
|
||||
sysobj dbus.BusObject
|
||||
|
||||
// sigconn/sigobj are only used to receive dbus signals
|
||||
sigconn *dbus.Conn
|
||||
sigobj dbus.BusObject
|
||||
|
||||
jobListener struct {
|
||||
jobs map[dbus.ObjectPath]chan<- string
|
||||
sync.Mutex
|
||||
}
|
||||
subscriber struct {
|
||||
updateCh chan<- *SubStateUpdate
|
||||
errCh chan<- error
|
||||
sync.Mutex
|
||||
ignore map[dbus.ObjectPath]int64
|
||||
cleanIgnore int64
|
||||
}
|
||||
}
|
||||
|
||||
// New establishes a connection to the system bus and authenticates.
|
||||
// Callers should call Close() when done with the connection.
|
||||
func New() (*Conn, error) {
|
||||
return newConnection(func() (*dbus.Conn, error) {
|
||||
return dbusAuthHelloConnection(dbus.SystemBusPrivate)
|
||||
})
|
||||
}
|
||||
|
||||
// NewUserConnection establishes a connection to the session bus and
|
||||
// authenticates. This can be used to connect to systemd user instances.
|
||||
// Callers should call Close() when done with the connection.
|
||||
func NewUserConnection() (*Conn, error) {
|
||||
return newConnection(func() (*dbus.Conn, error) {
|
||||
return dbusAuthHelloConnection(dbus.SessionBusPrivate)
|
||||
})
|
||||
}
|
||||
|
||||
// NewSystemdConnection establishes a private, direct connection to systemd.
|
||||
// This can be used for communicating with systemd without a dbus daemon.
|
||||
// Callers should call Close() when done with the connection.
|
||||
func NewSystemdConnection() (*Conn, error) {
|
||||
return newConnection(func() (*dbus.Conn, error) {
|
||||
// We skip Hello when talking directly to systemd.
|
||||
return dbusAuthConnection(func() (*dbus.Conn, error) {
|
||||
return dbus.Dial("unix:path=/run/systemd/private")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Close closes an established connection
|
||||
func (c *Conn) Close() {
|
||||
c.sysconn.Close()
|
||||
c.sigconn.Close()
|
||||
}
|
||||
|
||||
func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
|
||||
sysconn, err := createBus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sigconn, err := createBus()
|
||||
if err != nil {
|
||||
sysconn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c := &Conn{
|
||||
sysconn: sysconn,
|
||||
sysobj: systemdObject(sysconn),
|
||||
sigconn: sigconn,
|
||||
sigobj: systemdObject(sigconn),
|
||||
}
|
||||
|
||||
c.subscriber.ignore = make(map[dbus.ObjectPath]int64)
|
||||
c.jobListener.jobs = make(map[dbus.ObjectPath]chan<- string)
|
||||
|
||||
// Setup the listeners on jobs so that we can get completions
|
||||
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||
"type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'")
|
||||
|
||||
c.dispatch()
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// GetManagerProperty returns the value of a property on the org.freedesktop.systemd1.Manager
|
||||
// interface. The value is returned in its string representation, as defined at
|
||||
// https://developer.gnome.org/glib/unstable/gvariant-text.html
|
||||
func (c *Conn) GetManagerProperty(prop string) (string, error) {
|
||||
variant, err := c.sysobj.GetProperty("org.freedesktop.systemd1.Manager." + prop)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return variant.String(), nil
|
||||
}
|
||||
|
||||
func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
|
||||
conn, err := createBus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Only use EXTERNAL method, and hardcode the uid (not username)
|
||||
// to avoid a username lookup (which requires a dynamically linked
|
||||
// libc)
|
||||
methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}
|
||||
|
||||
err = conn.Auth(methods)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func dbusAuthHelloConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
|
||||
conn, err := dbusAuthConnection(createBus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = conn.Hello(); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func systemdObject(conn *dbus.Conn) dbus.BusObject {
|
||||
return conn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1"))
|
||||
}
|
77
vendor/github.com/coreos/go-systemd/dbus/dbus_test.go
generated
vendored
Normal file
77
vendor/github.com/coreos/go-systemd/dbus/dbus_test.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNeedsEscape(t *testing.T) {
|
||||
// Anything not 0-9a-zA-Z should always be escaped
|
||||
for want, vals := range map[bool][]byte{
|
||||
false: []byte{'a', 'b', 'z', 'A', 'Q', '1', '4', '9'},
|
||||
true: []byte{'#', '%', '$', '!', '.', '_', '-', '%', '\\'},
|
||||
} {
|
||||
for i := 1; i < 10; i++ {
|
||||
for _, b := range vals {
|
||||
got := needsEscape(i, b)
|
||||
if got != want {
|
||||
t.Errorf("needsEscape(%d, %c) returned %t, want %t", i, b, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 0-9 in position 0 should be escaped
|
||||
for want, vals := range map[bool][]byte{
|
||||
false: []byte{'A', 'a', 'e', 'x', 'Q', 'Z'},
|
||||
true: []byte{'0', '4', '5', '9'},
|
||||
} {
|
||||
for _, b := range vals {
|
||||
got := needsEscape(0, b)
|
||||
if got != want {
|
||||
t.Errorf("needsEscape(0, %c) returned %t, want %t", b, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPathBusEscape(t *testing.T) {
|
||||
for in, want := range map[string]string{
|
||||
"": "_",
|
||||
"foo.service": "foo_2eservice",
|
||||
"foobar": "foobar",
|
||||
"woof@woof.service": "woof_40woof_2eservice",
|
||||
"0123456": "_30123456",
|
||||
"account_db.service": "account_5fdb_2eservice",
|
||||
"got-dashes": "got_2ddashes",
|
||||
} {
|
||||
got := PathBusEscape(in)
|
||||
if got != want {
|
||||
t.Errorf("bad result for PathBusEscape(%s): got %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TestNew ensures that New() works without errors.
|
||||
func TestNew(t *testing.T) {
|
||||
_, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
442
vendor/github.com/coreos/go-systemd/dbus/methods.go
generated
vendored
Normal file
442
vendor/github.com/coreos/go-systemd/dbus/methods.go
generated
vendored
Normal file
@ -0,0 +1,442 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
func (c *Conn) jobComplete(signal *dbus.Signal) {
|
||||
var id uint32
|
||||
var job dbus.ObjectPath
|
||||
var unit string
|
||||
var result string
|
||||
dbus.Store(signal.Body, &id, &job, &unit, &result)
|
||||
c.jobListener.Lock()
|
||||
out, ok := c.jobListener.jobs[job]
|
||||
if ok {
|
||||
out <- result
|
||||
delete(c.jobListener.jobs, job)
|
||||
}
|
||||
c.jobListener.Unlock()
|
||||
}
|
||||
|
||||
func (c *Conn) startJob(ch chan<- string, job string, args ...interface{}) (int, error) {
|
||||
if ch != nil {
|
||||
c.jobListener.Lock()
|
||||
defer c.jobListener.Unlock()
|
||||
}
|
||||
|
||||
var p dbus.ObjectPath
|
||||
err := c.sysobj.Call(job, 0, args...).Store(&p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if ch != nil {
|
||||
c.jobListener.jobs[p] = ch
|
||||
}
|
||||
|
||||
// ignore error since 0 is fine if conversion fails
|
||||
jobID, _ := strconv.Atoi(path.Base(string(p)))
|
||||
|
||||
return jobID, nil
|
||||
}
|
||||
|
||||
// StartUnit enqueues a start job and depending jobs, if any (unless otherwise
|
||||
// specified by the mode string).
|
||||
//
|
||||
// Takes the unit to activate, plus a mode string. The mode needs to be one of
|
||||
// replace, fail, isolate, ignore-dependencies, ignore-requirements. If
|
||||
// "replace" the call will start the unit and its dependencies, possibly
|
||||
// replacing already queued jobs that conflict with this. If "fail" the call
|
||||
// will start the unit and its dependencies, but will fail if this would change
|
||||
// an already queued job. If "isolate" the call will start the unit in question
|
||||
// and terminate all units that aren't dependencies of it. If
|
||||
// "ignore-dependencies" it will start a unit but ignore all its dependencies.
|
||||
// If "ignore-requirements" it will start a unit but only ignore the
|
||||
// requirement dependencies. It is not recommended to make use of the latter
|
||||
// two options.
|
||||
//
|
||||
// If the provided channel is non-nil, a result string will be sent to it upon
|
||||
// job completion: one of done, canceled, timeout, failed, dependency, skipped.
|
||||
// done indicates successful execution of a job. canceled indicates that a job
|
||||
// has been canceled before it finished execution. timeout indicates that the
|
||||
// job timeout was reached. failed indicates that the job failed. dependency
|
||||
// indicates that a job this job has been depending on failed and the job hence
|
||||
// has been removed too. skipped indicates that a job was skipped because it
|
||||
// didn't apply to the units current state.
|
||||
//
|
||||
// If no error occurs, the ID of the underlying systemd job will be returned. There
|
||||
// does exist the possibility for no error to be returned, but for the returned job
|
||||
// ID to be 0. In this case, the actual underlying ID is not 0 and this datapoint
|
||||
// should not be considered authoritative.
|
||||
//
|
||||
// If an error does occur, it will be returned to the user alongside a job ID of 0.
|
||||
func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartUnit", name, mode)
|
||||
}
|
||||
|
||||
// StopUnit is similar to StartUnit but stops the specified unit rather
|
||||
// than starting it.
|
||||
func (c *Conn) StopUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StopUnit", name, mode)
|
||||
}
|
||||
|
||||
// ReloadUnit reloads a unit. Reloading is done only if the unit is already running and fails otherwise.
|
||||
func (c *Conn) ReloadUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
|
||||
}
|
||||
|
||||
// RestartUnit restarts a service. If a service is restarted that isn't
|
||||
// running it will be started.
|
||||
func (c *Conn) RestartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||
return c.startJob(ch, "org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
|
||||
}
|
||||
|
||||
// TryRestartUnit is like RestartUnit, except that a service that isn't running
|
||||
// is not affected by the restart.
|
||||
func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||
return c.startJob(ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
|
||||
}
|
||||
|
||||
// ReloadOrRestart attempts a reload if the unit supports it and use a restart
|
||||
// otherwise.
|
||||
func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode)
|
||||
}
|
||||
|
||||
// ReloadOrTryRestart attempts a reload if the unit supports it and use a "Try"
|
||||
// flavored restart otherwise.
|
||||
func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
|
||||
return c.startJob(ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode)
|
||||
}
|
||||
|
||||
// StartTransientUnit() may be used to create and start a transient unit, which
|
||||
// will be released as soon as it is not running or referenced anymore or the
|
||||
// system is rebooted. name is the unit name including suffix, and must be
|
||||
// unique. mode is the same as in StartUnit(), properties contains properties
|
||||
// of the unit.
|
||||
func (c *Conn) StartTransientUnit(name string, mode string, properties []Property, ch chan<- string) (int, error) {
|
||||
return c.startJob(ch, "org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
|
||||
}
|
||||
|
||||
// KillUnit takes the unit name and a UNIX signal number to send. All of the unit's
|
||||
// processes are killed.
|
||||
func (c *Conn) KillUnit(name string, signal int32) {
|
||||
c.sysobj.Call("org.freedesktop.systemd1.Manager.KillUnit", 0, name, "all", signal).Store()
|
||||
}
|
||||
|
||||
// ResetFailedUnit resets the "failed" state of a specific unit.
|
||||
func (c *Conn) ResetFailedUnit(name string) error {
|
||||
return c.sysobj.Call("org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store()
|
||||
}
|
||||
|
||||
// getProperties takes the unit name and returns all of its dbus object properties, for the given dbus interface
|
||||
func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]interface{}, error) {
|
||||
var err error
|
||||
var props map[string]dbus.Variant
|
||||
|
||||
path := unitPath(unit)
|
||||
if !path.IsValid() {
|
||||
return nil, errors.New("invalid unit name: " + unit)
|
||||
}
|
||||
|
||||
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
||||
err = obj.Call("org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make(map[string]interface{}, len(props))
|
||||
for k, v := range props {
|
||||
out[k] = v.Value()
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetUnitProperties takes the unit name and returns all of its dbus object properties.
|
||||
func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
|
||||
return c.getProperties(unit, "org.freedesktop.systemd1.Unit")
|
||||
}
|
||||
|
||||
func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) {
|
||||
var err error
|
||||
var prop dbus.Variant
|
||||
|
||||
path := unitPath(unit)
|
||||
if !path.IsValid() {
|
||||
return nil, errors.New("invalid unit name: " + unit)
|
||||
}
|
||||
|
||||
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
|
||||
err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Property{Name: propertyName, Value: prop}, nil
|
||||
}
|
||||
|
||||
func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) {
|
||||
return c.getProperty(unit, "org.freedesktop.systemd1.Unit", propertyName)
|
||||
}
|
||||
|
||||
// GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type.
|
||||
// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope
|
||||
// return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit
|
||||
func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) {
|
||||
return c.getProperties(unit, "org.freedesktop.systemd1."+unitType)
|
||||
}
|
||||
|
||||
// SetUnitProperties() may be used to modify certain unit properties at runtime.
|
||||
// Not all properties may be changed at runtime, but many resource management
|
||||
// settings (primarily those in systemd.cgroup(5)) may. The changes are applied
|
||||
// instantly, and stored on disk for future boots, unless runtime is true, in which
|
||||
// case the settings only apply until the next reboot. name is the name of the unit
|
||||
// to modify. properties are the settings to set, encoded as an array of property
|
||||
// name and value pairs.
|
||||
func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error {
|
||||
return c.sysobj.Call("org.freedesktop.systemd1.Manager.SetUnitProperties", 0, name, runtime, properties).Store()
|
||||
}
|
||||
|
||||
func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) {
|
||||
return c.getProperty(unit, "org.freedesktop.systemd1."+unitType, propertyName)
|
||||
}
|
||||
|
||||
type UnitStatus struct {
|
||||
Name string // The primary unit name as string
|
||||
Description string // The human readable description string
|
||||
LoadState string // The load state (i.e. whether the unit file has been loaded successfully)
|
||||
ActiveState string // The active state (i.e. whether the unit is currently started or not)
|
||||
SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not)
|
||||
Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string.
|
||||
Path dbus.ObjectPath // The unit object path
|
||||
JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise
|
||||
JobType string // The job type as string
|
||||
JobPath dbus.ObjectPath // The job object path
|
||||
}
|
||||
|
||||
// ListUnits returns an array with all currently loaded units. Note that
|
||||
// units may be known by multiple names at the same time, and hence there might
|
||||
// be more unit names loaded than actual units behind them.
|
||||
func (c *Conn) ListUnits() ([]UnitStatus, error) {
|
||||
result := make([][]interface{}, 0)
|
||||
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resultInterface := make([]interface{}, len(result))
|
||||
for i := range result {
|
||||
resultInterface[i] = result[i]
|
||||
}
|
||||
|
||||
status := make([]UnitStatus, len(result))
|
||||
statusInterface := make([]interface{}, len(status))
|
||||
for i := range status {
|
||||
statusInterface[i] = &status[i]
|
||||
}
|
||||
|
||||
err = dbus.Store(resultInterface, statusInterface...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
type UnitFile struct {
|
||||
Path string
|
||||
Type string
|
||||
}
|
||||
|
||||
// ListUnitFiles returns an array of all available units on disk.
|
||||
func (c *Conn) ListUnitFiles() ([]UnitFile, error) {
|
||||
result := make([][]interface{}, 0)
|
||||
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitFiles", 0).Store(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resultInterface := make([]interface{}, len(result))
|
||||
for i := range result {
|
||||
resultInterface[i] = result[i]
|
||||
}
|
||||
|
||||
files := make([]UnitFile, len(result))
|
||||
fileInterface := make([]interface{}, len(files))
|
||||
for i := range files {
|
||||
fileInterface[i] = &files[i]
|
||||
}
|
||||
|
||||
err = dbus.Store(resultInterface, fileInterface...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
type LinkUnitFileChange EnableUnitFileChange
|
||||
|
||||
// LinkUnitFiles() links unit files (that are located outside of the
|
||||
// usual unit search paths) into the unit search path.
|
||||
//
|
||||
// It takes a list of absolute paths to unit files to link and two
|
||||
// booleans. The first boolean controls whether the unit shall be
|
||||
// enabled for runtime only (true, /run), or persistently (false,
|
||||
// /etc).
|
||||
// The second controls whether symlinks pointing to other units shall
|
||||
// be replaced if necessary.
|
||||
//
|
||||
// This call returns a list of the changes made. The list consists of
|
||||
// structures with three strings: the type of the change (one of symlink
|
||||
// or unlink), the file name of the symlink and the destination of the
|
||||
// symlink.
|
||||
func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) {
|
||||
result := make([][]interface{}, 0)
|
||||
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.LinkUnitFiles", 0, files, runtime, force).Store(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resultInterface := make([]interface{}, len(result))
|
||||
for i := range result {
|
||||
resultInterface[i] = result[i]
|
||||
}
|
||||
|
||||
changes := make([]LinkUnitFileChange, len(result))
|
||||
changesInterface := make([]interface{}, len(changes))
|
||||
for i := range changes {
|
||||
changesInterface[i] = &changes[i]
|
||||
}
|
||||
|
||||
err = dbus.Store(resultInterface, changesInterface...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
// EnableUnitFiles() may be used to enable one or more units in the system (by
|
||||
// creating symlinks to them in /etc or /run).
|
||||
//
|
||||
// It takes a list of unit files to enable (either just file names or full
|
||||
// absolute paths if the unit files are residing outside the usual unit
|
||||
// search paths), and two booleans: the first controls whether the unit shall
|
||||
// be enabled for runtime only (true, /run), or persistently (false, /etc).
|
||||
// The second one controls whether symlinks pointing to other units shall
|
||||
// be replaced if necessary.
|
||||
//
|
||||
// This call returns one boolean and an array with the changes made. The
|
||||
// boolean signals whether the unit files contained any enablement
|
||||
// information (i.e. an [Install]) section. The changes list consists of
|
||||
// structures with three strings: the type of the change (one of symlink
|
||||
// or unlink), the file name of the symlink and the destination of the
|
||||
// symlink.
|
||||
func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
|
||||
var carries_install_info bool
|
||||
|
||||
result := make([][]interface{}, 0)
|
||||
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
resultInterface := make([]interface{}, len(result))
|
||||
for i := range result {
|
||||
resultInterface[i] = result[i]
|
||||
}
|
||||
|
||||
changes := make([]EnableUnitFileChange, len(result))
|
||||
changesInterface := make([]interface{}, len(changes))
|
||||
for i := range changes {
|
||||
changesInterface[i] = &changes[i]
|
||||
}
|
||||
|
||||
err = dbus.Store(resultInterface, changesInterface...)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
|
||||
return carries_install_info, changes, nil
|
||||
}
|
||||
|
||||
type EnableUnitFileChange struct {
|
||||
Type string // Type of the change (one of symlink or unlink)
|
||||
Filename string // File name of the symlink
|
||||
Destination string // Destination of the symlink
|
||||
}
|
||||
|
||||
// DisableUnitFiles() may be used to disable one or more units in the system (by
|
||||
// removing symlinks to them from /etc or /run).
|
||||
//
|
||||
// It takes a list of unit files to disable (either just file names or full
|
||||
// absolute paths if the unit files are residing outside the usual unit
|
||||
// search paths), and one boolean: whether the unit was enabled for runtime
|
||||
// only (true, /run), or persistently (false, /etc).
|
||||
//
|
||||
// This call returns an array with the changes made. The changes list
|
||||
// consists of structures with three strings: the type of the change (one of
|
||||
// symlink or unlink), the file name of the symlink and the destination of the
|
||||
// symlink.
|
||||
func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) {
|
||||
result := make([][]interface{}, 0)
|
||||
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.DisableUnitFiles", 0, files, runtime).Store(&result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resultInterface := make([]interface{}, len(result))
|
||||
for i := range result {
|
||||
resultInterface[i] = result[i]
|
||||
}
|
||||
|
||||
changes := make([]DisableUnitFileChange, len(result))
|
||||
changesInterface := make([]interface{}, len(changes))
|
||||
for i := range changes {
|
||||
changesInterface[i] = &changes[i]
|
||||
}
|
||||
|
||||
err = dbus.Store(resultInterface, changesInterface...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
type DisableUnitFileChange struct {
|
||||
Type string // Type of the change (one of symlink or unlink)
|
||||
Filename string // File name of the symlink
|
||||
Destination string // Destination of the symlink
|
||||
}
|
||||
|
||||
// Reload instructs systemd to scan for and reload unit files. This is
|
||||
// equivalent to a 'systemctl daemon-reload'.
|
||||
func (c *Conn) Reload() error {
|
||||
return c.sysobj.Call("org.freedesktop.systemd1.Manager.Reload", 0).Store()
|
||||
}
|
||||
|
||||
func unitPath(name string) dbus.ObjectPath {
|
||||
return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name))
|
||||
}
|
345
vendor/github.com/coreos/go-systemd/dbus/methods_test.go
generated
vendored
Normal file
345
vendor/github.com/coreos/go-systemd/dbus/methods_test.go
generated
vendored
Normal file
@ -0,0 +1,345 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 dbus
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
func setupConn(t *testing.T) *Conn {
|
||||
conn, err := New()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
func findFixture(target string, t *testing.T) string {
|
||||
abs, err := filepath.Abs("../fixtures/" + target)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return abs
|
||||
}
|
||||
|
||||
func setupUnit(target string, conn *Conn, t *testing.T) {
|
||||
// Blindly stop the unit in case it is running
|
||||
conn.StopUnit(target, "replace", nil)
|
||||
|
||||
// Blindly remove the symlink in case it exists
|
||||
targetRun := filepath.Join("/run/systemd/system/", target)
|
||||
os.Remove(targetRun)
|
||||
}
|
||||
|
||||
func linkUnit(target string, conn *Conn, t *testing.T) {
|
||||
abs := findFixture(target, t)
|
||||
fixture := []string{abs}
|
||||
|
||||
changes, err := conn.LinkUnitFiles(fixture, true, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(changes) < 1 {
|
||||
t.Fatalf("Expected one change, got %v", changes)
|
||||
}
|
||||
|
||||
runPath := filepath.Join("/run/systemd/system/", target)
|
||||
if changes[0].Filename != runPath {
|
||||
t.Fatal("Unexpected target filename")
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that basic unit starting and stopping works.
|
||||
func TestStartStopUnit(t *testing.T) {
|
||||
target := "start-stop.service"
|
||||
conn := setupConn(t)
|
||||
|
||||
setupUnit(target, conn, t)
|
||||
linkUnit(target, conn, t)
|
||||
|
||||
// 2. Start the unit
|
||||
reschan := make(chan string)
|
||||
_, err := conn.StartUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
job := <-reschan
|
||||
if job != "done" {
|
||||
t.Fatal("Job is not done:", job)
|
||||
}
|
||||
|
||||
units, err := conn.ListUnits()
|
||||
|
||||
var unit *UnitStatus
|
||||
for _, u := range units {
|
||||
if u.Name == target {
|
||||
unit = &u
|
||||
}
|
||||
}
|
||||
|
||||
if unit == nil {
|
||||
t.Fatalf("Test unit not found in list")
|
||||
}
|
||||
|
||||
if unit.ActiveState != "active" {
|
||||
t.Fatalf("Test unit not active")
|
||||
}
|
||||
|
||||
// 3. Stop the unit
|
||||
_, err = conn.StopUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wait for StopUnit job to complete
|
||||
<-reschan
|
||||
|
||||
units, err = conn.ListUnits()
|
||||
|
||||
unit = nil
|
||||
for _, u := range units {
|
||||
if u.Name == target {
|
||||
unit = &u
|
||||
}
|
||||
}
|
||||
|
||||
if unit != nil {
|
||||
t.Fatalf("Test unit found in list, should be stopped")
|
||||
}
|
||||
}
|
||||
|
||||
// Enables a unit and then immediately tears it down
|
||||
func TestEnableDisableUnit(t *testing.T) {
|
||||
target := "enable-disable.service"
|
||||
conn := setupConn(t)
|
||||
|
||||
setupUnit(target, conn, t)
|
||||
abs := findFixture(target, t)
|
||||
runPath := filepath.Join("/run/systemd/system/", target)
|
||||
|
||||
// 1. Enable the unit
|
||||
install, changes, err := conn.EnableUnitFiles([]string{abs}, true, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if install != false {
|
||||
t.Fatal("Install was true")
|
||||
}
|
||||
|
||||
if len(changes) < 1 {
|
||||
t.Fatalf("Expected one change, got %v", changes)
|
||||
}
|
||||
|
||||
if changes[0].Filename != runPath {
|
||||
t.Fatal("Unexpected target filename")
|
||||
}
|
||||
|
||||
// 2. Disable the unit
|
||||
dChanges, err := conn.DisableUnitFiles([]string{abs}, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(dChanges) != 1 {
|
||||
t.Fatalf("Changes should include the path, %v", dChanges)
|
||||
}
|
||||
if dChanges[0].Filename != runPath {
|
||||
t.Fatalf("Change should include correct filename, %+v", dChanges[0])
|
||||
}
|
||||
if dChanges[0].Destination != "" {
|
||||
t.Fatalf("Change destination should be empty, %+v", dChanges[0])
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetUnitProperties reads the `-.mount` which should exist on all systemd
|
||||
// systems and ensures that one of its properties is valid.
|
||||
func TestGetUnitProperties(t *testing.T) {
|
||||
conn := setupConn(t)
|
||||
|
||||
unit := "-.mount"
|
||||
|
||||
info, err := conn.GetUnitProperties(unit)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
names := info["Wants"].([]string)
|
||||
|
||||
if len(names) < 1 {
|
||||
t.Fatal("/ is unwanted")
|
||||
}
|
||||
|
||||
if names[0] != "system.slice" {
|
||||
t.Fatal("unexpected wants for /")
|
||||
}
|
||||
|
||||
prop, err := conn.GetUnitProperty(unit, "Wants")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if prop.Name != "Wants" {
|
||||
t.Fatal("unexpected property name")
|
||||
}
|
||||
|
||||
val := prop.Value.Value().([]string)
|
||||
if !reflect.DeepEqual(val, names) {
|
||||
t.Fatal("unexpected property value")
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetUnitPropertiesRejectsInvalidName attempts to get the properties for a
|
||||
// unit with an invalid name. This test should be run with --test.timeout set,
|
||||
// as a fail will manifest as GetUnitProperties hanging indefinitely.
|
||||
func TestGetUnitPropertiesRejectsInvalidName(t *testing.T) {
|
||||
conn := setupConn(t)
|
||||
|
||||
unit := "//invalid#$^/"
|
||||
|
||||
_, err := conn.GetUnitProperties(unit)
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error, got nil")
|
||||
}
|
||||
|
||||
_, err = conn.GetUnitProperty(unit, "Wants")
|
||||
if err == nil {
|
||||
t.Fatal("Expected an error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
// TestSetUnitProperties changes a cgroup setting on the `tmp.mount`
|
||||
// which should exist on all systemd systems and ensures that the
|
||||
// property was set.
|
||||
func TestSetUnitProperties(t *testing.T) {
|
||||
conn := setupConn(t)
|
||||
|
||||
unit := "tmp.mount"
|
||||
|
||||
if err := conn.SetUnitProperties(unit, true, Property{"CPUShares", dbus.MakeVariant(uint64(1023))}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
info, err := conn.GetUnitTypeProperties(unit, "Mount")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
value := info["CPUShares"].(uint64)
|
||||
if value != 1023 {
|
||||
t.Fatal("CPUShares of unit is not 1023:", value)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that basic transient unit starting and stopping works.
|
||||
func TestStartStopTransientUnit(t *testing.T) {
|
||||
conn := setupConn(t)
|
||||
|
||||
props := []Property{
|
||||
PropExecStart([]string{"/bin/sleep", "400"}, false),
|
||||
}
|
||||
target := fmt.Sprintf("testing-transient-%d.service", rand.Int())
|
||||
|
||||
// Start the unit
|
||||
reschan := make(chan string)
|
||||
_, err := conn.StartTransientUnit(target, "replace", props, reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
job := <-reschan
|
||||
if job != "done" {
|
||||
t.Fatal("Job is not done:", job)
|
||||
}
|
||||
|
||||
units, err := conn.ListUnits()
|
||||
|
||||
var unit *UnitStatus
|
||||
for _, u := range units {
|
||||
if u.Name == target {
|
||||
unit = &u
|
||||
}
|
||||
}
|
||||
|
||||
if unit == nil {
|
||||
t.Fatalf("Test unit not found in list")
|
||||
}
|
||||
|
||||
if unit.ActiveState != "active" {
|
||||
t.Fatalf("Test unit not active")
|
||||
}
|
||||
|
||||
// 3. Stop the unit
|
||||
_, err = conn.StopUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wait for StopUnit job to complete
|
||||
<-reschan
|
||||
|
||||
units, err = conn.ListUnits()
|
||||
|
||||
unit = nil
|
||||
for _, u := range units {
|
||||
if u.Name == target {
|
||||
unit = &u
|
||||
}
|
||||
}
|
||||
|
||||
if unit != nil {
|
||||
t.Fatalf("Test unit found in list, should be stopped")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnJobListener(t *testing.T) {
|
||||
target := "start-stop.service"
|
||||
conn := setupConn(t)
|
||||
|
||||
setupUnit(target, conn, t)
|
||||
linkUnit(target, conn, t)
|
||||
|
||||
jobSize := len(conn.jobListener.jobs)
|
||||
|
||||
reschan := make(chan string)
|
||||
_, err := conn.StartUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
<-reschan
|
||||
|
||||
_, err = conn.StopUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
<-reschan
|
||||
|
||||
currentJobSize := len(conn.jobListener.jobs)
|
||||
if jobSize != currentJobSize {
|
||||
t.Fatal("JobListener jobs leaked")
|
||||
}
|
||||
}
|
218
vendor/github.com/coreos/go-systemd/dbus/properties.go
generated
vendored
Normal file
218
vendor/github.com/coreos/go-systemd/dbus/properties.go
generated
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 dbus
|
||||
|
||||
import (
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
// From the systemd docs:
|
||||
//
|
||||
// The properties array of StartTransientUnit() may take many of the settings
|
||||
// that may also be configured in unit files. Not all parameters are currently
|
||||
// accepted though, but we plan to cover more properties with future release.
|
||||
// Currently you may set the Description, Slice and all dependency types of
|
||||
// units, as well as RemainAfterExit, ExecStart for service units,
|
||||
// TimeoutStopUSec and PIDs for scope units, and CPUAccounting, CPUShares,
|
||||
// BlockIOAccounting, BlockIOWeight, BlockIOReadBandwidth,
|
||||
// BlockIOWriteBandwidth, BlockIODeviceWeight, MemoryAccounting, MemoryLimit,
|
||||
// DevicePolicy, DeviceAllow for services/scopes/slices. These fields map
|
||||
// directly to their counterparts in unit files and as normal D-Bus object
|
||||
// properties. The exception here is the PIDs field of scope units which is
|
||||
// used for construction of the scope only and specifies the initial PIDs to
|
||||
// add to the scope object.
|
||||
|
||||
type Property struct {
|
||||
Name string
|
||||
Value dbus.Variant
|
||||
}
|
||||
|
||||
type PropertyCollection struct {
|
||||
Name string
|
||||
Properties []Property
|
||||
}
|
||||
|
||||
type execStart struct {
|
||||
Path string // the binary path to execute
|
||||
Args []string // an array with all arguments to pass to the executed command, starting with argument 0
|
||||
UncleanIsFailure bool // a boolean whether it should be considered a failure if the process exits uncleanly
|
||||
}
|
||||
|
||||
// PropExecStart sets the ExecStart service property. The first argument is a
|
||||
// slice with the binary path to execute followed by the arguments to pass to
|
||||
// the executed command. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart=
|
||||
func PropExecStart(command []string, uncleanIsFailure bool) Property {
|
||||
execStarts := []execStart{
|
||||
execStart{
|
||||
Path: command[0],
|
||||
Args: command,
|
||||
UncleanIsFailure: uncleanIsFailure,
|
||||
},
|
||||
}
|
||||
|
||||
return Property{
|
||||
Name: "ExecStart",
|
||||
Value: dbus.MakeVariant(execStarts),
|
||||
}
|
||||
}
|
||||
|
||||
// PropRemainAfterExit sets the RemainAfterExit service property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit=
|
||||
func PropRemainAfterExit(b bool) Property {
|
||||
return Property{
|
||||
Name: "RemainAfterExit",
|
||||
Value: dbus.MakeVariant(b),
|
||||
}
|
||||
}
|
||||
|
||||
// PropDescription sets the Description unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description=
|
||||
func PropDescription(desc string) Property {
|
||||
return Property{
|
||||
Name: "Description",
|
||||
Value: dbus.MakeVariant(desc),
|
||||
}
|
||||
}
|
||||
|
||||
func propDependency(name string, units []string) Property {
|
||||
return Property{
|
||||
Name: name,
|
||||
Value: dbus.MakeVariant(units),
|
||||
}
|
||||
}
|
||||
|
||||
// PropRequires sets the Requires unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires=
|
||||
func PropRequires(units ...string) Property {
|
||||
return propDependency("Requires", units)
|
||||
}
|
||||
|
||||
// PropRequiresOverridable sets the RequiresOverridable unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresOverridable=
|
||||
func PropRequiresOverridable(units ...string) Property {
|
||||
return propDependency("RequiresOverridable", units)
|
||||
}
|
||||
|
||||
// PropRequisite sets the Requisite unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requisite=
|
||||
func PropRequisite(units ...string) Property {
|
||||
return propDependency("Requisite", units)
|
||||
}
|
||||
|
||||
// PropRequisiteOverridable sets the RequisiteOverridable unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequisiteOverridable=
|
||||
func PropRequisiteOverridable(units ...string) Property {
|
||||
return propDependency("RequisiteOverridable", units)
|
||||
}
|
||||
|
||||
// PropWants sets the Wants unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Wants=
|
||||
func PropWants(units ...string) Property {
|
||||
return propDependency("Wants", units)
|
||||
}
|
||||
|
||||
// PropBindsTo sets the BindsTo unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo=
|
||||
func PropBindsTo(units ...string) Property {
|
||||
return propDependency("BindsTo", units)
|
||||
}
|
||||
|
||||
// PropRequiredBy sets the RequiredBy unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredBy=
|
||||
func PropRequiredBy(units ...string) Property {
|
||||
return propDependency("RequiredBy", units)
|
||||
}
|
||||
|
||||
// PropRequiredByOverridable sets the RequiredByOverridable unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredByOverridable=
|
||||
func PropRequiredByOverridable(units ...string) Property {
|
||||
return propDependency("RequiredByOverridable", units)
|
||||
}
|
||||
|
||||
// PropWantedBy sets the WantedBy unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy=
|
||||
func PropWantedBy(units ...string) Property {
|
||||
return propDependency("WantedBy", units)
|
||||
}
|
||||
|
||||
// PropBoundBy sets the BoundBy unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#BoundBy=
|
||||
func PropBoundBy(units ...string) Property {
|
||||
return propDependency("BoundBy", units)
|
||||
}
|
||||
|
||||
// PropConflicts sets the Conflicts unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts=
|
||||
func PropConflicts(units ...string) Property {
|
||||
return propDependency("Conflicts", units)
|
||||
}
|
||||
|
||||
// PropConflictedBy sets the ConflictedBy unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConflictedBy=
|
||||
func PropConflictedBy(units ...string) Property {
|
||||
return propDependency("ConflictedBy", units)
|
||||
}
|
||||
|
||||
// PropBefore sets the Before unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before=
|
||||
func PropBefore(units ...string) Property {
|
||||
return propDependency("Before", units)
|
||||
}
|
||||
|
||||
// PropAfter sets the After unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#After=
|
||||
func PropAfter(units ...string) Property {
|
||||
return propDependency("After", units)
|
||||
}
|
||||
|
||||
// PropOnFailure sets the OnFailure unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#OnFailure=
|
||||
func PropOnFailure(units ...string) Property {
|
||||
return propDependency("OnFailure", units)
|
||||
}
|
||||
|
||||
// PropTriggers sets the Triggers unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Triggers=
|
||||
func PropTriggers(units ...string) Property {
|
||||
return propDependency("Triggers", units)
|
||||
}
|
||||
|
||||
// PropTriggeredBy sets the TriggeredBy unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#TriggeredBy=
|
||||
func PropTriggeredBy(units ...string) Property {
|
||||
return propDependency("TriggeredBy", units)
|
||||
}
|
||||
|
||||
// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#PropagatesReloadTo=
|
||||
func PropPropagatesReloadTo(units ...string) Property {
|
||||
return propDependency("PropagatesReloadTo", units)
|
||||
}
|
||||
|
||||
// PropRequiresMountsFor sets the RequiresMountsFor unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor=
|
||||
func PropRequiresMountsFor(units ...string) Property {
|
||||
return propDependency("RequiresMountsFor", units)
|
||||
}
|
||||
|
||||
// PropSlice sets the Slice unit property. See
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Slice=
|
||||
func PropSlice(slice string) Property {
|
||||
return Property{
|
||||
Name: "Slice",
|
||||
Value: dbus.MakeVariant(slice),
|
||||
}
|
||||
}
|
47
vendor/github.com/coreos/go-systemd/dbus/set.go
generated
vendored
Normal file
47
vendor/github.com/coreos/go-systemd/dbus/set.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 dbus
|
||||
|
||||
type set struct {
|
||||
data map[string]bool
|
||||
}
|
||||
|
||||
func (s *set) Add(value string) {
|
||||
s.data[value] = true
|
||||
}
|
||||
|
||||
func (s *set) Remove(value string) {
|
||||
delete(s.data, value)
|
||||
}
|
||||
|
||||
func (s *set) Contains(value string) (exists bool) {
|
||||
_, exists = s.data[value]
|
||||
return
|
||||
}
|
||||
|
||||
func (s *set) Length() int {
|
||||
return len(s.data)
|
||||
}
|
||||
|
||||
func (s *set) Values() (values []string) {
|
||||
for val, _ := range s.data {
|
||||
values = append(values, val)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func newSet() *set {
|
||||
return &set{make(map[string]bool)}
|
||||
}
|
53
vendor/github.com/coreos/go-systemd/dbus/set_test.go
generated
vendored
Normal file
53
vendor/github.com/coreos/go-systemd/dbus/set_test.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestBasicSetActions asserts that Add & Remove behavior is correct
|
||||
func TestBasicSetActions(t *testing.T) {
|
||||
s := newSet()
|
||||
|
||||
if s.Contains("foo") {
|
||||
t.Fatal("set should not contain 'foo'")
|
||||
}
|
||||
|
||||
s.Add("foo")
|
||||
|
||||
if !s.Contains("foo") {
|
||||
t.Fatal("set should contain 'foo'")
|
||||
}
|
||||
|
||||
v := s.Values()
|
||||
if len(v) != 1 {
|
||||
t.Fatal("set.Values did not report correct number of values")
|
||||
}
|
||||
if v[0] != "foo" {
|
||||
t.Fatal("set.Values did not report value")
|
||||
}
|
||||
|
||||
s.Remove("foo")
|
||||
|
||||
if s.Contains("foo") {
|
||||
t.Fatal("set should not contain 'foo'")
|
||||
}
|
||||
|
||||
v = s.Values()
|
||||
if len(v) != 0 {
|
||||
t.Fatal("set.Values did not report correct number of values")
|
||||
}
|
||||
}
|
250
vendor/github.com/coreos/go-systemd/dbus/subscription.go
generated
vendored
Normal file
250
vendor/github.com/coreos/go-systemd/dbus/subscription.go
generated
vendored
Normal file
@ -0,0 +1,250 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 dbus
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
const (
|
||||
cleanIgnoreInterval = int64(10 * time.Second)
|
||||
ignoreInterval = int64(30 * time.Millisecond)
|
||||
)
|
||||
|
||||
// Subscribe sets up this connection to subscribe to all systemd dbus events.
|
||||
// This is required before calling SubscribeUnits. When the connection closes
|
||||
// systemd will automatically stop sending signals so there is no need to
|
||||
// explicitly call Unsubscribe().
|
||||
func (c *Conn) Subscribe() error {
|
||||
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||
"type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'")
|
||||
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||
"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")
|
||||
|
||||
err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unsubscribe this connection from systemd dbus events.
|
||||
func (c *Conn) Unsubscribe() error {
|
||||
err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) dispatch() {
|
||||
ch := make(chan *dbus.Signal, signalBuffer)
|
||||
|
||||
c.sigconn.Signal(ch)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
signal, ok := <-ch
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if signal.Name == "org.freedesktop.systemd1.Manager.JobRemoved" {
|
||||
c.jobComplete(signal)
|
||||
}
|
||||
|
||||
if c.subscriber.updateCh == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var unitPath dbus.ObjectPath
|
||||
switch signal.Name {
|
||||
case "org.freedesktop.systemd1.Manager.JobRemoved":
|
||||
unitName := signal.Body[2].(string)
|
||||
c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath)
|
||||
case "org.freedesktop.systemd1.Manager.UnitNew":
|
||||
unitPath = signal.Body[1].(dbus.ObjectPath)
|
||||
case "org.freedesktop.DBus.Properties.PropertiesChanged":
|
||||
if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" {
|
||||
unitPath = signal.Path
|
||||
}
|
||||
}
|
||||
|
||||
if unitPath == dbus.ObjectPath("") {
|
||||
continue
|
||||
}
|
||||
|
||||
c.sendSubStateUpdate(unitPath)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Returns two unbuffered channels which will receive all changed units every
|
||||
// interval. Deleted units are sent as nil.
|
||||
func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) {
|
||||
return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil)
|
||||
}
|
||||
|
||||
// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer
|
||||
// size of the channels, the comparison function for detecting changes and a filter
|
||||
// function for cutting down on the noise that your channel receives.
|
||||
func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func(string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
|
||||
old := make(map[string]*UnitStatus)
|
||||
statusChan := make(chan map[string]*UnitStatus, buffer)
|
||||
errChan := make(chan error, buffer)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
timerChan := time.After(interval)
|
||||
|
||||
units, err := c.ListUnits()
|
||||
if err == nil {
|
||||
cur := make(map[string]*UnitStatus)
|
||||
for i := range units {
|
||||
if filterUnit != nil && filterUnit(units[i].Name) {
|
||||
continue
|
||||
}
|
||||
cur[units[i].Name] = &units[i]
|
||||
}
|
||||
|
||||
// add all new or changed units
|
||||
changed := make(map[string]*UnitStatus)
|
||||
for n, u := range cur {
|
||||
if oldU, ok := old[n]; !ok || isChanged(oldU, u) {
|
||||
changed[n] = u
|
||||
}
|
||||
delete(old, n)
|
||||
}
|
||||
|
||||
// add all deleted units
|
||||
for oldN := range old {
|
||||
changed[oldN] = nil
|
||||
}
|
||||
|
||||
old = cur
|
||||
|
||||
if len(changed) != 0 {
|
||||
statusChan <- changed
|
||||
}
|
||||
} else {
|
||||
errChan <- err
|
||||
}
|
||||
|
||||
<-timerChan
|
||||
}
|
||||
}()
|
||||
|
||||
return statusChan, errChan
|
||||
}
|
||||
|
||||
type SubStateUpdate struct {
|
||||
UnitName string
|
||||
SubState string
|
||||
}
|
||||
|
||||
// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
|
||||
// Although this writes to updateCh on every state change, the reported state
|
||||
// may be more recent than the change that generated it (due to an unavoidable
|
||||
// race in the systemd dbus interface). That is, this method provides a good
|
||||
// way to keep a current view of all units' states, but is not guaranteed to
|
||||
// show every state transition they go through. Furthermore, state changes
|
||||
// will only be written to the channel with non-blocking writes. If updateCh
|
||||
// is full, it attempts to write an error to errCh; if errCh is full, the error
|
||||
// passes silently.
|
||||
func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) {
|
||||
c.subscriber.Lock()
|
||||
defer c.subscriber.Unlock()
|
||||
c.subscriber.updateCh = updateCh
|
||||
c.subscriber.errCh = errCh
|
||||
}
|
||||
|
||||
func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
|
||||
c.subscriber.Lock()
|
||||
defer c.subscriber.Unlock()
|
||||
|
||||
if c.shouldIgnore(path) {
|
||||
return
|
||||
}
|
||||
|
||||
info, err := c.GetUnitProperties(string(path))
|
||||
if err != nil {
|
||||
select {
|
||||
case c.subscriber.errCh <- err:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
name := info["Id"].(string)
|
||||
substate := info["SubState"].(string)
|
||||
|
||||
update := &SubStateUpdate{name, substate}
|
||||
select {
|
||||
case c.subscriber.updateCh <- update:
|
||||
default:
|
||||
select {
|
||||
case c.subscriber.errCh <- errors.New("update channel full!"):
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
c.updateIgnore(path, info)
|
||||
}
|
||||
|
||||
// The ignore functions work around a wart in the systemd dbus interface.
|
||||
// Requesting the properties of an unloaded unit will cause systemd to send a
|
||||
// pair of UnitNew/UnitRemoved signals. Because we need to get a unit's
|
||||
// properties on UnitNew (as that's the only indication of a new unit coming up
|
||||
// for the first time), we would enter an infinite loop if we did not attempt
|
||||
// to detect and ignore these spurious signals. The signal themselves are
|
||||
// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an
|
||||
// unloaded unit's signals for a short time after requesting its properties.
|
||||
// This means that we will miss e.g. a transient unit being restarted
|
||||
// *immediately* upon failure and also a transient unit being started
|
||||
// immediately after requesting its status (with systemctl status, for example,
|
||||
// because this causes a UnitNew signal to be sent which then causes us to fetch
|
||||
// the properties).
|
||||
|
||||
func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool {
|
||||
t, ok := c.subscriber.ignore[path]
|
||||
return ok && t >= time.Now().UnixNano()
|
||||
}
|
||||
|
||||
func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) {
|
||||
c.cleanIgnore()
|
||||
|
||||
// unit is unloaded - it will trigger bad systemd dbus behavior
|
||||
if info["LoadState"].(string) == "not-found" {
|
||||
c.subscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval
|
||||
}
|
||||
}
|
||||
|
||||
// without this, ignore would grow unboundedly over time
|
||||
func (c *Conn) cleanIgnore() {
|
||||
now := time.Now().UnixNano()
|
||||
if c.subscriber.cleanIgnore < now {
|
||||
c.subscriber.cleanIgnore = now + cleanIgnoreInterval
|
||||
|
||||
for p, t := range c.subscriber.ignore {
|
||||
if t < now {
|
||||
delete(c.subscriber.ignore, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
57
vendor/github.com/coreos/go-systemd/dbus/subscription_set.go
generated
vendored
Normal file
57
vendor/github.com/coreos/go-systemd/dbus/subscription_set.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 dbus
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// SubscriptionSet returns a subscription set which is like conn.Subscribe but
|
||||
// can filter to only return events for a set of units.
|
||||
type SubscriptionSet struct {
|
||||
*set
|
||||
conn *Conn
|
||||
}
|
||||
|
||||
func (s *SubscriptionSet) filter(unit string) bool {
|
||||
return !s.Contains(unit)
|
||||
}
|
||||
|
||||
// Subscribe starts listening for dbus events for all of the units in the set.
|
||||
// Returns channels identical to conn.SubscribeUnits.
|
||||
func (s *SubscriptionSet) Subscribe() (<-chan map[string]*UnitStatus, <-chan error) {
|
||||
// TODO: Make fully evented by using systemd 209 with properties changed values
|
||||
return s.conn.SubscribeUnitsCustom(time.Second, 0,
|
||||
mismatchUnitStatus,
|
||||
func(unit string) bool { return s.filter(unit) },
|
||||
)
|
||||
}
|
||||
|
||||
// NewSubscriptionSet returns a new subscription set.
|
||||
func (conn *Conn) NewSubscriptionSet() *SubscriptionSet {
|
||||
return &SubscriptionSet{newSet(), conn}
|
||||
}
|
||||
|
||||
// mismatchUnitStatus returns true if the provided UnitStatus objects
|
||||
// are not equivalent. false is returned if the objects are equivalent.
|
||||
// Only the Name, Description and state-related fields are used in
|
||||
// the comparison.
|
||||
func mismatchUnitStatus(u1, u2 *UnitStatus) bool {
|
||||
return u1.Name != u2.Name ||
|
||||
u1.Description != u2.Description ||
|
||||
u1.LoadState != u2.LoadState ||
|
||||
u1.ActiveState != u2.ActiveState ||
|
||||
u1.SubState != u2.SubState
|
||||
}
|
82
vendor/github.com/coreos/go-systemd/dbus/subscription_set_test.go
generated
vendored
Normal file
82
vendor/github.com/coreos/go-systemd/dbus/subscription_set_test.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestSubscribeUnit exercises the basics of subscription of a particular unit.
|
||||
func TestSubscriptionSetUnit(t *testing.T) {
|
||||
target := "subscribe-events-set.service"
|
||||
|
||||
conn, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
subSet := conn.NewSubscriptionSet()
|
||||
evChan, errChan := subSet.Subscribe()
|
||||
|
||||
subSet.Add(target)
|
||||
setupUnit(target, conn, t)
|
||||
linkUnit(target, conn, t)
|
||||
|
||||
reschan := make(chan string)
|
||||
_, err = conn.StartUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
job := <-reschan
|
||||
if job != "done" {
|
||||
t.Fatal("Couldn't start", target)
|
||||
}
|
||||
|
||||
timeout := make(chan bool, 1)
|
||||
go func() {
|
||||
time.Sleep(3 * time.Second)
|
||||
close(timeout)
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case changes := <-evChan:
|
||||
tCh, ok := changes[target]
|
||||
|
||||
if !ok {
|
||||
t.Fatal("Unexpected event:", changes)
|
||||
}
|
||||
|
||||
if tCh.ActiveState == "active" && tCh.Name == target {
|
||||
goto success
|
||||
}
|
||||
case err = <-errChan:
|
||||
t.Fatal(err)
|
||||
case <-timeout:
|
||||
t.Fatal("Reached timeout")
|
||||
}
|
||||
}
|
||||
|
||||
success:
|
||||
return
|
||||
}
|
105
vendor/github.com/coreos/go-systemd/dbus/subscription_test.go
generated
vendored
Normal file
105
vendor/github.com/coreos/go-systemd/dbus/subscription_test.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 dbus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestSubscribe exercises the basics of subscription
|
||||
func TestSubscribe(t *testing.T) {
|
||||
conn, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Unsubscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSubscribeUnit exercises the basics of subscription of a particular unit.
|
||||
func TestSubscribeUnit(t *testing.T) {
|
||||
target := "subscribe-events.service"
|
||||
|
||||
conn, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Subscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = conn.Unsubscribe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
evChan, errChan := conn.SubscribeUnits(time.Second)
|
||||
|
||||
setupUnit(target, conn, t)
|
||||
linkUnit(target, conn, t)
|
||||
|
||||
reschan := make(chan string)
|
||||
_, err = conn.StartUnit(target, "replace", reschan)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
job := <-reschan
|
||||
if job != "done" {
|
||||
t.Fatal("Couldn't start", target)
|
||||
}
|
||||
|
||||
timeout := make(chan bool, 1)
|
||||
go func() {
|
||||
time.Sleep(3 * time.Second)
|
||||
close(timeout)
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case changes := <-evChan:
|
||||
tCh, ok := changes[target]
|
||||
|
||||
// Just continue until we see our event.
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if tCh.ActiveState == "active" && tCh.Name == target {
|
||||
goto success
|
||||
}
|
||||
case err = <-errChan:
|
||||
t.Fatal(err)
|
||||
case <-timeout:
|
||||
t.Fatal("Reached timeout")
|
||||
}
|
||||
}
|
||||
|
||||
success:
|
||||
return
|
||||
}
|
58
vendor/github.com/coreos/go-systemd/examples/activation/activation.go
generated
vendored
Normal file
58
vendor/github.com/coreos/go-systemd/examples/activation/activation.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Activation example used by the activation unit tests.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/coreos/go-systemd/activation"
|
||||
)
|
||||
|
||||
func fixListenPid() {
|
||||
if os.Getenv("FIX_LISTEN_PID") != "" {
|
||||
// HACK: real systemd would set LISTEN_PID before exec'ing but
|
||||
// this is too difficult in golang for the purpose of a test.
|
||||
// Do not do this in real code.
|
||||
os.Setenv("LISTEN_PID", fmt.Sprintf("%d", os.Getpid()))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fixListenPid()
|
||||
|
||||
files := activation.Files(false)
|
||||
|
||||
if len(files) == 0 {
|
||||
panic("No files")
|
||||
}
|
||||
|
||||
if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" {
|
||||
panic("Should not unset envs")
|
||||
}
|
||||
|
||||
files = activation.Files(true)
|
||||
|
||||
if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" {
|
||||
panic("Can not unset envs")
|
||||
}
|
||||
|
||||
// Write out the expected strings to the two pipes
|
||||
files[0].Write([]byte("Hello world"))
|
||||
files[1].Write([]byte("Goodbye world"))
|
||||
|
||||
return
|
||||
}
|
19
vendor/github.com/coreos/go-systemd/examples/activation/httpserver/README.md
generated
vendored
Normal file
19
vendor/github.com/coreos/go-systemd/examples/activation/httpserver/README.md
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
## socket activated http server
|
||||
|
||||
This is a simple example of using socket activation with systemd to serve a
|
||||
simple HTTP server on http://127.0.0.1:8076
|
||||
|
||||
To try it out `go get` the httpserver and run it under the systemd-activate helper
|
||||
|
||||
```
|
||||
export GOPATH=`pwd`
|
||||
go get github.com/coreos/go-systemd/examples/activation/httpserver
|
||||
sudo /usr/lib/systemd/systemd-activate -l 127.0.0.1:8076 ./bin/httpserver
|
||||
```
|
||||
|
||||
Then curl the URL and you will notice that it starts up:
|
||||
|
||||
```
|
||||
curl 127.0.0.1:8076
|
||||
hello socket activated world!
|
||||
```
|
11
vendor/github.com/coreos/go-systemd/examples/activation/httpserver/hello.service
generated
vendored
Normal file
11
vendor/github.com/coreos/go-systemd/examples/activation/httpserver/hello.service
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Hello World HTTP
|
||||
Requires=network.target
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/httpserver
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
5
vendor/github.com/coreos/go-systemd/examples/activation/httpserver/hello.socket
generated
vendored
Normal file
5
vendor/github.com/coreos/go-systemd/examples/activation/httpserver/hello.socket
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
[Socket]
|
||||
ListenStream=127.0.0.1:8076
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
40
vendor/github.com/coreos/go-systemd/examples/activation/httpserver/httpserver.go
generated
vendored
Normal file
40
vendor/github.com/coreos/go-systemd/examples/activation/httpserver/httpserver.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/coreos/go-systemd/activation"
|
||||
)
|
||||
|
||||
func HelloServer(w http.ResponseWriter, req *http.Request) {
|
||||
io.WriteString(w, "hello socket activated world!\n")
|
||||
}
|
||||
|
||||
func main() {
|
||||
listeners, err := activation.Listeners(true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if len(listeners) != 1 {
|
||||
panic("Unexpected number of socket activation fds")
|
||||
}
|
||||
|
||||
http.HandleFunc("/", HelloServer)
|
||||
http.Serve(listeners[0], nil)
|
||||
}
|
64
vendor/github.com/coreos/go-systemd/examples/activation/listen.go
generated
vendored
Normal file
64
vendor/github.com/coreos/go-systemd/examples/activation/listen.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Activation example used by the activation unit tests.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/coreos/go-systemd/activation"
|
||||
)
|
||||
|
||||
func fixListenPid() {
|
||||
if os.Getenv("FIX_LISTEN_PID") != "" {
|
||||
// HACK: real systemd would set LISTEN_PID before exec'ing but
|
||||
// this is too difficult in golang for the purpose of a test.
|
||||
// Do not do this in real code.
|
||||
os.Setenv("LISTEN_PID", fmt.Sprintf("%d", os.Getpid()))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fixListenPid()
|
||||
|
||||
listeners, _ := activation.Listeners(false)
|
||||
|
||||
if len(listeners) == 0 {
|
||||
panic("No listeners")
|
||||
}
|
||||
|
||||
if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" {
|
||||
panic("Should not unset envs")
|
||||
}
|
||||
|
||||
listeners, err := activation.Listeners(true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" {
|
||||
panic("Can not unset envs")
|
||||
}
|
||||
|
||||
c0, _ := listeners[0].Accept()
|
||||
c1, _ := listeners[1].Accept()
|
||||
|
||||
// Write out the expected strings to the two pipes
|
||||
c0.Write([]byte("Hello world"))
|
||||
c1.Write([]byte("Goodbye world"))
|
||||
|
||||
return
|
||||
}
|
86
vendor/github.com/coreos/go-systemd/examples/activation/udpconn.go
generated
vendored
Normal file
86
vendor/github.com/coreos/go-systemd/examples/activation/udpconn.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Activation example used by the activation unit tests.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/coreos/go-systemd/activation"
|
||||
)
|
||||
|
||||
func fixListenPid() {
|
||||
if os.Getenv("FIX_LISTEN_PID") != "" {
|
||||
// HACK: real systemd would set LISTEN_PID before exec'ing but
|
||||
// this is too difficult in golang for the purpose of a test.
|
||||
// Do not do this in real code.
|
||||
os.Setenv("LISTEN_PID", fmt.Sprintf("%d", os.Getpid()))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fixListenPid()
|
||||
|
||||
pc, _ := activation.PacketConns(false)
|
||||
|
||||
if len(pc) == 0 {
|
||||
panic("No packetConns")
|
||||
}
|
||||
|
||||
if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" {
|
||||
panic("Should not unset envs")
|
||||
}
|
||||
|
||||
pc, err := activation.PacketConns(true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" {
|
||||
panic("Can not unset envs")
|
||||
}
|
||||
|
||||
udp1, ok := pc[0].(*net.UDPConn)
|
||||
if !ok {
|
||||
panic("packetConn 1 not UDP")
|
||||
}
|
||||
udp2, ok := pc[1].(*net.UDPConn)
|
||||
if !ok {
|
||||
panic("packetConn 2 not UDP")
|
||||
}
|
||||
|
||||
_, addr1, err := udp1.ReadFromUDP(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, addr2, err := udp2.ReadFromUDP(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Write out the expected strings to the two pipes
|
||||
_, err = udp1.WriteToUDP([]byte("Hello world"), addr1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = udp2.WriteToUDP([]byte("Goodbye world"), addr2)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
5
vendor/github.com/coreos/go-systemd/fixtures/enable-disable.service
generated
vendored
Normal file
5
vendor/github.com/coreos/go-systemd/fixtures/enable-disable.service
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
[Unit]
|
||||
Description=enable disable test
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sleep 400
|
5
vendor/github.com/coreos/go-systemd/fixtures/start-stop.service
generated
vendored
Normal file
5
vendor/github.com/coreos/go-systemd/fixtures/start-stop.service
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
[Unit]
|
||||
Description=start stop test
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sleep 400
|
5
vendor/github.com/coreos/go-systemd/fixtures/subscribe-events-set.service
generated
vendored
Normal file
5
vendor/github.com/coreos/go-systemd/fixtures/subscribe-events-set.service
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
[Unit]
|
||||
Description=start stop test
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sleep 400
|
5
vendor/github.com/coreos/go-systemd/fixtures/subscribe-events.service
generated
vendored
Normal file
5
vendor/github.com/coreos/go-systemd/fixtures/subscribe-events.service
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
[Unit]
|
||||
Description=start stop test
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/sleep 400
|
108
vendor/github.com/coreos/go-systemd/login1/dbus.go
generated
vendored
Normal file
108
vendor/github.com/coreos/go-systemd/login1/dbus.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Integration with the systemd logind API. See http://www.freedesktop.org/wiki/Software/systemd/logind/
|
||||
package login1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
const (
|
||||
dbusInterface = "org.freedesktop.login1.Manager"
|
||||
dbusPath = "/org/freedesktop/login1"
|
||||
)
|
||||
|
||||
// Conn is a connection to systemds dbus endpoint.
|
||||
type Conn struct {
|
||||
conn *dbus.Conn
|
||||
object dbus.BusObject
|
||||
}
|
||||
|
||||
// New() establishes a connection to the system bus and authenticates.
|
||||
func New() (*Conn, error) {
|
||||
c := new(Conn)
|
||||
|
||||
if err := c.initConnection(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Conn) initConnection() error {
|
||||
var err error
|
||||
c.conn, err = dbus.SystemBusPrivate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Only use EXTERNAL method, and hardcode the uid (not username)
|
||||
// to avoid a username lookup (which requires a dynamically linked
|
||||
// libc)
|
||||
methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}
|
||||
|
||||
err = c.conn.Auth(methods)
|
||||
if err != nil {
|
||||
c.conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.conn.Hello()
|
||||
if err != nil {
|
||||
c.conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
c.object = c.conn.Object("org.freedesktop.login1", dbus.ObjectPath(dbusPath))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reboot asks logind for a reboot optionally asking for auth.
|
||||
func (c *Conn) Reboot(askForAuth bool) {
|
||||
c.object.Call(dbusInterface+".Reboot", 0, askForAuth)
|
||||
}
|
||||
|
||||
// Inhibit takes inhibition lock in logind.
|
||||
func (c *Conn) Inhibit(what, who, why, mode string) (*os.File, error) {
|
||||
var fd dbus.UnixFD
|
||||
|
||||
err := c.object.Call(dbusInterface+".Inhibit", 0, what, who, why, mode).Store(&fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return os.NewFile(uintptr(fd), "inhibit"), nil
|
||||
}
|
||||
|
||||
// Subscribe to signals on the logind dbus
|
||||
func (c *Conn) Subscribe(members ...string) chan *dbus.Signal {
|
||||
for _, member := range members {
|
||||
c.conn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
|
||||
fmt.Sprintf("type='signal',interface='org.freedesktop.login1.Manager',member='%s'", member))
|
||||
}
|
||||
ch := make(chan *dbus.Signal, 10)
|
||||
c.conn.Signal(ch)
|
||||
return ch
|
||||
}
|
||||
|
||||
// PowerOff asks logind for a power off optionally asking for auth.
|
||||
func (c *Conn) PowerOff(askForAuth bool) {
|
||||
c.object.Call(dbusInterface+".PowerOff", 0, askForAuth)
|
||||
}
|
28
vendor/github.com/coreos/go-systemd/login1/dbus_test.go
generated
vendored
Normal file
28
vendor/github.com/coreos/go-systemd/login1/dbus_test.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 login1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestNew ensures that New() works without errors.
|
||||
func TestNew(t *testing.T) {
|
||||
_, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
81
vendor/github.com/coreos/go-systemd/machine1/dbus.go
generated
vendored
Normal file
81
vendor/github.com/coreos/go-systemd/machine1/dbus.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright 2015 CoreOS Inc.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// Integration with the systemd machined API. See http://www.freedesktop.org/wiki/Software/systemd/machined/
|
||||
package machine1
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/godbus/dbus"
|
||||
)
|
||||
|
||||
const (
|
||||
dbusInterface = "org.freedesktop.machine1.Manager"
|
||||
dbusPath = "/org/freedesktop/machine1"
|
||||
)
|
||||
|
||||
// Conn is a connection to systemds dbus endpoint.
|
||||
type Conn struct {
|
||||
conn *dbus.Conn
|
||||
object dbus.BusObject
|
||||
}
|
||||
|
||||
// New() establishes a connection to the system bus and authenticates.
|
||||
func New() (*Conn, error) {
|
||||
c := new(Conn)
|
||||
|
||||
if err := c.initConnection(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Conn) initConnection() error {
|
||||
var err error
|
||||
c.conn, err = dbus.SystemBusPrivate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Only use EXTERNAL method, and hardcode the uid (not username)
|
||||
// to avoid a username lookup (which requires a dynamically linked
|
||||
// libc)
|
||||
methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))}
|
||||
|
||||
err = c.conn.Auth(methods)
|
||||
if err != nil {
|
||||
c.conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.conn.Hello()
|
||||
if err != nil {
|
||||
c.conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
c.object = c.conn.Object("org.freedesktop.machine1", dbus.ObjectPath(dbusPath))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegisterMachine registers the container with the systemd-machined
|
||||
func (c *Conn) RegisterMachine(name string, id []byte, service string, class string, pid int, root_directory string) error {
|
||||
return c.object.Call(dbusInterface+".RegisterMachine", 0, name, id, service, class, uint32(pid), root_directory).Err
|
||||
}
|
30
vendor/github.com/coreos/go-systemd/machine1/dbus_test.go
generated
vendored
Normal file
30
vendor/github.com/coreos/go-systemd/machine1/dbus_test.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright 2015 CoreOS Inc.
|
||||
|
||||
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 machine1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestNew ensures that New() works without errors.
|
||||
func TestNew(t *testing.T) {
|
||||
_, err := New()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
356
vendor/github.com/coreos/go-systemd/sdjournal/journal.go
generated
vendored
Normal file
356
vendor/github.com/coreos/go-systemd/sdjournal/journal.go
generated
vendored
Normal file
@ -0,0 +1,356 @@
|
||||
// Copyright 2015 RedHat, Inc.
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 sdjournal provides a low-level Go interface to the
|
||||
// systemd journal wrapped around the sd-journal C API.
|
||||
//
|
||||
// All public read methods map closely to the sd-journal API functions. See the
|
||||
// sd-journal.h documentation[1] for information about each function.
|
||||
//
|
||||
// To write to the journal, see the pure-Go "journal" package
|
||||
//
|
||||
// [1] http://www.freedesktop.org/software/systemd/man/sd-journal.html
|
||||
package sdjournal
|
||||
|
||||
/*
|
||||
#cgo pkg-config: libsystemd
|
||||
#include <systemd/sd-journal.h>
|
||||
#include <stdlib.h>
|
||||
#include <syslog.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Journal entry field strings which correspond to:
|
||||
// http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html
|
||||
const (
|
||||
SD_JOURNAL_FIELD_SYSTEMD_UNIT = "_SYSTEMD_UNIT"
|
||||
SD_JOURNAL_FIELD_MESSAGE = "MESSAGE"
|
||||
SD_JOURNAL_FIELD_PID = "_PID"
|
||||
SD_JOURNAL_FIELD_UID = "_UID"
|
||||
SD_JOURNAL_FIELD_GID = "_GID"
|
||||
SD_JOURNAL_FIELD_HOSTNAME = "_HOSTNAME"
|
||||
SD_JOURNAL_FIELD_MACHINE_ID = "_MACHINE_ID"
|
||||
)
|
||||
|
||||
// Journal event constants
|
||||
const (
|
||||
SD_JOURNAL_NOP = int(C.SD_JOURNAL_NOP)
|
||||
SD_JOURNAL_APPEND = int(C.SD_JOURNAL_APPEND)
|
||||
SD_JOURNAL_INVALIDATE = int(C.SD_JOURNAL_INVALIDATE)
|
||||
)
|
||||
|
||||
const (
|
||||
// IndefiniteWait is a sentinel value that can be passed to
|
||||
// sdjournal.Wait() to signal an indefinite wait for new journal
|
||||
// events. It is implemented as the maximum value for a time.Duration:
|
||||
// https://github.com/golang/go/blob/e4dcf5c8c22d98ac9eac7b9b226596229624cb1d/src/time/time.go#L434
|
||||
IndefiniteWait time.Duration = 1<<63 - 1
|
||||
)
|
||||
|
||||
// Journal is a Go wrapper of an sd_journal structure.
|
||||
type Journal struct {
|
||||
cjournal *C.sd_journal
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// Match is a convenience wrapper to describe filters supplied to AddMatch.
|
||||
type Match struct {
|
||||
Field string
|
||||
Value string
|
||||
}
|
||||
|
||||
// String returns a string representation of a Match suitable for use with AddMatch.
|
||||
func (m *Match) String() string {
|
||||
return m.Field + "=" + m.Value
|
||||
}
|
||||
|
||||
// NewJournal returns a new Journal instance pointing to the local journal
|
||||
func NewJournal() (*Journal, error) {
|
||||
j := &Journal{}
|
||||
r := C.sd_journal_open(&j.cjournal, C.SD_JOURNAL_LOCAL_ONLY)
|
||||
|
||||
if r < 0 {
|
||||
return nil, fmt.Errorf("failed to open journal: %d", r)
|
||||
}
|
||||
|
||||
return j, nil
|
||||
}
|
||||
|
||||
// NewJournalFromDir returns a new Journal instance pointing to a journal residing
|
||||
// in a given directory. The supplied path may be relative or absolute; if
|
||||
// relative, it will be converted to an absolute path before being opened.
|
||||
func NewJournalFromDir(path string) (*Journal, error) {
|
||||
path, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := C.CString(path)
|
||||
defer C.free(unsafe.Pointer(p))
|
||||
|
||||
j := &Journal{}
|
||||
r := C.sd_journal_open_directory(&j.cjournal, p, 0)
|
||||
if r < 0 {
|
||||
return nil, fmt.Errorf("failed to open journal in directory %q: %d", path, r)
|
||||
}
|
||||
|
||||
return j, nil
|
||||
}
|
||||
|
||||
// Close closes a journal opened with NewJournal.
|
||||
func (j *Journal) Close() error {
|
||||
j.mu.Lock()
|
||||
C.sd_journal_close(j.cjournal)
|
||||
j.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddMatch adds a match by which to filter the entries of the journal.
|
||||
func (j *Journal) AddMatch(match string) error {
|
||||
m := C.CString(match)
|
||||
defer C.free(unsafe.Pointer(m))
|
||||
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_add_match(j.cjournal, unsafe.Pointer(m), C.size_t(len(match)))
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return fmt.Errorf("failed to add match: %d", r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddDisjunction inserts a logical OR in the match list.
|
||||
func (j *Journal) AddDisjunction() error {
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_add_disjunction(j.cjournal)
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return fmt.Errorf("failed to add a disjunction in the match list: %d", r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddConjunction inserts a logical AND in the match list.
|
||||
func (j *Journal) AddConjunction() error {
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_add_conjunction(j.cjournal)
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return fmt.Errorf("failed to add a conjunction in the match list: %d", r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FlushMatches flushes all matches, disjunctions and conjunctions.
|
||||
func (j *Journal) FlushMatches() {
|
||||
j.mu.Lock()
|
||||
C.sd_journal_flush_matches(j.cjournal)
|
||||
j.mu.Unlock()
|
||||
}
|
||||
|
||||
// Next advances the read pointer into the journal by one entry.
|
||||
func (j *Journal) Next() (int, error) {
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_next(j.cjournal)
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return int(r), fmt.Errorf("failed to iterate journal: %d", r)
|
||||
}
|
||||
|
||||
return int(r), nil
|
||||
}
|
||||
|
||||
// NextSkip advances the read pointer by multiple entries at once,
|
||||
// as specified by the skip parameter.
|
||||
func (j *Journal) NextSkip(skip uint64) (uint64, error) {
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_next_skip(j.cjournal, C.uint64_t(skip))
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return uint64(r), fmt.Errorf("failed to iterate journal: %d", r)
|
||||
}
|
||||
|
||||
return uint64(r), nil
|
||||
}
|
||||
|
||||
// Previous sets the read pointer into the journal back by one entry.
|
||||
func (j *Journal) Previous() (uint64, error) {
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_previous(j.cjournal)
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return uint64(r), fmt.Errorf("failed to iterate journal: %d", r)
|
||||
}
|
||||
|
||||
return uint64(r), nil
|
||||
}
|
||||
|
||||
// PreviousSkip sets back the read pointer by multiple entries at once,
|
||||
// as specified by the skip parameter.
|
||||
func (j *Journal) PreviousSkip(skip uint64) (uint64, error) {
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_previous_skip(j.cjournal, C.uint64_t(skip))
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return uint64(r), fmt.Errorf("failed to iterate journal: %d", r)
|
||||
}
|
||||
|
||||
return uint64(r), nil
|
||||
}
|
||||
|
||||
// GetData gets the data object associated with a specific field from the
|
||||
// current journal entry.
|
||||
func (j *Journal) GetData(field string) (string, error) {
|
||||
f := C.CString(field)
|
||||
defer C.free(unsafe.Pointer(f))
|
||||
|
||||
var d unsafe.Pointer
|
||||
var l C.size_t
|
||||
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_get_data(j.cjournal, f, &d, &l)
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return "", fmt.Errorf("failed to read message: %d", r)
|
||||
}
|
||||
|
||||
msg := C.GoStringN((*C.char)(d), C.int(l))
|
||||
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
// GetDataValue gets the data object associated with a specific field from the
|
||||
// current journal entry, returning only the value of the object.
|
||||
func (j *Journal) GetDataValue(field string) (string, error) {
|
||||
val, err := j.GetData(field)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.SplitN(val, "=", 2)[1], nil
|
||||
}
|
||||
|
||||
// SetDataThresold sets the data field size threshold for data returned by
|
||||
// GetData. To retrieve the complete data fields this threshold should be
|
||||
// turned off by setting it to 0, so that the library always returns the
|
||||
// complete data objects.
|
||||
func (j *Journal) SetDataThreshold(threshold uint64) error {
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_set_data_threshold(j.cjournal, C.size_t(threshold))
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return fmt.Errorf("failed to set data threshold: %d", r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRealtimeUsec gets the realtime (wallclock) timestamp of the current
|
||||
// journal entry.
|
||||
func (j *Journal) GetRealtimeUsec() (uint64, error) {
|
||||
var usec C.uint64_t
|
||||
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_get_realtime_usec(j.cjournal, &usec)
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return 0, fmt.Errorf("error getting timestamp for entry: %d", r)
|
||||
}
|
||||
|
||||
return uint64(usec), nil
|
||||
}
|
||||
|
||||
// SeekTail may be used to seek to the end of the journal, i.e. the most recent
|
||||
// available entry.
|
||||
func (j *Journal) SeekTail() error {
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_seek_tail(j.cjournal)
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return fmt.Errorf("failed to seek to tail of journal: %d", r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SeekRealtimeUsec seeks to the entry with the specified realtime (wallclock)
|
||||
// timestamp, i.e. CLOCK_REALTIME.
|
||||
func (j *Journal) SeekRealtimeUsec(usec uint64) error {
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_seek_realtime_usec(j.cjournal, C.uint64_t(usec))
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return fmt.Errorf("failed to seek to %d: %d", usec, r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wait will synchronously wait until the journal gets changed. The maximum time
|
||||
// this call sleeps may be controlled with the timeout parameter. If
|
||||
// sdjournal.IndefiniteWait is passed as the timeout parameter, Wait will
|
||||
// wait indefinitely for a journal change.
|
||||
func (j *Journal) Wait(timeout time.Duration) int {
|
||||
var to uint64
|
||||
if timeout == IndefiniteWait {
|
||||
// sd_journal_wait(3) calls for a (uint64_t) -1 to be passed to signify
|
||||
// indefinite wait, but using a -1 overflows our C.uint64_t, so we use an
|
||||
// equivalent hex value.
|
||||
to = 0xffffffffffffffff
|
||||
} else {
|
||||
to = uint64(time.Now().Add(timeout).Unix() / 1000)
|
||||
}
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_wait(j.cjournal, C.uint64_t(to))
|
||||
j.mu.Unlock()
|
||||
|
||||
return int(r)
|
||||
}
|
||||
|
||||
// GetUsage returns the journal disk space usage, in bytes.
|
||||
func (j *Journal) GetUsage() (uint64, error) {
|
||||
var out C.uint64_t
|
||||
j.mu.Lock()
|
||||
r := C.sd_journal_get_usage(j.cjournal, &out)
|
||||
j.mu.Unlock()
|
||||
|
||||
if r < 0 {
|
||||
return 0, fmt.Errorf("failed to get journal disk space usage: %d", r)
|
||||
}
|
||||
|
||||
return uint64(out), nil
|
||||
}
|
90
vendor/github.com/coreos/go-systemd/sdjournal/journal_test.go
generated
vendored
Normal file
90
vendor/github.com/coreos/go-systemd/sdjournal/journal_test.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright 2015 RedHat, Inc.
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 sdjournal
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-systemd/journal"
|
||||
)
|
||||
|
||||
func TestJournalFollow(t *testing.T) {
|
||||
r, err := NewJournalReader(JournalReaderConfig{
|
||||
Since: time.Duration(-15) * time.Second,
|
||||
Matches: []Match{
|
||||
{
|
||||
Field: SD_JOURNAL_FIELD_SYSTEMD_UNIT,
|
||||
Value: "NetworkManager.service",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening journal: %s", err)
|
||||
}
|
||||
|
||||
if r == nil {
|
||||
t.Fatal("Got a nil reader")
|
||||
}
|
||||
|
||||
defer r.Close()
|
||||
|
||||
// start writing some test entries
|
||||
done := make(chan struct{}, 1)
|
||||
defer close(done)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
if err = journal.Print(journal.PriInfo, "test message %s", time.Now()); err != nil {
|
||||
t.Fatalf("Error writing to journal: %s", err)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// and follow the reader synchronously
|
||||
timeout := time.Duration(5) * time.Second
|
||||
if err = r.Follow(time.After(timeout), os.Stdout); err != ErrExpired {
|
||||
t.Fatalf("Error during follow: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJournalGetUsage(t *testing.T) {
|
||||
j, err := NewJournal()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error opening journal: %s", err)
|
||||
}
|
||||
|
||||
if j == nil {
|
||||
t.Fatal("Got a nil journal")
|
||||
}
|
||||
|
||||
defer j.Close()
|
||||
|
||||
_, err = j.GetUsage()
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Error getting journal size: %s", err)
|
||||
}
|
||||
}
|
201
vendor/github.com/coreos/go-systemd/sdjournal/read.go
generated
vendored
Normal file
201
vendor/github.com/coreos/go-systemd/sdjournal/read.go
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
// Copyright 2015 RedHat, Inc.
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 sdjournal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrExpired = errors.New("Timeout expired")
|
||||
)
|
||||
|
||||
// JournalReaderConfig represents options to drive the behavior of a JournalReader.
|
||||
type JournalReaderConfig struct {
|
||||
// The Since and NumFromTail options are mutually exclusive and determine
|
||||
// where the reading begins within the journal.
|
||||
Since time.Duration // start relative to a Duration from now
|
||||
NumFromTail uint64 // start relative to the tail
|
||||
|
||||
// Show only journal entries whose fields match the supplied values. If
|
||||
// the array is empty, entries will not be filtered.
|
||||
Matches []Match
|
||||
}
|
||||
|
||||
// JournalReader is an io.ReadCloser which provides a simple interface for iterating through the
|
||||
// systemd journal.
|
||||
type JournalReader struct {
|
||||
journal *Journal
|
||||
}
|
||||
|
||||
// NewJournalReader creates a new JournalReader with configuration options that are similar to the
|
||||
// systemd journalctl tool's iteration and filtering features.
|
||||
func NewJournalReader(config JournalReaderConfig) (*JournalReader, error) {
|
||||
r := &JournalReader{}
|
||||
|
||||
var err error
|
||||
// Open the journal
|
||||
if r.journal, err = NewJournal(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Add any supplied matches
|
||||
for _, m := range config.Matches {
|
||||
r.journal.AddMatch(m.String())
|
||||
}
|
||||
|
||||
// Set the start position based on options
|
||||
if config.Since != 0 {
|
||||
// Start based on a relative time
|
||||
start := time.Now().Add(config.Since)
|
||||
if err := r.journal.SeekRealtimeUsec(uint64(start.UnixNano() / 1000)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if config.NumFromTail != 0 {
|
||||
// Start based on a number of lines before the tail
|
||||
if err := r.journal.SeekTail(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Move the read pointer into position near the tail. Go one further than
|
||||
// the option so that the initial cursor advancement positions us at the
|
||||
// correct starting point.
|
||||
if _, err := r.journal.PreviousSkip(config.NumFromTail + 1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r *JournalReader) Read(b []byte) (int, error) {
|
||||
var err error
|
||||
var c int
|
||||
|
||||
// Advance the journal cursor
|
||||
c, err = r.journal.Next()
|
||||
|
||||
// An unexpected error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// EOF detection
|
||||
if c == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
// Build a message
|
||||
var msg string
|
||||
msg, err = r.buildMessage()
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Copy and return the message
|
||||
copy(b, []byte(msg))
|
||||
|
||||
return len(msg), nil
|
||||
}
|
||||
|
||||
func (r *JournalReader) Close() error {
|
||||
return r.journal.Close()
|
||||
}
|
||||
|
||||
// Follow synchronously follows the JournalReader, writing each new journal entry to writer. The
|
||||
// follow will continue until a single time.Time is received on the until channel.
|
||||
func (r *JournalReader) Follow(until <-chan time.Time, writer io.Writer) (err error) {
|
||||
|
||||
// Process journal entries and events. Entries are flushed until the tail or
|
||||
// timeout is reached, and then we wait for new events or the timeout.
|
||||
process:
|
||||
for {
|
||||
var msg = make([]byte, 64*1<<(10))
|
||||
|
||||
c, err := r.Read(msg)
|
||||
if err != nil && err != io.EOF {
|
||||
break process
|
||||
}
|
||||
|
||||
select {
|
||||
case <-until:
|
||||
return ErrExpired
|
||||
default:
|
||||
if c > 0 {
|
||||
writer.Write(msg)
|
||||
continue process
|
||||
}
|
||||
}
|
||||
|
||||
// We're at the tail, so wait for new events or time out.
|
||||
// Holds journal events to process. Tightly bounded for now unless there's a
|
||||
// reason to unblock the journal watch routine more quickly.
|
||||
events := make(chan int, 1)
|
||||
pollDone := make(chan bool, 1)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-pollDone:
|
||||
return
|
||||
default:
|
||||
events <- r.journal.Wait(time.Duration(1) * time.Second)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-until:
|
||||
pollDone <- true
|
||||
return ErrExpired
|
||||
case e := <-events:
|
||||
pollDone <- true
|
||||
switch e {
|
||||
case SD_JOURNAL_NOP, SD_JOURNAL_APPEND, SD_JOURNAL_INVALIDATE:
|
||||
// TODO: need to account for any of these?
|
||||
default:
|
||||
log.Printf("Received unknown event: %d\n", e)
|
||||
}
|
||||
continue process
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// buildMessage returns a string representing the current journal entry in a simple format which
|
||||
// includes the entry timestamp and MESSAGE field.
|
||||
func (r *JournalReader) buildMessage() (string, error) {
|
||||
var msg string
|
||||
var usec uint64
|
||||
var err error
|
||||
|
||||
if msg, err = r.journal.GetData("MESSAGE"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if usec, err = r.journal.GetRealtimeUsec(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
timestamp := time.Unix(0, int64(usec)*int64(time.Microsecond))
|
||||
|
||||
return fmt.Sprintf("%s %s\n", timestamp, msg), nil
|
||||
}
|
76
vendor/github.com/coreos/go-systemd/test
generated
vendored
Executable file
76
vendor/github.com/coreos/go-systemd/test
generated
vendored
Executable file
@ -0,0 +1,76 @@
|
||||
#!/bin/bash -e
|
||||
#
|
||||
# Run all tests
|
||||
# ./test
|
||||
# ./test -v
|
||||
#
|
||||
# Run tests for one package
|
||||
# PKG=./foo ./test
|
||||
# PKG=bar ./test
|
||||
#
|
||||
|
||||
# Invoke ./cover for HTML output
|
||||
COVER=${COVER:-"-cover"}
|
||||
|
||||
PROJ="go-systemd"
|
||||
ORG_PATH="github.com/coreos"
|
||||
REPO_PATH="${ORG_PATH}/${PROJ}"
|
||||
|
||||
# As a convenience, set up a self-contained GOPATH if none set
|
||||
if [ -z "$GOPATH" ]; then
|
||||
if [ ! -h gopath/src/${REPO_PATH} ]; then
|
||||
mkdir -p gopath/src/${ORG_PATH}
|
||||
ln -s ../../../.. gopath/src/${REPO_PATH} || exit 255
|
||||
fi
|
||||
export GOPATH=${PWD}/gopath
|
||||
go get -u github.com/godbus/dbus
|
||||
fi
|
||||
|
||||
TESTABLE="activation journal login1 machine1 unit"
|
||||
FORMATTABLE="$TESTABLE sdjournal dbus"
|
||||
if [ -e "/run/systemd/system/" ]; then
|
||||
TESTABLE="${TESTABLE} sdjournal"
|
||||
if [ "$EUID" == "0" ]; then
|
||||
# testing actual systemd behaviour requires root
|
||||
TESTABLE="${TESTABLE} dbus"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# user has not provided PKG override
|
||||
if [ -z "$PKG" ]; then
|
||||
TEST=$TESTABLE
|
||||
FMT=$FORMATTABLE
|
||||
|
||||
# user has provided PKG override
|
||||
else
|
||||
# strip out slashes and dots from PKG=./foo/
|
||||
TEST=${PKG//\//}
|
||||
TEST=${TEST//./}
|
||||
|
||||
# only run gofmt on packages provided by user
|
||||
FMT="$TEST"
|
||||
fi
|
||||
|
||||
# split TEST into an array and prepend REPO_PATH to each local package
|
||||
split=(${TEST// / })
|
||||
TEST=${split[@]/#/${REPO_PATH}/}
|
||||
|
||||
echo "Running tests..."
|
||||
go test ${COVER} $@ ${TEST}
|
||||
|
||||
echo "Checking gofmt..."
|
||||
fmtRes=$(gofmt -l $FMT)
|
||||
if [ -n "${fmtRes}" ]; then
|
||||
echo -e "gofmt checking failed:\n${fmtRes}"
|
||||
exit 255
|
||||
fi
|
||||
|
||||
echo "Checking govet..."
|
||||
vetRes=$(go vet $TEST)
|
||||
if [ -n "${vetRes}" ]; then
|
||||
echo -e "govet checking failed:\n${vetRes}"
|
||||
exit 255
|
||||
fi
|
||||
|
||||
echo "Success"
|
276
vendor/github.com/coreos/go-systemd/unit/deserialize.go
generated
vendored
Normal file
276
vendor/github.com/coreos/go-systemd/unit/deserialize.go
generated
vendored
Normal file
@ -0,0 +1,276 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 unit
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const (
|
||||
// SYSTEMD_LINE_MAX mimics the maximum line length that systemd can use.
|
||||
// On typical systemd platforms (i.e. modern Linux), this will most
|
||||
// commonly be 2048, so let's use that as a sanity check.
|
||||
// Technically, we should probably pull this at runtime:
|
||||
// SYSTEMD_LINE_MAX = int(C.sysconf(C.__SC_LINE_MAX))
|
||||
// but this would introduce an (unfortunate) dependency on cgo
|
||||
SYSTEMD_LINE_MAX = 2048
|
||||
|
||||
// characters that systemd considers indicate a newline
|
||||
SYSTEMD_NEWLINE = "\r\n"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLineTooLong = fmt.Errorf("line too long (max %d bytes)", SYSTEMD_LINE_MAX)
|
||||
)
|
||||
|
||||
// Deserialize parses a systemd unit file into a list of UnitOption objects.
|
||||
func Deserialize(f io.Reader) (opts []*UnitOption, err error) {
|
||||
lexer, optchan, errchan := newLexer(f)
|
||||
go lexer.lex()
|
||||
|
||||
for opt := range optchan {
|
||||
opts = append(opts, &(*opt))
|
||||
}
|
||||
|
||||
err = <-errchan
|
||||
return opts, err
|
||||
}
|
||||
|
||||
func newLexer(f io.Reader) (*lexer, <-chan *UnitOption, <-chan error) {
|
||||
optchan := make(chan *UnitOption)
|
||||
errchan := make(chan error, 1)
|
||||
buf := bufio.NewReader(f)
|
||||
|
||||
return &lexer{buf, optchan, errchan, ""}, optchan, errchan
|
||||
}
|
||||
|
||||
type lexer struct {
|
||||
buf *bufio.Reader
|
||||
optchan chan *UnitOption
|
||||
errchan chan error
|
||||
section string
|
||||
}
|
||||
|
||||
func (l *lexer) lex() {
|
||||
var err error
|
||||
defer func() {
|
||||
close(l.optchan)
|
||||
close(l.errchan)
|
||||
}()
|
||||
next := l.lexNextSection
|
||||
for next != nil {
|
||||
if l.buf.Buffered() >= SYSTEMD_LINE_MAX {
|
||||
// systemd truncates lines longer than LINE_MAX
|
||||
// https://bugs.freedesktop.org/show_bug.cgi?id=85308
|
||||
// Rather than allowing this to pass silently, let's
|
||||
// explicitly gate people from encountering this
|
||||
line, err := l.buf.Peek(SYSTEMD_LINE_MAX)
|
||||
if err != nil {
|
||||
l.errchan <- err
|
||||
return
|
||||
}
|
||||
if bytes.IndexAny(line, SYSTEMD_NEWLINE) == -1 {
|
||||
l.errchan <- ErrLineTooLong
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
next, err = next()
|
||||
if err != nil {
|
||||
l.errchan <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type lexStep func() (lexStep, error)
|
||||
|
||||
func (l *lexer) lexSectionName() (lexStep, error) {
|
||||
sec, err := l.buf.ReadBytes(']')
|
||||
if err != nil {
|
||||
return nil, errors.New("unable to find end of section")
|
||||
}
|
||||
|
||||
return l.lexSectionSuffixFunc(string(sec[:len(sec)-1])), nil
|
||||
}
|
||||
|
||||
func (l *lexer) lexSectionSuffixFunc(section string) lexStep {
|
||||
return func() (lexStep, error) {
|
||||
garbage, _, err := l.toEOL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
garbage = bytes.TrimSpace(garbage)
|
||||
if len(garbage) > 0 {
|
||||
return nil, fmt.Errorf("found garbage after section name %s: %v", l.section, garbage)
|
||||
}
|
||||
|
||||
return l.lexNextSectionOrOptionFunc(section), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lexer) ignoreLineFunc(next lexStep) lexStep {
|
||||
return func() (lexStep, error) {
|
||||
for {
|
||||
line, _, err := l.toEOL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
line = bytes.TrimSuffix(line, []byte{' '})
|
||||
|
||||
// lack of continuation means this line has been exhausted
|
||||
if !bytes.HasSuffix(line, []byte{'\\'}) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// reached end of buffer, safe to exit
|
||||
return next, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lexer) lexNextSection() (lexStep, error) {
|
||||
r, _, err := l.buf.ReadRune()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r == '[' {
|
||||
return l.lexSectionName, nil
|
||||
} else if isComment(r) {
|
||||
return l.ignoreLineFunc(l.lexNextSection), nil
|
||||
}
|
||||
|
||||
return l.lexNextSection, nil
|
||||
}
|
||||
|
||||
func (l *lexer) lexNextSectionOrOptionFunc(section string) lexStep {
|
||||
return func() (lexStep, error) {
|
||||
r, _, err := l.buf.ReadRune()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if unicode.IsSpace(r) {
|
||||
return l.lexNextSectionOrOptionFunc(section), nil
|
||||
} else if r == '[' {
|
||||
return l.lexSectionName, nil
|
||||
} else if isComment(r) {
|
||||
return l.ignoreLineFunc(l.lexNextSectionOrOptionFunc(section)), nil
|
||||
}
|
||||
|
||||
l.buf.UnreadRune()
|
||||
return l.lexOptionNameFunc(section), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lexer) lexOptionNameFunc(section string) lexStep {
|
||||
return func() (lexStep, error) {
|
||||
var partial bytes.Buffer
|
||||
for {
|
||||
r, _, err := l.buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r == '\n' || r == '\r' {
|
||||
return nil, errors.New("unexpected newline encountered while parsing option name")
|
||||
}
|
||||
|
||||
if r == '=' {
|
||||
break
|
||||
}
|
||||
|
||||
partial.WriteRune(r)
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(partial.String())
|
||||
return l.lexOptionValueFunc(section, name, bytes.Buffer{}), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lexer) lexOptionValueFunc(section, name string, partial bytes.Buffer) lexStep {
|
||||
return func() (lexStep, error) {
|
||||
for {
|
||||
line, eof, err := l.toEOL()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(bytes.TrimSpace(line)) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
partial.Write(line)
|
||||
|
||||
// lack of continuation means this value has been exhausted
|
||||
idx := bytes.LastIndex(line, []byte{'\\'})
|
||||
if idx == -1 || idx != (len(line)-1) {
|
||||
break
|
||||
}
|
||||
|
||||
if !eof {
|
||||
partial.WriteRune('\n')
|
||||
}
|
||||
|
||||
return l.lexOptionValueFunc(section, name, partial), nil
|
||||
}
|
||||
|
||||
val := partial.String()
|
||||
if strings.HasSuffix(val, "\n") {
|
||||
// A newline was added to the end, so the file didn't end with a backslash.
|
||||
// => Keep the newline
|
||||
val = strings.TrimSpace(val) + "\n"
|
||||
} else {
|
||||
val = strings.TrimSpace(val)
|
||||
}
|
||||
l.optchan <- &UnitOption{Section: section, Name: name, Value: val}
|
||||
|
||||
return l.lexNextSectionOrOptionFunc(section), nil
|
||||
}
|
||||
}
|
||||
|
||||
// toEOL reads until the end-of-line or end-of-file.
|
||||
// Returns (data, EOFfound, error)
|
||||
func (l *lexer) toEOL() ([]byte, bool, error) {
|
||||
line, err := l.buf.ReadBytes('\n')
|
||||
// ignore EOF here since it's roughly equivalent to EOL
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
line = bytes.TrimSuffix(line, []byte{'\r'})
|
||||
line = bytes.TrimSuffix(line, []byte{'\n'})
|
||||
|
||||
return line, err == io.EOF, nil
|
||||
}
|
||||
|
||||
func isComment(r rune) bool {
|
||||
return r == '#' || r == ';'
|
||||
}
|
381
vendor/github.com/coreos/go-systemd/unit/deserialize_test.go
generated
vendored
Normal file
381
vendor/github.com/coreos/go-systemd/unit/deserialize_test.go
generated
vendored
Normal file
@ -0,0 +1,381 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 unit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDeserialize(t *testing.T) {
|
||||
tests := []struct {
|
||||
input []byte
|
||||
output []*UnitOption
|
||||
}{
|
||||
// multiple options underneath a section
|
||||
{
|
||||
[]byte(`[Unit]
|
||||
Description=Foo
|
||||
Description=Bar
|
||||
Requires=baz.service
|
||||
After=baz.service
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Foo"},
|
||||
&UnitOption{"Unit", "Description", "Bar"},
|
||||
&UnitOption{"Unit", "Requires", "baz.service"},
|
||||
&UnitOption{"Unit", "After", "baz.service"},
|
||||
},
|
||||
},
|
||||
|
||||
// multiple sections
|
||||
{
|
||||
[]byte(`[Unit]
|
||||
Description=Foo
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/sleep infinity
|
||||
|
||||
[X-Third-Party]
|
||||
Pants=on
|
||||
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Foo"},
|
||||
&UnitOption{"Service", "ExecStart", "/usr/bin/sleep infinity"},
|
||||
&UnitOption{"X-Third-Party", "Pants", "on"},
|
||||
},
|
||||
},
|
||||
|
||||
// multiple sections with no options
|
||||
{
|
||||
[]byte(`[Unit]
|
||||
[Service]
|
||||
[X-Third-Party]
|
||||
`),
|
||||
[]*UnitOption{},
|
||||
},
|
||||
|
||||
// multiple values not special-cased
|
||||
{
|
||||
[]byte(`[Service]
|
||||
Environment= "FOO=BAR" "BAZ=QUX"
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Service", "Environment", "\"FOO=BAR\" \"BAZ=QUX\""},
|
||||
},
|
||||
},
|
||||
|
||||
// line continuations unmodified
|
||||
{
|
||||
[]byte(`[Unit]
|
||||
Description= Unnecessarily wrapped \
|
||||
words here
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", `Unnecessarily wrapped \
|
||||
words here`},
|
||||
},
|
||||
},
|
||||
|
||||
// comments ignored
|
||||
{
|
||||
[]byte(`; comment alpha
|
||||
# comment bravo
|
||||
[Unit]
|
||||
; comment charlie
|
||||
# comment delta
|
||||
#Description=Foo
|
||||
Description=Bar
|
||||
; comment echo
|
||||
# comment foxtrot
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Bar"},
|
||||
},
|
||||
},
|
||||
|
||||
// apparent comment lines inside of line continuations not ignored
|
||||
{
|
||||
[]byte(`[Unit]
|
||||
Description=Bar\
|
||||
# comment alpha
|
||||
|
||||
Description=Bar\
|
||||
# comment bravo \
|
||||
Baz
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Bar\\\n# comment alpha"},
|
||||
&UnitOption{"Unit", "Description", "Bar\\\n# comment bravo \\\nBaz"},
|
||||
},
|
||||
},
|
||||
|
||||
// options outside of sections are ignored
|
||||
{
|
||||
[]byte(`Description=Foo
|
||||
[Unit]
|
||||
Description=Bar
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Bar"},
|
||||
},
|
||||
},
|
||||
|
||||
// garbage outside of sections are ignored
|
||||
{
|
||||
[]byte(`<<<<<<<<
|
||||
[Unit]
|
||||
Description=Bar
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Bar"},
|
||||
},
|
||||
},
|
||||
|
||||
// garbage used as unit option
|
||||
{
|
||||
[]byte(`[Unit]
|
||||
<<<<<<<<=Bar
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "<<<<<<<<", "Bar"},
|
||||
},
|
||||
},
|
||||
|
||||
// option name with spaces are valid
|
||||
{
|
||||
[]byte(`[Unit]
|
||||
Some Thing = Bar
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Some Thing", "Bar"},
|
||||
},
|
||||
},
|
||||
|
||||
// lack of trailing newline doesn't cause problem for non-continued file
|
||||
{
|
||||
[]byte(`[Unit]
|
||||
Description=Bar`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Bar"},
|
||||
},
|
||||
},
|
||||
|
||||
// unit file with continuation but no following line is ok, too
|
||||
{
|
||||
[]byte(`[Unit]
|
||||
Description=Bar \`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Bar \\"},
|
||||
},
|
||||
},
|
||||
|
||||
// Assert utf8 characters are preserved
|
||||
{
|
||||
[]byte(`[©]
|
||||
µ☃=ÇôrèÕ$`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"©", "µ☃", "ÇôrèÕ$"},
|
||||
},
|
||||
},
|
||||
|
||||
// whitespace removed around option name
|
||||
{
|
||||
[]byte(`[Unit]
|
||||
Description =words here
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "words here"},
|
||||
},
|
||||
},
|
||||
|
||||
// whitespace around option value stripped
|
||||
{
|
||||
[]byte(`[Unit]
|
||||
Description= words here `),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "words here"},
|
||||
},
|
||||
},
|
||||
|
||||
// whitespace around option value stripped, regardless of continuation
|
||||
{
|
||||
[]byte(`[Unit]
|
||||
Description= words here \
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "words here \\\n"},
|
||||
},
|
||||
},
|
||||
|
||||
// backslash not considered continuation if followed by text
|
||||
{
|
||||
[]byte(`[Service]
|
||||
ExecStart=/bin/bash -c "while true; do echo \"ping\"; sleep 1; done"
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Service", "ExecStart", `/bin/bash -c "while true; do echo \"ping\"; sleep 1; done"`},
|
||||
},
|
||||
},
|
||||
|
||||
// backslash not considered continuation if followed by whitespace, but still trimmed
|
||||
{
|
||||
[]byte(`[Service]
|
||||
ExecStart=/bin/bash echo poof \ `),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Service", "ExecStart", `/bin/bash echo poof \`},
|
||||
},
|
||||
},
|
||||
// a long unit file line that's just equal to the maximum permitted length
|
||||
{
|
||||
[]byte(`[Service]
|
||||
ExecStart=/bin/bash -c "echo ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Service", "ExecStart", `/bin/bash -c "echo ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."`},
|
||||
},
|
||||
},
|
||||
// the same, but with a trailing newline
|
||||
{
|
||||
[]byte(`[Service]
|
||||
ExecStart=/bin/bash -c "echo ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."
|
||||
Option=value
|
||||
`),
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Service", "ExecStart", `/bin/bash -c "echo ................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."`},
|
||||
&UnitOption{"Service", "Option", "value"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert := func(expect, output []*UnitOption) error {
|
||||
if len(expect) != len(output) {
|
||||
return fmt.Errorf("expected %d items, got %d", len(expect), len(output))
|
||||
}
|
||||
|
||||
for i, _ := range expect {
|
||||
if !reflect.DeepEqual(expect[i], output[i]) {
|
||||
return fmt.Errorf("item %d: expected %v, got %v", i, expect[i], output[i])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
output, err := Deserialize(bytes.NewReader(tt.input))
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error parsing unit: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
err = assert(tt.output, output)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: %v", i, err)
|
||||
t.Log("Expected options:")
|
||||
logUnitOptionSlice(t, tt.output)
|
||||
t.Log("Actual options:")
|
||||
logUnitOptionSlice(t, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeserializeFail(t *testing.T) {
|
||||
tests := [][]byte{
|
||||
// malformed section header
|
||||
[]byte(`[Unit
|
||||
Description=Foo
|
||||
`),
|
||||
|
||||
// garbage following section header
|
||||
[]byte(`[Unit] pants
|
||||
Description=Foo
|
||||
`),
|
||||
|
||||
// option without value
|
||||
[]byte(`[Unit]
|
||||
Description
|
||||
`),
|
||||
|
||||
// garbage inside of section
|
||||
[]byte(`[Unit]
|
||||
<<<<<<
|
||||
Description=Foo
|
||||
`),
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
output, err := Deserialize(bytes.NewReader(tt))
|
||||
if err == nil {
|
||||
t.Errorf("case %d: unexpected nil error", i)
|
||||
t.Log("Output:")
|
||||
logUnitOptionSlice(t, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func logUnitOptionSlice(t *testing.T, opts []*UnitOption) {
|
||||
for idx, opt := range opts {
|
||||
t.Logf("%d: %v", idx, opt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeserializeLineTooLong(t *testing.T) {
|
||||
tests := [][]byte{
|
||||
// section header that's far too long
|
||||
[]byte(`[Seeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeervice]
|
||||
`),
|
||||
// sane-looking unit file with a line just greater than the maximum allowed (currently, 2048)
|
||||
[]byte(`[Service]
|
||||
ExecStart=/bin/bash -c "echo ..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................."
|
||||
`),
|
||||
// sane-looking unit file with option value way too long
|
||||
[]byte(`
|
||||
# test unit file
|
||||
|
||||
[Service]
|
||||
ExecStartPre=-/usr/bin/docker rm %p
|
||||
ExecStartPre=-/usr/bin/docker pull busybox
|
||||
ExecStart=/usr/bin/docker run --rm --name %p --net=host \
|
||||
-e "test=1123t" \
|
||||
-e "test=1123t" \
|
||||
-e "fiz=1123t" \
|
||||
-e "buz=1123t" \
|
||||
-e "FOO=BARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBABARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARRBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBAR"BARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBABARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARRBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBARBAR" \
|
||||
busybox sleep 10
|
||||
ExecStop=-/usr/bin/docker kill %p
|
||||
SyslogIdentifier=busybox
|
||||
Restart=always
|
||||
RestartSec=10s
|
||||
`),
|
||||
// single arbitrary line that's way too long
|
||||
[]byte(`arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 character arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 character arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 character arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 character arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters arbitrary and extraordinarily long line that is far greater than 2048 characters`),
|
||||
// sane-looking unit file with option name way too long
|
||||
[]byte(`[Service]
|
||||
ExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStartExecStart=/bin/true
|
||||
`),
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
output, err := Deserialize(bytes.NewReader(tt))
|
||||
if err != ErrLineTooLong {
|
||||
t.Errorf("case %d: unexpected err: %v", i, err)
|
||||
t.Log("Output:")
|
||||
logUnitOptionSlice(t, output)
|
||||
}
|
||||
}
|
||||
}
|
88
vendor/github.com/coreos/go-systemd/unit/end_to_end_test.go
generated
vendored
Normal file
88
vendor/github.com/coreos/go-systemd/unit/end_to_end_test.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 unit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDeserializeAndReserialize(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
wout string
|
||||
}{
|
||||
{
|
||||
`[Service]
|
||||
ExecStart=/bin/bash -c "while true; do echo \"ping\"; sleep 1; done"
|
||||
`,
|
||||
`[Service]
|
||||
ExecStart=/bin/bash -c "while true; do echo \"ping\"; sleep 1; done"
|
||||
`},
|
||||
{
|
||||
`[Unit]
|
||||
Description= Unnecessarily wrapped \
|
||||
words here`,
|
||||
`[Unit]
|
||||
Description=Unnecessarily wrapped \
|
||||
words here
|
||||
`,
|
||||
},
|
||||
{
|
||||
`[Unit]
|
||||
Description=Demo \
|
||||
|
||||
Requires=docker.service
|
||||
`,
|
||||
`[Unit]
|
||||
Description=Demo \
|
||||
|
||||
Requires=docker.service
|
||||
`,
|
||||
},
|
||||
{
|
||||
`; comment alpha
|
||||
# comment bravo
|
||||
[Unit]
|
||||
; comment charlie
|
||||
# comment delta
|
||||
#Description=Foo
|
||||
Description=Bar
|
||||
; comment echo
|
||||
# comment foxtrot
|
||||
`,
|
||||
`[Unit]
|
||||
Description=Bar
|
||||
`},
|
||||
}
|
||||
for i, tt := range tests {
|
||||
ds, err := Deserialize(bytes.NewBufferString(tt.in))
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error parsing unit: %v", i, err)
|
||||
continue
|
||||
}
|
||||
out, err := ioutil.ReadAll(Serialize(ds))
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error serializing unit: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if g := string(out); g != tt.wout {
|
||||
t.Errorf("case %d: incorrect output", i)
|
||||
t.Logf("Expected:\n%#v", tt.wout)
|
||||
t.Logf("Actual:\n%#v", g)
|
||||
}
|
||||
}
|
||||
}
|
116
vendor/github.com/coreos/go-systemd/unit/escape.go
generated
vendored
Normal file
116
vendor/github.com/coreos/go-systemd/unit/escape.go
generated
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Implements systemd-escape [--unescape] [--path]
|
||||
|
||||
package unit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
allowed = `:_.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`
|
||||
)
|
||||
|
||||
// If isPath is true:
|
||||
// We remove redundant '/'s, the leading '/', and trailing '/'.
|
||||
// If the result is empty, a '/' is inserted.
|
||||
//
|
||||
// We always:
|
||||
// Replace the following characters with `\x%x`:
|
||||
// Leading `.`
|
||||
// `-`, `\`, and anything not in this set: `:-_.\[0-9a-zA-Z]`
|
||||
// Replace '/' with '-'.
|
||||
func escape(unescaped string, isPath bool) string {
|
||||
e := []byte{}
|
||||
inSlashes := false
|
||||
start := true
|
||||
for i := 0; i < len(unescaped); i++ {
|
||||
c := unescaped[i]
|
||||
if isPath {
|
||||
if c == '/' {
|
||||
inSlashes = true
|
||||
continue
|
||||
} else if inSlashes {
|
||||
inSlashes = false
|
||||
if !start {
|
||||
e = append(e, '-')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c == '/' {
|
||||
e = append(e, '-')
|
||||
} else if start && c == '.' || strings.IndexByte(allowed, c) == -1 {
|
||||
e = append(e, []byte(fmt.Sprintf(`\x%x`, c))...)
|
||||
} else {
|
||||
e = append(e, c)
|
||||
}
|
||||
start = false
|
||||
}
|
||||
if isPath && len(e) == 0 {
|
||||
e = append(e, '-')
|
||||
}
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// If isPath is true:
|
||||
// We always return a string beginning with '/'.
|
||||
//
|
||||
// We always:
|
||||
// Replace '-' with '/'.
|
||||
// Replace `\x%x` with the value represented in hex.
|
||||
func unescape(escaped string, isPath bool) string {
|
||||
u := []byte{}
|
||||
for i := 0; i < len(escaped); i++ {
|
||||
c := escaped[i]
|
||||
if c == '-' {
|
||||
c = '/'
|
||||
} else if c == '\\' && len(escaped)-i >= 4 && escaped[i+1] == 'x' {
|
||||
n, err := strconv.ParseInt(escaped[i+2:i+4], 16, 8)
|
||||
if err == nil {
|
||||
c = byte(n)
|
||||
i += 3
|
||||
}
|
||||
}
|
||||
u = append(u, c)
|
||||
}
|
||||
if isPath && (len(u) == 0 || u[0] != '/') {
|
||||
u = append([]byte("/"), u...)
|
||||
}
|
||||
return string(u)
|
||||
}
|
||||
|
||||
// UnitNameEscape escapes a string as `systemd-escape` would
|
||||
func UnitNameEscape(unescaped string) string {
|
||||
return escape(unescaped, false)
|
||||
}
|
||||
|
||||
// UnitNameUnescape unescapes a string as `systemd-escape --unescape` would
|
||||
func UnitNameUnescape(escaped string) string {
|
||||
return unescape(escaped, false)
|
||||
}
|
||||
|
||||
// UnitNamePathEscape escapes a string as `systemd-escape --path` would
|
||||
func UnitNamePathEscape(unescaped string) string {
|
||||
return escape(unescaped, true)
|
||||
}
|
||||
|
||||
// UnitNamePathUnescape unescapes a string as `systemd-escape --path --unescape` would
|
||||
func UnitNamePathUnescape(escaped string) string {
|
||||
return unescape(escaped, true)
|
||||
}
|
211
vendor/github.com/coreos/go-systemd/unit/escape_test.go
generated
vendored
Normal file
211
vendor/github.com/coreos/go-systemd/unit/escape_test.go
generated
vendored
Normal file
@ -0,0 +1,211 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 unit
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUnitNameEscape(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
out string
|
||||
isPath bool
|
||||
}{
|
||||
// turn empty string path into escaped /
|
||||
{
|
||||
in: "",
|
||||
out: "-",
|
||||
isPath: true,
|
||||
},
|
||||
// turn redundant ////s into single escaped /
|
||||
{
|
||||
in: "/////////",
|
||||
out: "-",
|
||||
isPath: true,
|
||||
},
|
||||
// remove all redundant ////s
|
||||
{
|
||||
in: "///foo////bar/////tail//////",
|
||||
out: "foo-bar-tail",
|
||||
isPath: true,
|
||||
},
|
||||
// leave empty string empty
|
||||
{
|
||||
in: "",
|
||||
out: "",
|
||||
isPath: false,
|
||||
},
|
||||
// escape leading dot
|
||||
{
|
||||
in: ".",
|
||||
out: `\x2e`,
|
||||
isPath: true,
|
||||
},
|
||||
// escape leading dot
|
||||
{
|
||||
in: "/.",
|
||||
out: `\x2e`,
|
||||
isPath: true,
|
||||
},
|
||||
// escape leading dot
|
||||
{
|
||||
in: "/////////.",
|
||||
out: `\x2e`,
|
||||
isPath: true,
|
||||
},
|
||||
// escape leading dot
|
||||
{
|
||||
in: "/////////.///////////////",
|
||||
out: `\x2e`,
|
||||
isPath: true,
|
||||
},
|
||||
// escape leading dot
|
||||
{
|
||||
in: ".....",
|
||||
out: `\x2e....`,
|
||||
isPath: true,
|
||||
},
|
||||
// escape leading dot
|
||||
{
|
||||
in: "/.foo/.bar",
|
||||
out: `\x2efoo-.bar`,
|
||||
isPath: true,
|
||||
},
|
||||
// escape leading dot
|
||||
{
|
||||
in: ".foo/.bar",
|
||||
out: `\x2efoo-.bar`,
|
||||
isPath: true,
|
||||
},
|
||||
// escape leading dot
|
||||
{
|
||||
in: ".foo/.bar",
|
||||
out: `\x2efoo-.bar`,
|
||||
isPath: false,
|
||||
},
|
||||
// escape disallowed
|
||||
{
|
||||
in: `///..\-!#??///`,
|
||||
out: `---..\x5c\x2d\x21\x23\x3f\x3f---`,
|
||||
isPath: false,
|
||||
},
|
||||
// escape disallowed
|
||||
{
|
||||
in: `///..\-!#??///`,
|
||||
out: `\x2e.\x5c\x2d\x21\x23\x3f\x3f`,
|
||||
isPath: true,
|
||||
},
|
||||
// escape real-world example
|
||||
{
|
||||
in: `user-cloudinit@/var/lib/coreos/vagrant/vagrantfile-user-data.service`,
|
||||
out: `user\x2dcloudinit\x40-var-lib-coreos-vagrant-vagrantfile\x2duser\x2ddata.service`,
|
||||
isPath: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
var s string
|
||||
if tt.isPath {
|
||||
s = UnitNamePathEscape(tt.in)
|
||||
} else {
|
||||
s = UnitNameEscape(tt.in)
|
||||
}
|
||||
if s != tt.out {
|
||||
t.Errorf("case %d: failed escaping %v isPath: %v - expected %v, got %v", i, tt.in, tt.isPath, tt.out, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnitNameUnescape(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
out string
|
||||
isPath bool
|
||||
}{
|
||||
// turn empty string path into /
|
||||
{
|
||||
in: "",
|
||||
out: "/",
|
||||
isPath: true,
|
||||
},
|
||||
// leave empty string empty
|
||||
{
|
||||
in: "",
|
||||
out: "",
|
||||
isPath: false,
|
||||
},
|
||||
// turn ////s into
|
||||
{
|
||||
in: "---------",
|
||||
out: "/////////",
|
||||
isPath: true,
|
||||
},
|
||||
// unescape hex
|
||||
{
|
||||
in: `---..\x5c\x2d\x21\x23\x3f\x3f---`,
|
||||
out: `///..\-!#??///`,
|
||||
isPath: false,
|
||||
},
|
||||
// unescape hex
|
||||
{
|
||||
in: `\x2e.\x5c\x2d\x21\x23\x3f\x3f`,
|
||||
out: `/..\-!#??`,
|
||||
isPath: true,
|
||||
},
|
||||
// unescape hex, retain invalids
|
||||
{
|
||||
in: `\x2e.\x5c\x2d\xaZ\x.o\x21\x23\x3f\x3f`,
|
||||
out: `/..\-\xaZ\x.o!#??`,
|
||||
isPath: true,
|
||||
},
|
||||
// unescape hex, retain invalids, partial tail
|
||||
{
|
||||
in: `\x2e.\x5c\x\x2d\xaZ\x.o\x21\x23\x3f\x3f\x3`,
|
||||
out: `/..\\x-\xaZ\x.o!#??\x3`,
|
||||
isPath: true,
|
||||
},
|
||||
// unescape hex, retain invalids, partial tail
|
||||
{
|
||||
in: `\x2e.\x5c\x\x2d\xaZ\x.o\x21\x23\x3f\x3f\x`,
|
||||
out: `/..\\x-\xaZ\x.o!#??\x`,
|
||||
isPath: true,
|
||||
},
|
||||
// unescape hex, retain invalids, partial tail
|
||||
{
|
||||
in: `\x2e.\x5c\x\x2d\xaZ\x.o\x21\x23\x3f\x3f\`,
|
||||
out: `/..\\x-\xaZ\x.o!#??\`,
|
||||
isPath: true,
|
||||
},
|
||||
// unescape real-world example
|
||||
{
|
||||
in: `user\x2dcloudinit\x40-var-lib-coreos-vagrant-vagrantfile\x2duser\x2ddata.service`,
|
||||
out: `user-cloudinit@/var/lib/coreos/vagrant/vagrantfile-user-data.service`,
|
||||
isPath: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
var s string
|
||||
if tt.isPath {
|
||||
s = UnitNamePathUnescape(tt.in)
|
||||
} else {
|
||||
s = UnitNameUnescape(tt.in)
|
||||
}
|
||||
if s != tt.out {
|
||||
t.Errorf("case %d: failed unescaping %v isPath: %v - expected %v, got %v", i, tt.in, tt.isPath, tt.out, s)
|
||||
}
|
||||
}
|
||||
}
|
54
vendor/github.com/coreos/go-systemd/unit/option.go
generated
vendored
Normal file
54
vendor/github.com/coreos/go-systemd/unit/option.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 unit
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type UnitOption struct {
|
||||
Section string
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
func NewUnitOption(section, name, value string) *UnitOption {
|
||||
return &UnitOption{Section: section, Name: name, Value: value}
|
||||
}
|
||||
|
||||
func (uo *UnitOption) String() string {
|
||||
return fmt.Sprintf("{Section: %q, Name: %q, Value: %q}", uo.Section, uo.Name, uo.Value)
|
||||
}
|
||||
|
||||
func (uo *UnitOption) Match(other *UnitOption) bool {
|
||||
return uo.Section == other.Section &&
|
||||
uo.Name == other.Name &&
|
||||
uo.Value == other.Value
|
||||
}
|
||||
|
||||
func AllMatch(u1 []*UnitOption, u2 []*UnitOption) bool {
|
||||
length := len(u1)
|
||||
if length != len(u2) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
if !u1[i].Match(u2[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
214
vendor/github.com/coreos/go-systemd/unit/option_test.go
generated
vendored
Normal file
214
vendor/github.com/coreos/go-systemd/unit/option_test.go
generated
vendored
Normal file
@ -0,0 +1,214 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 unit
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAllMatch(t *testing.T) {
|
||||
tests := []struct {
|
||||
u1 []*UnitOption
|
||||
u2 []*UnitOption
|
||||
match bool
|
||||
}{
|
||||
// empty lists match
|
||||
{
|
||||
u1: []*UnitOption{},
|
||||
u2: []*UnitOption{},
|
||||
match: true,
|
||||
},
|
||||
|
||||
// simple match of a single option
|
||||
{
|
||||
u1: []*UnitOption{
|
||||
{Section: "Unit", Name: "Description", Value: "FOO"},
|
||||
},
|
||||
u2: []*UnitOption{
|
||||
{Section: "Unit", Name: "Description", Value: "FOO"},
|
||||
},
|
||||
match: true,
|
||||
},
|
||||
|
||||
// single option mismatched
|
||||
{
|
||||
u1: []*UnitOption{
|
||||
{Section: "Unit", Name: "Description", Value: "FOO"},
|
||||
},
|
||||
u2: []*UnitOption{
|
||||
{Section: "Unit", Name: "Description", Value: "BAR"},
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
|
||||
// multiple options match
|
||||
{
|
||||
u1: []*UnitOption{
|
||||
{Section: "Unit", Name: "Description", Value: "FOO"},
|
||||
{Section: "Unit", Name: "BindsTo", Value: "bar.service"},
|
||||
{Section: "Service", Name: "ExecStart", Value: "/bin/true"},
|
||||
},
|
||||
u2: []*UnitOption{
|
||||
{Section: "Unit", Name: "Description", Value: "FOO"},
|
||||
{Section: "Unit", Name: "BindsTo", Value: "bar.service"},
|
||||
{Section: "Service", Name: "ExecStart", Value: "/bin/true"},
|
||||
},
|
||||
match: true,
|
||||
},
|
||||
|
||||
// mismatch length
|
||||
{
|
||||
u1: []*UnitOption{
|
||||
{Section: "Unit", Name: "Description", Value: "FOO"},
|
||||
{Section: "Unit", Name: "BindsTo", Value: "bar.service"},
|
||||
},
|
||||
u2: []*UnitOption{
|
||||
{Section: "Unit", Name: "Description", Value: "FOO"},
|
||||
{Section: "Unit", Name: "BindsTo", Value: "bar.service"},
|
||||
{Section: "Service", Name: "ExecStart", Value: "/bin/true"},
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
|
||||
// multiple options misordered
|
||||
{
|
||||
u1: []*UnitOption{
|
||||
{Section: "Unit", Name: "Description", Value: "FOO"},
|
||||
{Section: "Service", Name: "ExecStart", Value: "/bin/true"},
|
||||
},
|
||||
u2: []*UnitOption{
|
||||
{Section: "Service", Name: "ExecStart", Value: "/bin/true"},
|
||||
{Section: "Unit", Name: "Description", Value: "FOO"},
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
|
||||
// interleaved sections mismatch
|
||||
{
|
||||
u1: []*UnitOption{
|
||||
{Section: "Unit", Name: "Description", Value: "FOO"},
|
||||
{Section: "Unit", Name: "BindsTo", Value: "bar.service"},
|
||||
{Section: "Service", Name: "ExecStart", Value: "/bin/true"},
|
||||
{Section: "Service", Name: "ExecStop", Value: "/bin/true"},
|
||||
},
|
||||
u2: []*UnitOption{
|
||||
{Section: "Unit", Name: "Description", Value: "FOO"},
|
||||
{Section: "Service", Name: "ExecStart", Value: "/bin/true"},
|
||||
{Section: "Unit", Name: "BindsTo", Value: "bar.service"},
|
||||
{Section: "Service", Name: "ExecStop", Value: "/bin/true"},
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
match := AllMatch(tt.u1, tt.u2)
|
||||
if match != tt.match {
|
||||
t.Errorf("case %d: failed comparing u1 to u2 - expected match=%t, got %t", i, tt.match, match)
|
||||
}
|
||||
|
||||
match = AllMatch(tt.u2, tt.u1)
|
||||
if match != tt.match {
|
||||
t.Errorf("case %d: failed comparing u2 to u1 - expected match=%t, got %t", i, tt.match, match)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
tests := []struct {
|
||||
o1 *UnitOption
|
||||
o2 *UnitOption
|
||||
match bool
|
||||
}{
|
||||
// empty options match
|
||||
{
|
||||
o1: &UnitOption{},
|
||||
o2: &UnitOption{},
|
||||
match: true,
|
||||
},
|
||||
|
||||
// all fields match
|
||||
{
|
||||
o1: &UnitOption{
|
||||
Section: "Unit",
|
||||
Name: "Description",
|
||||
Value: "FOO",
|
||||
},
|
||||
o2: &UnitOption{
|
||||
Section: "Unit",
|
||||
Name: "Description",
|
||||
Value: "FOO",
|
||||
},
|
||||
match: true,
|
||||
},
|
||||
|
||||
// Section mismatch
|
||||
{
|
||||
o1: &UnitOption{
|
||||
Section: "Unit",
|
||||
Name: "Description",
|
||||
Value: "FOO",
|
||||
},
|
||||
o2: &UnitOption{
|
||||
Section: "X-Other",
|
||||
Name: "Description",
|
||||
Value: "FOO",
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
|
||||
// Name mismatch
|
||||
{
|
||||
o1: &UnitOption{
|
||||
Section: "Unit",
|
||||
Name: "Description",
|
||||
Value: "FOO",
|
||||
},
|
||||
o2: &UnitOption{
|
||||
Section: "Unit",
|
||||
Name: "BindsTo",
|
||||
Value: "FOO",
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
|
||||
// Value mismatch
|
||||
{
|
||||
o1: &UnitOption{
|
||||
Section: "Unit",
|
||||
Name: "Description",
|
||||
Value: "FOO",
|
||||
},
|
||||
o2: &UnitOption{
|
||||
Section: "Unit",
|
||||
Name: "Description",
|
||||
Value: "BAR",
|
||||
},
|
||||
match: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
match := tt.o1.Match(tt.o2)
|
||||
if match != tt.match {
|
||||
t.Errorf("case %d: failed comparing o1 to o2 - expected match=%t, got %t", i, tt.match, match)
|
||||
}
|
||||
|
||||
match = tt.o2.Match(tt.o1)
|
||||
if match != tt.match {
|
||||
t.Errorf("case %d: failed comparing o2 to o1 - expected match=%t, got %t", i, tt.match, match)
|
||||
}
|
||||
}
|
||||
}
|
75
vendor/github.com/coreos/go-systemd/unit/serialize.go
generated
vendored
Normal file
75
vendor/github.com/coreos/go-systemd/unit/serialize.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 unit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Serialize encodes all of the given UnitOption objects into a
|
||||
// unit file. When serialized the options are sorted in their
|
||||
// supplied order but grouped by section.
|
||||
func Serialize(opts []*UnitOption) io.Reader {
|
||||
var buf bytes.Buffer
|
||||
|
||||
if len(opts) == 0 {
|
||||
return &buf
|
||||
}
|
||||
|
||||
// Index of sections -> ordered options
|
||||
idx := map[string][]*UnitOption{}
|
||||
// Separately preserve order in which sections were seen
|
||||
sections := []string{}
|
||||
for _, opt := range opts {
|
||||
sec := opt.Section
|
||||
if _, ok := idx[sec]; !ok {
|
||||
sections = append(sections, sec)
|
||||
}
|
||||
idx[sec] = append(idx[sec], opt)
|
||||
}
|
||||
|
||||
for i, sect := range sections {
|
||||
writeSectionHeader(&buf, sect)
|
||||
writeNewline(&buf)
|
||||
|
||||
opts := idx[sect]
|
||||
for _, opt := range opts {
|
||||
writeOption(&buf, opt)
|
||||
writeNewline(&buf)
|
||||
}
|
||||
if i < len(sections)-1 {
|
||||
writeNewline(&buf)
|
||||
}
|
||||
}
|
||||
|
||||
return &buf
|
||||
}
|
||||
|
||||
func writeNewline(buf *bytes.Buffer) {
|
||||
buf.WriteRune('\n')
|
||||
}
|
||||
|
||||
func writeSectionHeader(buf *bytes.Buffer, section string) {
|
||||
buf.WriteRune('[')
|
||||
buf.WriteString(section)
|
||||
buf.WriteRune(']')
|
||||
}
|
||||
|
||||
func writeOption(buf *bytes.Buffer, opt *UnitOption) {
|
||||
buf.WriteString(opt.Name)
|
||||
buf.WriteRune('=')
|
||||
buf.WriteString(opt.Value)
|
||||
}
|
170
vendor/github.com/coreos/go-systemd/unit/serialize_test.go
generated
vendored
Normal file
170
vendor/github.com/coreos/go-systemd/unit/serialize_test.go
generated
vendored
Normal file
@ -0,0 +1,170 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 unit
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSerialize(t *testing.T) {
|
||||
tests := []struct {
|
||||
input []*UnitOption
|
||||
output string
|
||||
}{
|
||||
// no options results in empty file
|
||||
{
|
||||
[]*UnitOption{},
|
||||
``,
|
||||
},
|
||||
|
||||
// options with same section share the header
|
||||
{
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Foo"},
|
||||
&UnitOption{"Unit", "BindsTo", "bar.service"},
|
||||
},
|
||||
`[Unit]
|
||||
Description=Foo
|
||||
BindsTo=bar.service
|
||||
`,
|
||||
},
|
||||
|
||||
// options with same name are not combined
|
||||
{
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Foo"},
|
||||
&UnitOption{"Unit", "Description", "Bar"},
|
||||
},
|
||||
`[Unit]
|
||||
Description=Foo
|
||||
Description=Bar
|
||||
`,
|
||||
},
|
||||
|
||||
// multiple options printed under different section headers
|
||||
{
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Foo"},
|
||||
&UnitOption{"Service", "ExecStart", "/usr/bin/sleep infinity"},
|
||||
},
|
||||
`[Unit]
|
||||
Description=Foo
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/sleep infinity
|
||||
`,
|
||||
},
|
||||
|
||||
// options are grouped into sections
|
||||
{
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Foo"},
|
||||
&UnitOption{"Service", "ExecStart", "/usr/bin/sleep infinity"},
|
||||
&UnitOption{"Unit", "BindsTo", "bar.service"},
|
||||
},
|
||||
`[Unit]
|
||||
Description=Foo
|
||||
BindsTo=bar.service
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/sleep infinity
|
||||
`,
|
||||
},
|
||||
|
||||
// options are ordered within groups, and sections are ordered in the order in which they were first seen
|
||||
{
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Foo"},
|
||||
&UnitOption{"Service", "ExecStart", "/usr/bin/sleep infinity"},
|
||||
&UnitOption{"Unit", "BindsTo", "bar.service"},
|
||||
&UnitOption{"X-Foo", "Bar", "baz"},
|
||||
&UnitOption{"Service", "ExecStop", "/usr/bin/sleep 1"},
|
||||
&UnitOption{"Unit", "Documentation", "https://foo.com"},
|
||||
},
|
||||
`[Unit]
|
||||
Description=Foo
|
||||
BindsTo=bar.service
|
||||
Documentation=https://foo.com
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/sleep infinity
|
||||
ExecStop=/usr/bin/sleep 1
|
||||
|
||||
[X-Foo]
|
||||
Bar=baz
|
||||
`,
|
||||
},
|
||||
|
||||
// utf8 characters are not a problem
|
||||
{
|
||||
[]*UnitOption{
|
||||
&UnitOption{"©", "µ☃", "ÇôrèÕ$"},
|
||||
},
|
||||
`[©]
|
||||
µ☃=ÇôrèÕ$
|
||||
`,
|
||||
},
|
||||
|
||||
// no verification is done on section names
|
||||
{
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Un\nit", "Description", "Foo"},
|
||||
},
|
||||
`[Un
|
||||
it]
|
||||
Description=Foo
|
||||
`,
|
||||
},
|
||||
|
||||
// no verification is done on option names
|
||||
{
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Desc\nription", "Foo"},
|
||||
},
|
||||
`[Unit]
|
||||
Desc
|
||||
ription=Foo
|
||||
`,
|
||||
},
|
||||
|
||||
// no verification is done on option values
|
||||
{
|
||||
[]*UnitOption{
|
||||
&UnitOption{"Unit", "Description", "Fo\no"},
|
||||
},
|
||||
`[Unit]
|
||||
Description=Fo
|
||||
o
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
outReader := Serialize(tt.input)
|
||||
outBytes, err := ioutil.ReadAll(outReader)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: encountered error while reading output: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
output := string(outBytes)
|
||||
if tt.output != output {
|
||||
t.Errorf("case %d: incorrect output", i)
|
||||
t.Logf("Expected:\n%s", tt.output)
|
||||
t.Logf("Actual:\n%s", output)
|
||||
}
|
||||
}
|
||||
}
|
270
vendor/github.com/coreos/go-systemd/util/util.go
generated
vendored
Normal file
270
vendor/github.com/coreos/go-systemd/util/util.go
generated
vendored
Normal file
@ -0,0 +1,270 @@
|
||||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// 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 contains utility functions related to systemd that applications
|
||||
// can use to check things like whether systemd is running. Note that some of
|
||||
// these functions attempt to manually load systemd libraries at runtime rather
|
||||
// than linking against them.
|
||||
package util
|
||||
|
||||
// #cgo LDFLAGS: -ldl
|
||||
// #include <stdlib.h>
|
||||
// #include <dlfcn.h>
|
||||
// #include <sys/types.h>
|
||||
// #include <unistd.h>
|
||||
//
|
||||
// int
|
||||
// my_sd_pid_get_owner_uid(void *f, pid_t pid, uid_t *uid)
|
||||
// {
|
||||
// int (*sd_pid_get_owner_uid)(pid_t, uid_t *);
|
||||
//
|
||||
// sd_pid_get_owner_uid = (int (*)(pid_t, uid_t *))f;
|
||||
// return sd_pid_get_owner_uid(pid, uid);
|
||||
// }
|
||||
//
|
||||
// int
|
||||
// my_sd_pid_get_unit(void *f, pid_t pid, char **unit)
|
||||
// {
|
||||
// int (*sd_pid_get_unit)(pid_t, char **);
|
||||
//
|
||||
// sd_pid_get_unit = (int (*)(pid_t, char **))f;
|
||||
// return sd_pid_get_unit(pid, unit);
|
||||
// }
|
||||
//
|
||||
// int
|
||||
// my_sd_pid_get_slice(void *f, pid_t pid, char **slice)
|
||||
// {
|
||||
// int (*sd_pid_get_slice)(pid_t, char **);
|
||||
//
|
||||
// sd_pid_get_slice = (int (*)(pid_t, char **))f;
|
||||
// return sd_pid_get_slice(pid, slice);
|
||||
// }
|
||||
//
|
||||
// int
|
||||
// am_session_leader()
|
||||
// {
|
||||
// return (getsid(0) == getpid());
|
||||
// }
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var ErrSoNotFound = errors.New("unable to open a handle to libsystemd")
|
||||
|
||||
// libHandle represents an open handle to the systemd C library
|
||||
type libHandle struct {
|
||||
handle unsafe.Pointer
|
||||
libname string
|
||||
}
|
||||
|
||||
func (h *libHandle) Close() error {
|
||||
if r := C.dlclose(h.handle); r != 0 {
|
||||
return fmt.Errorf("error closing %v: %d", h.libname, r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getHandle tries to get a handle to a systemd library (.so), attempting to
|
||||
// access it by several different names and returning the first that is
|
||||
// successfully opened. Callers are responsible for closing the handler.
|
||||
// If no library can be successfully opened, an error is returned.
|
||||
func getHandle() (*libHandle, error) {
|
||||
for _, name := range []string{
|
||||
// systemd < 209
|
||||
"libsystemd-login.so",
|
||||
"libsystemd-login.so.0",
|
||||
|
||||
// systemd >= 209 merged libsystemd-login into libsystemd proper
|
||||
"libsystemd.so",
|
||||
"libsystemd.so.0",
|
||||
} {
|
||||
libname := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(libname))
|
||||
handle := C.dlopen(libname, C.RTLD_LAZY)
|
||||
if handle != nil {
|
||||
h := &libHandle{
|
||||
handle: handle,
|
||||
libname: name,
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrSoNotFound
|
||||
}
|
||||
|
||||
// GetRunningSlice attempts to retrieve the name of the systemd slice in which
|
||||
// the current process is running.
|
||||
// This function is a wrapper around the libsystemd C library; if it cannot be
|
||||
// opened, an error is returned.
|
||||
func GetRunningSlice() (slice string, err error) {
|
||||
var h *libHandle
|
||||
h, err = getHandle()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err1 := h.Close(); err1 != nil {
|
||||
err = err1
|
||||
}
|
||||
}()
|
||||
|
||||
sym := C.CString("sd_pid_get_slice")
|
||||
defer C.free(unsafe.Pointer(sym))
|
||||
sd_pid_get_slice := C.dlsym(h.handle, sym)
|
||||
if sd_pid_get_slice == nil {
|
||||
err = fmt.Errorf("error resolving sd_pid_get_slice function")
|
||||
return
|
||||
}
|
||||
|
||||
var s string
|
||||
sl := C.CString(s)
|
||||
defer C.free(unsafe.Pointer(sl))
|
||||
|
||||
ret := C.my_sd_pid_get_slice(sd_pid_get_slice, 0, &sl)
|
||||
if ret < 0 {
|
||||
err = fmt.Errorf("error calling sd_pid_get_slice: %v", syscall.Errno(-ret))
|
||||
return
|
||||
}
|
||||
|
||||
return C.GoString(sl), nil
|
||||
}
|
||||
|
||||
// RunningFromSystemService tries to detect whether the current process has
|
||||
// been invoked from a system service. The condition for this is whether the
|
||||
// process is _not_ a user process. User processes are those running in session
|
||||
// scopes or under per-user `systemd --user` instances.
|
||||
//
|
||||
// To avoid false positives on systems without `pam_systemd` (which is
|
||||
// responsible for creating user sessions), this function also uses a heuristic
|
||||
// to detect whether it's being invoked from a session leader process. This is
|
||||
// the case if the current process is executed directly from a service file
|
||||
// (e.g. with `ExecStart=/this/cmd`). Note that this heuristic will fail if the
|
||||
// command is instead launched in a subshell or similar so that it is not
|
||||
// session leader (e.g. `ExecStart=/bin/bash -c "/this/cmd"`)
|
||||
//
|
||||
// This function is a wrapper around the libsystemd C library; if this is
|
||||
// unable to successfully open a handle to the library for any reason (e.g. it
|
||||
// cannot be found), an errr will be returned
|
||||
func RunningFromSystemService() (ret bool, err error) {
|
||||
var h *libHandle
|
||||
h, err = getHandle()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err1 := h.Close(); err1 != nil {
|
||||
err = err1
|
||||
}
|
||||
}()
|
||||
|
||||
sym := C.CString("sd_pid_get_owner_uid")
|
||||
defer C.free(unsafe.Pointer(sym))
|
||||
sd_pid_get_owner_uid := C.dlsym(h.handle, sym)
|
||||
if sd_pid_get_owner_uid == nil {
|
||||
err = fmt.Errorf("error resolving sd_pid_get_owner_uid function")
|
||||
return
|
||||
}
|
||||
|
||||
var uid C.uid_t
|
||||
errno := C.my_sd_pid_get_owner_uid(sd_pid_get_owner_uid, 0, &uid)
|
||||
serrno := syscall.Errno(-errno)
|
||||
// when we're running from a unit file, sd_pid_get_owner_uid returns
|
||||
// ENOENT (systemd <220) or ENXIO (systemd >=220)
|
||||
switch {
|
||||
case errno >= 0:
|
||||
ret = false
|
||||
case serrno == syscall.ENOENT, serrno == syscall.ENXIO:
|
||||
// Since the implementation of sessions in systemd relies on
|
||||
// the `pam_systemd` module, using the sd_pid_get_owner_uid
|
||||
// heuristic alone can result in false positives if that module
|
||||
// (or PAM itself) is not present or properly configured on the
|
||||
// system. As such, we also check if we're the session leader,
|
||||
// which should be the case if we're invoked from a unit file,
|
||||
// but not if e.g. we're invoked from the command line from a
|
||||
// user's login session
|
||||
ret = C.am_session_leader() == 1
|
||||
default:
|
||||
err = fmt.Errorf("error calling sd_pid_get_owner_uid: %v", syscall.Errno(-errno))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CurrentUnitName attempts to retrieve the name of the systemd system unit
|
||||
// from which the calling process has been invoked. It wraps the systemd
|
||||
// `sd_pid_get_unit` call, with the same caveat: for processes not part of a
|
||||
// systemd system unit, this function will return an error.
|
||||
func CurrentUnitName() (unit string, err error) {
|
||||
var h *libHandle
|
||||
h, err = getHandle()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err1 := h.Close(); err1 != nil {
|
||||
err = err1
|
||||
}
|
||||
}()
|
||||
|
||||
sym := C.CString("sd_pid_get_unit")
|
||||
defer C.free(unsafe.Pointer(sym))
|
||||
sd_pid_get_unit := C.dlsym(h.handle, sym)
|
||||
if sd_pid_get_unit == nil {
|
||||
err = fmt.Errorf("error resolving sd_pid_get_unit function")
|
||||
return
|
||||
}
|
||||
|
||||
var s string
|
||||
u := C.CString(s)
|
||||
defer C.free(unsafe.Pointer(u))
|
||||
|
||||
ret := C.my_sd_pid_get_unit(sd_pid_get_unit, 0, &u)
|
||||
if ret < 0 {
|
||||
err = fmt.Errorf("error calling sd_pid_get_unit: %v", syscall.Errno(-ret))
|
||||
return
|
||||
}
|
||||
|
||||
unit = C.GoString(u)
|
||||
return
|
||||
}
|
||||
|
||||
// IsRunningSystemd checks whether the host was booted with systemd as its init
|
||||
// system. This functions similarly to systemd's `sd_booted(3)`: internally, it
|
||||
// checks whether /run/systemd/system/ exists and is a directory.
|
||||
// http://www.freedesktop.org/software/systemd/man/sd_booted.html
|
||||
func IsRunningSystemd() bool {
|
||||
fi, err := os.Lstat("/run/systemd/system")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return fi.IsDir()
|
||||
}
|
||||
|
||||
// GetMachineID returns a host's 128-bit machine ID as a string. This functions
|
||||
// similarly to systemd's `sd_id128_get_machine`: internally, it simply reads
|
||||
// the contents of /etc/machine-id
|
||||
// http://www.freedesktop.org/software/systemd/man/sd_id128_get_machine.html
|
||||
func GetMachineID() (string, error) {
|
||||
machineID, err := ioutil.ReadFile("/etc/machine-id")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read /etc/machine-id: %v", err)
|
||||
}
|
||||
return strings.TrimSpace(string(machineID)), nil
|
||||
}
|
27
vendor/github.com/coreos/pkg/.gitignore
generated
vendored
Normal file
27
vendor/github.com/coreos/pkg/.gitignore
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
# 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
|
||||
|
||||
bin/
|
||||
coverage/
|
71
vendor/github.com/coreos/pkg/CONTRIBUTING.md
generated
vendored
Normal file
71
vendor/github.com/coreos/pkg/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
# How to Contribute
|
||||
|
||||
CoreOS projects are [Apache 2.0 licensed](LICENSE) and accept contributions via
|
||||
GitHub pull requests. This document outlines some of the conventions on
|
||||
development workflow, commit message formatting, contact points and other
|
||||
resources to make it easier to get your contribution accepted.
|
||||
|
||||
# Certificate of Origin
|
||||
|
||||
By contributing to this project you agree to the Developer Certificate of
|
||||
Origin (DCO). This document was created by the Linux Kernel community and is a
|
||||
simple statement that you, as a contributor, have the legal right to make the
|
||||
contribution. See the [DCO](DCO) file for details.
|
||||
|
||||
# Email and Chat
|
||||
|
||||
The project currently uses the general CoreOS email list and IRC channel:
|
||||
- Email: [coreos-dev](https://groups.google.com/forum/#!forum/coreos-dev)
|
||||
- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org
|
||||
|
||||
Please avoid emailing maintainers found in the MAINTAINERS file directly. They
|
||||
are very busy and read the mailing lists.
|
||||
|
||||
## Getting Started
|
||||
|
||||
- Fork the repository on GitHub
|
||||
- Read the [README](README.md) for build and test instructions
|
||||
- Play with the project, submit bugs, submit patches!
|
||||
|
||||
## Contribution Flow
|
||||
|
||||
This is a rough outline of what a contributor's workflow looks like:
|
||||
|
||||
- Create a topic branch from where you want to base your work (usually master).
|
||||
- Make commits of logical units.
|
||||
- Make sure your commit messages are in the proper format (see below).
|
||||
- Push your changes to a topic branch in your fork of the repository.
|
||||
- Make sure the tests pass, and add any new tests as appropriate.
|
||||
- Submit a pull request to the original repository.
|
||||
|
||||
Thanks for your contributions!
|
||||
|
||||
### Format of the Commit Message
|
||||
|
||||
We follow a rough convention for commit messages that is designed to answer two
|
||||
questions: what changed and why. The subject line should feature the what and
|
||||
the body of the commit should describe the why.
|
||||
|
||||
```
|
||||
scripts: add the test-cluster command
|
||||
|
||||
this uses tmux to setup a test cluster that you can easily kill and
|
||||
start for debugging.
|
||||
|
||||
Fixes #38
|
||||
```
|
||||
|
||||
The format can be described more formally as follows:
|
||||
|
||||
```
|
||||
<subsystem>: <what changed>
|
||||
<BLANK LINE>
|
||||
<why this change was made>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
The first line is the subject and should be no longer than 70 characters, the
|
||||
second line is always blank, and other lines should be wrapped at 80 characters.
|
||||
This allows the message to be easier to read on GitHub as well as in various
|
||||
git tools.
|
36
vendor/github.com/coreos/pkg/DCO
generated
vendored
Normal file
36
vendor/github.com/coreos/pkg/DCO
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
660 York Street, Suite 102,
|
||||
San Francisco, CA 94110 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
1
vendor/github.com/coreos/pkg/MAINTAINERS
generated
vendored
Normal file
1
vendor/github.com/coreos/pkg/MAINTAINERS
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
Ed Rooth <ed.rooth@coreos.com> (@sym3tri)
|
3
vendor/github.com/coreos/pkg/README.md
generated
vendored
Normal file
3
vendor/github.com/coreos/pkg/README.md
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
a collection of go utility packages
|
||||
|
||||
[![Build Status](https://semaphoreci.com/api/v1/projects/14b3f261-22c2-4f56-b1ff-f23f4aa03f5c/411991/badge.svg)](https://semaphoreci.com/coreos/pkg) [![Godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/coreos/pkg)
|
3
vendor/github.com/coreos/pkg/build
generated
vendored
Executable file
3
vendor/github.com/coreos/pkg/build
generated
vendored
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
go build ./...
|
94
vendor/github.com/coreos/pkg/cryptoutil/aes.go
generated
vendored
Normal file
94
vendor/github.com/coreos/pkg/cryptoutil/aes.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
package cryptoutil
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// pad uses the PKCS#7 padding scheme to align the a payload to a specific block size
|
||||
func pad(plaintext []byte, bsize int) ([]byte, error) {
|
||||
if bsize >= 256 {
|
||||
return nil, errors.New("bsize must be < 256")
|
||||
}
|
||||
pad := bsize - (len(plaintext) % bsize)
|
||||
if pad == 0 {
|
||||
pad = bsize
|
||||
}
|
||||
for i := 0; i < pad; i++ {
|
||||
plaintext = append(plaintext, byte(pad))
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// unpad strips the padding previously added using the PKCS#7 padding scheme
|
||||
func unpad(paddedtext []byte) ([]byte, error) {
|
||||
length := len(paddedtext)
|
||||
paddedtext, lbyte := paddedtext[:length-1], paddedtext[length-1]
|
||||
pad := int(lbyte)
|
||||
if pad >= 256 || pad > length {
|
||||
return nil, errors.New("padding malformed")
|
||||
}
|
||||
return paddedtext[:length-(pad)], nil
|
||||
}
|
||||
|
||||
// AESEncrypt encrypts a payload with an AES cipher.
|
||||
// The returned ciphertext has three notable properties:
|
||||
// 1. ciphertext is aligned to the standard AES block size
|
||||
// 2. ciphertext is padded using PKCS#7
|
||||
// 3. IV is prepended to the ciphertext
|
||||
func AESEncrypt(plaintext, key []byte) ([]byte, error) {
|
||||
plaintext, err := pad(plaintext, aes.BlockSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
if _, err := rand.Read(iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
|
||||
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// AESDecrypt decrypts an encrypted payload with an AES cipher.
|
||||
// The decryption algorithm makes three assumptions:
|
||||
// 1. ciphertext is aligned to the standard AES block size
|
||||
// 2. ciphertext is padded using PKCS#7
|
||||
// 3. the IV is prepended to ciphertext
|
||||
func AESDecrypt(ciphertext, key []byte) ([]byte, error) {
|
||||
if len(ciphertext) < aes.BlockSize {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
ciphertext = ciphertext[aes.BlockSize:]
|
||||
|
||||
if len(ciphertext)%aes.BlockSize != 0 {
|
||||
return nil, errors.New("ciphertext is not a multiple of the block size")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
mode.CryptBlocks(ciphertext, ciphertext)
|
||||
|
||||
if len(ciphertext)%aes.BlockSize != 0 {
|
||||
return nil, errors.New("ciphertext is not a multiple of the block size")
|
||||
}
|
||||
|
||||
return unpad(ciphertext)
|
||||
}
|
93
vendor/github.com/coreos/pkg/cryptoutil/aes_test.go
generated
vendored
Normal file
93
vendor/github.com/coreos/pkg/cryptoutil/aes_test.go
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
package cryptoutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPadUnpad(t *testing.T) {
|
||||
tests := []struct {
|
||||
plaintext []byte
|
||||
bsize int
|
||||
padded []byte
|
||||
}{
|
||||
{
|
||||
plaintext: []byte{1, 2, 3, 4},
|
||||
bsize: 7,
|
||||
padded: []byte{1, 2, 3, 4, 3, 3, 3},
|
||||
},
|
||||
{
|
||||
plaintext: []byte{1, 2, 3, 4, 5, 6, 7},
|
||||
bsize: 3,
|
||||
padded: []byte{1, 2, 3, 4, 5, 6, 7, 2, 2},
|
||||
},
|
||||
{
|
||||
plaintext: []byte{9, 9, 9, 9},
|
||||
bsize: 4,
|
||||
padded: []byte{9, 9, 9, 9, 4, 4, 4, 4},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
padded, err := pad(tt.plaintext, tt.bsize)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(tt.padded, padded) {
|
||||
t.Errorf("case %d: want=%v got=%v", i, tt.padded, padded)
|
||||
continue
|
||||
}
|
||||
|
||||
plaintext, err := unpad(tt.padded)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(tt.plaintext, plaintext) {
|
||||
t.Errorf("case %d: want=%v got=%v", i, tt.plaintext, plaintext)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPadMaxBlockSize(t *testing.T) {
|
||||
_, err := pad([]byte{1, 2, 3}, 256)
|
||||
if err == nil {
|
||||
t.Errorf("Expected non-nil error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAESEncryptDecrypt(t *testing.T) {
|
||||
message := []byte("Let me worry about blank.")
|
||||
key := append([]byte("shark"), make([]byte, 27)...)
|
||||
|
||||
ciphertext, err := AESEncrypt(message, key)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if reflect.DeepEqual(message, ciphertext) {
|
||||
t.Fatal("Encrypted data matches original payload")
|
||||
}
|
||||
|
||||
decrypted, err := AESDecrypt(ciphertext, key)
|
||||
if !reflect.DeepEqual(message, decrypted) {
|
||||
t.Fatalf("Decrypted data does not match original payload: want=%v got=%v", message, decrypted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAESDecryptWrongKey(t *testing.T) {
|
||||
message := []byte("My bones!")
|
||||
key := append([]byte("shark"), make([]byte, 27)...)
|
||||
|
||||
ciphertext, err := AESEncrypt(message, key)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
wrongKey := append([]byte("sheep"), make([]byte, 27)...)
|
||||
decrypted, _ := AESDecrypt(ciphertext, wrongKey)
|
||||
if reflect.DeepEqual(message, decrypted) {
|
||||
t.Fatalf("Data decrypted with different key matches original payload")
|
||||
}
|
||||
}
|
33
vendor/github.com/coreos/pkg/flagutil/env.go
generated
vendored
Normal file
33
vendor/github.com/coreos/pkg/flagutil/env.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package flagutil
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SetFlagsFromEnv parses all registered flags in the given flagset,
|
||||
// and if they are not already set it attempts to set their values from
|
||||
// environment variables. Environment variables take the name of the flag but
|
||||
// are UPPERCASE, and any dashes are replaced by underscores. Environment
|
||||
// variables additionally are prefixed by the given string followed by
|
||||
// and underscore. For example, if prefix=PREFIX: some-flag => PREFIX_SOME_FLAG
|
||||
func SetFlagsFromEnv(fs *flag.FlagSet, prefix string) (err error) {
|
||||
alreadySet := make(map[string]bool)
|
||||
fs.Visit(func(f *flag.Flag) {
|
||||
alreadySet[f.Name] = true
|
||||
})
|
||||
fs.VisitAll(func(f *flag.Flag) {
|
||||
if !alreadySet[f.Name] {
|
||||
key := prefix + "_" + strings.ToUpper(strings.Replace(f.Name, "-", "_", -1))
|
||||
val := os.Getenv(key)
|
||||
if val != "" {
|
||||
if serr := fs.Set(f.Name, val); serr != nil {
|
||||
err = fmt.Errorf("invalid value %q for %s: %v", val, key, serr)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return err
|
||||
}
|
64
vendor/github.com/coreos/pkg/flagutil/env_test.go
generated
vendored
Normal file
64
vendor/github.com/coreos/pkg/flagutil/env_test.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
package flagutil
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSetFlagsFromEnv(t *testing.T) {
|
||||
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||
fs.String("a", "", "")
|
||||
fs.String("b", "", "")
|
||||
fs.String("c", "", "")
|
||||
fs.Parse([]string{})
|
||||
|
||||
os.Clearenv()
|
||||
// flags should be settable using env vars
|
||||
os.Setenv("MYPROJ_A", "foo")
|
||||
// and command-line flags
|
||||
if err := fs.Set("b", "bar"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// command-line flags take precedence over env vars
|
||||
os.Setenv("MYPROJ_C", "woof")
|
||||
if err := fs.Set("c", "quack"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// first verify that flags are as expected before reading the env
|
||||
for f, want := range map[string]string{
|
||||
"a": "",
|
||||
"b": "bar",
|
||||
"c": "quack",
|
||||
} {
|
||||
if got := fs.Lookup(f).Value.String(); got != want {
|
||||
t.Fatalf("flag %q=%q, want %q", f, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// now read the env and verify flags were updated as expected
|
||||
err := SetFlagsFromEnv(fs, "MYPROJ")
|
||||
if err != nil {
|
||||
t.Errorf("err=%v, want nil", err)
|
||||
}
|
||||
for f, want := range map[string]string{
|
||||
"a": "foo",
|
||||
"b": "bar",
|
||||
"c": "quack",
|
||||
} {
|
||||
if got := fs.Lookup(f).Value.String(); got != want {
|
||||
t.Errorf("flag %q=%q, want %q", f, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetFlagsFromEnvBad(t *testing.T) {
|
||||
// now verify that an error is propagated
|
||||
fs := flag.NewFlagSet("testing", flag.ExitOnError)
|
||||
fs.Int("x", 0, "")
|
||||
os.Setenv("MYPROJ_X", "not_a_number")
|
||||
if err := SetFlagsFromEnv(fs, "MYPROJ"); err == nil {
|
||||
t.Errorf("err=nil, want != nil")
|
||||
}
|
||||
}
|
44
vendor/github.com/coreos/pkg/flagutil/types.go
generated
vendored
Normal file
44
vendor/github.com/coreos/pkg/flagutil/types.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
package flagutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IPv4Flag parses a string into a net.IP after asserting that it
|
||||
// is an IPv4 address. This type implements the flag.Value interface.
|
||||
type IPv4Flag struct {
|
||||
val net.IP
|
||||
}
|
||||
|
||||
func (f *IPv4Flag) IP() net.IP {
|
||||
return f.val
|
||||
}
|
||||
|
||||
func (f *IPv4Flag) Set(v string) error {
|
||||
ip := net.ParseIP(v)
|
||||
if ip == nil || ip.To4() == nil {
|
||||
return errors.New("not an IPv4 address")
|
||||
}
|
||||
f.val = ip
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *IPv4Flag) String() string {
|
||||
return f.val.String()
|
||||
}
|
||||
|
||||
// StringSliceFlag parses a comma-delimited list of strings into
|
||||
// a []string. This type implements the flag.Value interface.
|
||||
type StringSliceFlag []string
|
||||
|
||||
func (ss *StringSliceFlag) String() string {
|
||||
return fmt.Sprintf("%+v", *ss)
|
||||
}
|
||||
|
||||
func (ss *StringSliceFlag) Set(v string) error {
|
||||
*ss = strings.Split(v, ",")
|
||||
return nil
|
||||
}
|
57
vendor/github.com/coreos/pkg/flagutil/types_test.go
generated
vendored
Normal file
57
vendor/github.com/coreos/pkg/flagutil/types_test.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
package flagutil
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIPv4FlagSetInvalidArgument(t *testing.T) {
|
||||
tests := []string{
|
||||
"",
|
||||
"foo",
|
||||
"::",
|
||||
"127.0.0.1:4328",
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
var f IPv4Flag
|
||||
if err := f.Set(tt); err == nil {
|
||||
t.Errorf("case %d: expected non-nil error", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPv4FlagSetValidArgument(t *testing.T) {
|
||||
tests := []string{
|
||||
"127.0.0.1",
|
||||
"0.0.0.0",
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
var f IPv4Flag
|
||||
if err := f.Set(tt); err != nil {
|
||||
t.Errorf("case %d: err=%v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringSliceFlag(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want []string
|
||||
}{
|
||||
{input: "", want: []string{""}},
|
||||
{input: "foo", want: []string{"foo"}},
|
||||
{input: "foo,bar", want: []string{"foo", "bar"}},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
var f StringSliceFlag
|
||||
if err := f.Set(tt.input); err != nil {
|
||||
t.Errorf("case %d: err=%v", i, err)
|
||||
}
|
||||
if !reflect.DeepEqual(tt.want, []string(f)) {
|
||||
t.Errorf("case %d: want=%v got=%v", i, tt.want, f)
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user