mirror of
https://github.com/poseidon/matchbox.git
synced 2026-03-02 22:58:55 +00:00
Compare commits
378 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2813003174 | ||
|
|
e309d55279 | ||
|
|
3a176ad84e | ||
|
|
45d1500010 | ||
|
|
ae6777f274 | ||
|
|
0736b1619a | ||
|
|
4d8e86ff9f | ||
|
|
d1a5fdbbc8 | ||
|
|
af1c2247d7 | ||
|
|
f24ce09525 | ||
|
|
a4434c6fe0 | ||
|
|
46c39eaf8e | ||
|
|
f37afcd15d | ||
|
|
5fc50eef22 | ||
|
|
00cb6006a4 | ||
|
|
1a1742310e | ||
|
|
5e6640b5a5 | ||
|
|
3b52d587e9 | ||
|
|
b0a4fdd958 | ||
|
|
e32c7b9390 | ||
|
|
cec5ffeb2e | ||
|
|
4bf27c6002 | ||
|
|
b275a14c4f | ||
|
|
a1b4fbf7b8 | ||
|
|
588895c9a2 | ||
|
|
33279d20a9 | ||
|
|
b4079cbf50 | ||
|
|
1a5d47a48d | ||
|
|
1aa0923aa9 | ||
|
|
eeed2f7035 | ||
|
|
1c9ce3f30d | ||
|
|
de13f67741 | ||
|
|
287884436e | ||
|
|
99166c9480 | ||
|
|
c7926d5882 | ||
|
|
81b4492243 | ||
|
|
20b273be13 | ||
|
|
2ea6681b97 | ||
|
|
0d00aef7ca | ||
|
|
11361f2829 | ||
|
|
8671162f50 | ||
|
|
6f64a032a9 | ||
|
|
ee861cd0a8 | ||
|
|
7784afe779 | ||
|
|
798573a77d | ||
|
|
2df23b033b | ||
|
|
14fb7a76aa | ||
|
|
511f246fd3 | ||
|
|
c30b353938 | ||
|
|
526407d670 | ||
|
|
adfddf569c | ||
|
|
95af0b2666 | ||
|
|
93974c3a8f | ||
|
|
c8b1b4ef21 | ||
|
|
6fd4fc7d5e | ||
|
|
e8c9b66746 | ||
|
|
8d924d669c | ||
|
|
fa12b4694e | ||
|
|
4f603f9893 | ||
|
|
7a8f2de433 | ||
|
|
00e2b9bcd9 | ||
|
|
30f9385e70 | ||
|
|
b0372e03b6 | ||
|
|
959f6791a0 | ||
|
|
8d9c9979bb | ||
|
|
dcac456eb4 | ||
|
|
d0c826c21d | ||
|
|
97b3758124 | ||
|
|
4404863047 | ||
|
|
f3634d56e9 | ||
|
|
419865eabf | ||
|
|
ab5257846a | ||
|
|
669fa493d4 | ||
|
|
b0c099bfb8 | ||
|
|
a933dd7918 | ||
|
|
2c06b2a96c | ||
|
|
336957426e | ||
|
|
41e27cf9a4 | ||
|
|
72df1ad55d | ||
|
|
72850410dd | ||
|
|
df82b919e8 | ||
|
|
1b6a05748f | ||
|
|
802290f6e8 | ||
|
|
93ab2dcdd5 | ||
|
|
1484cf7354 | ||
|
|
32ddba55c1 | ||
|
|
27a8726ef6 | ||
|
|
11fbf73169 | ||
|
|
fb9cffe521 | ||
|
|
b6c6a18a13 | ||
|
|
59226ee76b | ||
|
|
90d46067d3 | ||
|
|
f99192008d | ||
|
|
433dbed3f4 | ||
|
|
a9efef2791 | ||
|
|
31bba50dd2 | ||
|
|
2fed985923 | ||
|
|
7fd1422f99 | ||
|
|
fa64d8979b | ||
|
|
1bc5254c1e | ||
|
|
43ef43de86 | ||
|
|
cd7111b2d0 | ||
|
|
93b7d20019 | ||
|
|
54d0d0a5cd | ||
|
|
995d52bf2c | ||
|
|
813ba1d83f | ||
|
|
fb84da65fc | ||
|
|
7787259feb | ||
|
|
c002e06639 | ||
|
|
647c777f4b | ||
|
|
8b5cab7353 | ||
|
|
a3540f9e9d | ||
|
|
c2bbc4c26b | ||
|
|
80d1b003fc | ||
|
|
5cb099e766 | ||
|
|
4b17269625 | ||
|
|
4239717a3e | ||
|
|
5abdaf3d05 | ||
|
|
fb3e6b2a7f | ||
|
|
99f7fd410b | ||
|
|
d0649f56c5 | ||
|
|
d0d5e9d523 | ||
|
|
5775a1b0ca | ||
|
|
3cf3c40dfc | ||
|
|
f3e9e3aa39 | ||
|
|
b01a878acd | ||
|
|
f21575e4a0 | ||
|
|
fe4bade05e | ||
|
|
e432763f51 | ||
|
|
4013fbc50f | ||
|
|
4b17bedaef | ||
|
|
147199e3cc | ||
|
|
f5e2f6de12 | ||
|
|
81a34c36b4 | ||
|
|
247e7b41a8 | ||
|
|
c9e5356047 | ||
|
|
9767ef93f3 | ||
|
|
74ba8baa50 | ||
|
|
043f987600 | ||
|
|
3bc3aaa171 | ||
|
|
6c2d9d5a82 | ||
|
|
91d6405c1a | ||
|
|
d797b39f28 | ||
|
|
a374e55ccb | ||
|
|
fbec35ba8b | ||
|
|
7104c6937c | ||
|
|
ac5613c453 | ||
|
|
826e800f0a | ||
|
|
496138f28e | ||
|
|
0d1d61225f | ||
|
|
e24c54451c | ||
|
|
9e7b5b9075 | ||
|
|
f1d33f6cd6 | ||
|
|
6809dee2a5 | ||
|
|
86be74997a | ||
|
|
762d3d2018 | ||
|
|
5f786b86c2 | ||
|
|
b8a7994b28 | ||
|
|
c57ec32d50 | ||
|
|
8765e07bed | ||
|
|
635be79b3a | ||
|
|
5b1df675d8 | ||
|
|
525521f5d8 | ||
|
|
743d34b056 | ||
|
|
c22f2620b6 | ||
|
|
66a40b563c | ||
|
|
bc2779b850 | ||
|
|
339dd19b05 | ||
|
|
c9a3f915c5 | ||
|
|
2eb8cb4074 | ||
|
|
2415ca968a | ||
|
|
acb0f1829c | ||
|
|
2ec8018c67 | ||
|
|
ddbed0517f | ||
|
|
998ad6cc05 | ||
|
|
97b6aae19a | ||
|
|
981029bb0a | ||
|
|
e4d5149278 | ||
|
|
5ec4cfba67 | ||
|
|
d75881044e | ||
|
|
82a51f57b4 | ||
|
|
5bcd401cc5 | ||
|
|
6d8303fd58 | ||
|
|
e7a60d7b7b | ||
|
|
241ca837db | ||
|
|
ca5c8061c0 | ||
|
|
d24d01e413 | ||
|
|
205346a81d | ||
|
|
6899e600b1 | ||
|
|
d863b0e0cd | ||
|
|
3c28d1f7f1 | ||
|
|
30025c0cbf | ||
|
|
1d83ba7364 | ||
|
|
71e81f9fc8 | ||
|
|
9ea960fc2b | ||
|
|
1f8c8f6397 | ||
|
|
139e7d0c03 | ||
|
|
dc40f3cd24 | ||
|
|
cddcaa33ff | ||
|
|
0af1425c36 | ||
|
|
481f428da7 | ||
|
|
c9c8158c27 | ||
|
|
bb1f2c7633 | ||
|
|
e8b6a33bca | ||
|
|
e21b16c9a4 | ||
|
|
726f64bd01 | ||
|
|
3893b7f44d | ||
|
|
2301b48bcc | ||
|
|
3787138fb2 | ||
|
|
cff233c93e | ||
|
|
60ebb2eb3b | ||
|
|
c4a50cb804 | ||
|
|
44925727e5 | ||
|
|
392e4fdfef | ||
|
|
1ac110cf71 | ||
|
|
828854b404 | ||
|
|
0866f15bd5 | ||
|
|
5280455637 | ||
|
|
d98164a330 | ||
|
|
5146a78cb9 | ||
|
|
e19f119a09 | ||
|
|
82cd8461c9 | ||
|
|
6dc47973a5 | ||
|
|
e59b2b7b55 | ||
|
|
ed5ec02649 | ||
|
|
1f96cd36b2 | ||
|
|
304d5a1563 | ||
|
|
98e27ac48b | ||
|
|
bacde00ccd | ||
|
|
95999ab989 | ||
|
|
1532f25141 | ||
|
|
947988e09f | ||
|
|
62a6a6d8fc | ||
|
|
e993407d35 | ||
|
|
2f05a453f6 | ||
|
|
70f6621044 | ||
|
|
43080bc486 | ||
|
|
0d01f2b3f1 | ||
|
|
4c7eba2639 | ||
|
|
600f36edd4 | ||
|
|
e10bcff97d | ||
|
|
25c2c0b169 | ||
|
|
c39f6d7ce6 | ||
|
|
2dc1a342c5 | ||
|
|
1b5d668c38 | ||
|
|
6d4f482b51 | ||
|
|
2e93c8637c | ||
|
|
7e55c55c37 | ||
|
|
c9e8ee71e3 | ||
|
|
579065b08f | ||
|
|
648937189a | ||
|
|
ccc7ef095e | ||
|
|
3e4a33f8fe | ||
|
|
533045c0ff | ||
|
|
1a23d55d33 | ||
|
|
cc66332a4f | ||
|
|
66bcdfbbec | ||
|
|
6904fa694c | ||
|
|
614e1ef049 | ||
|
|
2cefc3f1d8 | ||
|
|
6fd8359a8b | ||
|
|
53bf3b0703 | ||
|
|
5447fcb7ba | ||
|
|
de0c2fb759 | ||
|
|
0b073d70bd | ||
|
|
f5e0536168 | ||
|
|
ec325f82ec | ||
|
|
302cace1dc | ||
|
|
46430bf3f5 | ||
|
|
b5df190a35 | ||
|
|
721125ac95 | ||
|
|
ab9ece68bc | ||
|
|
f2a6e58a2e | ||
|
|
603f33aa26 | ||
|
|
1ab6b0ea08 | ||
|
|
1d2fd41cb2 | ||
|
|
b111628b01 | ||
|
|
a4f74757d7 | ||
|
|
c0099271d2 | ||
|
|
cc96609323 | ||
|
|
b928ba05a4 | ||
|
|
0eaa794c9f | ||
|
|
4a7e07dc86 | ||
|
|
30fc0b1a10 | ||
|
|
2d55773119 | ||
|
|
eb2c9e97af | ||
|
|
20f7237cf2 | ||
|
|
ae3c41337e | ||
|
|
07f090640a | ||
|
|
f8d02429ca | ||
|
|
189018f2e5 | ||
|
|
608bc4b93f | ||
|
|
a3ebe72265 | ||
|
|
99fc47a3c0 | ||
|
|
8e97a0dfee | ||
|
|
49652579bc | ||
|
|
fe5fd89a86 | ||
|
|
4798c5a98a | ||
|
|
fc378149ee | ||
|
|
8ae6cd654e | ||
|
|
07633ef73e | ||
|
|
9422f8ece5 | ||
|
|
b017a68268 | ||
|
|
c5182bb038 | ||
|
|
710ed88e05 | ||
|
|
0b8c8df913 | ||
|
|
d1d410641e | ||
|
|
7f6ea37400 | ||
|
|
634508fc8e | ||
|
|
55c68b7955 | ||
|
|
49984f68e7 | ||
|
|
bd9f53ac4e | ||
|
|
2918a228ce | ||
|
|
70dbf35386 | ||
|
|
3421c98e30 | ||
|
|
b19885110c | ||
|
|
dfc04155fc | ||
|
|
3154973f22 | ||
|
|
a73f6262f8 | ||
|
|
28804d8d7f | ||
|
|
d2460fa3e2 | ||
|
|
ea7527983a | ||
|
|
587020d37d | ||
|
|
a95404c459 | ||
|
|
831c055211 | ||
|
|
c0bf4dc4c4 | ||
|
|
669963e74e | ||
|
|
35ed7ca879 | ||
|
|
4347790ea7 | ||
|
|
890ebdcc1a | ||
|
|
d665e09c1e | ||
|
|
41a44f9d01 | ||
|
|
1ca91a6288 | ||
|
|
6d6d077478 | ||
|
|
769b82e14c | ||
|
|
3378f237f9 | ||
|
|
e29da3c5db | ||
|
|
fc8e260b88 | ||
|
|
349838b849 | ||
|
|
1ccec3d1ca | ||
|
|
c294e6ad88 | ||
|
|
0def20088a | ||
|
|
74bcedebda | ||
|
|
958add8da7 | ||
|
|
acb9513544 | ||
|
|
ba4e489369 | ||
|
|
d6bb21d585 | ||
|
|
d5ffa7309c | ||
|
|
b4810a355f | ||
|
|
4892db2843 | ||
|
|
d5d5455768 | ||
|
|
6e0a03a9e5 | ||
|
|
5494009cee | ||
|
|
87ffc2368f | ||
|
|
da831145ed | ||
|
|
3b75307565 | ||
|
|
df029a9827 | ||
|
|
c4479ee021 | ||
|
|
77c966aee4 | ||
|
|
639bc9b94b | ||
|
|
14a1b67f78 | ||
|
|
ba362ba003 | ||
|
|
1dcb6fdd25 | ||
|
|
fc0f3eb144 | ||
|
|
f65829e161 | ||
|
|
18a84ff091 | ||
|
|
21bcc46015 | ||
|
|
d081cf30d3 | ||
|
|
d40d895ab5 | ||
|
|
b0e69b494e | ||
|
|
4d8e2f74ea | ||
|
|
bee3be97fc | ||
|
|
d1f14f0f76 | ||
|
|
f7a7d63d91 | ||
|
|
2ee42c7230 | ||
|
|
fad3fe18cb | ||
|
|
e0ba049931 | ||
|
|
68e4900abe |
@@ -1,2 +1,3 @@
|
||||
*
|
||||
!bin/matchbox
|
||||
bin/
|
||||
_output/
|
||||
|
||||
|
||||
18
.github/dependabot.yaml
vendored
Normal file
18
.github/dependabot.yaml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: gomod
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
- package-ecosystem: docker
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
- package-ecosystem: pip
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
12
.github/release.yaml
vendored
Normal file
12
.github/release.yaml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
changelog:
|
||||
categories:
|
||||
- title: Contributions
|
||||
labels:
|
||||
- '*'
|
||||
exclude:
|
||||
labels:
|
||||
- dependencies
|
||||
- no-release-note
|
||||
- title: Dependencies
|
||||
labels:
|
||||
- dependencies
|
||||
15
.github/workflows/build.yaml
vendored
Normal file
15
.github/workflows/build.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
go:
|
||||
uses: poseidon/.github/.github/workflows/golang-library.yaml@main
|
||||
multiarch:
|
||||
uses: poseidon/fleetlock/.github/workflows/multiarch.yaml@main
|
||||
secrets:
|
||||
QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }}
|
||||
26
.github/workflows/mkdocs-pages.yaml
vendored
Normal file
26
.github/workflows/mkdocs-pages.yaml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: mkdocs-pages
|
||||
on:
|
||||
workflow_call:
|
||||
jobs:
|
||||
publish:
|
||||
name: publish
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Checkout repo to GitHub Actions runner
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Install Python
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.x
|
||||
|
||||
# Install PyPI packages
|
||||
- name: Dependencies
|
||||
run: pip install -r requirements.txt
|
||||
|
||||
# Push to GitHub Pages
|
||||
- name: Push Docs
|
||||
run: |
|
||||
mkdocs gh-deploy --force
|
||||
13
.github/workflows/publish.yaml
vendored
Normal file
13
.github/workflows/publish.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: publish
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- release-docs
|
||||
jobs:
|
||||
mkdocs:
|
||||
name: mkdocs
|
||||
uses: ./.github/workflows/mkdocs-pages.yaml
|
||||
# Add content write for GitHub Pages
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
31
.github/workflows/test.yaml
vendored
31
.github/workflows/test.yaml
vendored
@@ -1,31 +0,0 @@
|
||||
name: test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
build:
|
||||
name: go
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
go: ['1.13', '1.14', '1.15']
|
||||
steps:
|
||||
- name: setup
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{matrix.go}}
|
||||
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: tools
|
||||
run: GO111MODULE=off go get golang.org/x/lint/golint
|
||||
|
||||
- name: test
|
||||
run: make
|
||||
|
||||
49
CHANGES.md
49
CHANGES.md
@@ -4,6 +4,53 @@ Notable changes between releases.
|
||||
|
||||
## Latest
|
||||
|
||||
## v0.11.0
|
||||
|
||||
* Update butane from v0.17.0 to v0.18.0 ([#1079](https://github.com/poseidon/matchbox/pull/1079))
|
||||
* Add support for `fcos` [v1.5.0](https://coreos.github.io/butane/config-fcos-v1_5/) Butane Configs
|
||||
* Add support for `flatcar` [v1.1.0](https://coreos.github.io/butane/config-flatcar-v1_1/) Butane Configs
|
||||
* Render Ignition as Ignition spec [v3.4.0](https://coreos.github.io/ignition/configuration-v3_4/)
|
||||
|
||||
## v0.10.0
|
||||
|
||||
* Remove support for Ignition v0.35.0 (Ignition spec v2.x)
|
||||
* Remove support for Container Linux Configs (**action required**)
|
||||
* Container Linux Configs were a YAML format that rendered to Ignition (spec v2.x)
|
||||
* Flatcar Linux now supports Ignition v2 (spec v3.x)
|
||||
* Butane is a suitable YAML format that renders Ignition v2 (spec v3.x)
|
||||
* Upgrade Ignition from v0.35.0 (spec v2.x) to v2.14.0 (spec v3.x)
|
||||
* Update Go version (v1.20.2) and alpine base image (v3.17.3)
|
||||
* Add limited support for Matchbox rendering Butane configs ([#997](https://github.com/poseidon/matchbox/pull/997)) ([docs](https://matchbox.psdn.io/ignition/#matchbox-rendering))
|
||||
* Recommend writing Butane via external tools (**action required**)
|
||||
* For Terraform, use [poseidon/terraform-provider-ct](https://github.com/poseidon/terraform-provider-ct)
|
||||
* For a CLI, use [`butane`](https://github.com/coreos/butane)
|
||||
* Parse Ignition and render forward to Ignition v2 (spec v3.3)
|
||||
* Ignition is [forward](https://github.com/coreos/ignition/blob/main/config/v3_3/config.go#L61) compatible (e.g. a `v3.1` spec can be rendered as `v3.3` safely)
|
||||
|
||||
If you still template Container Linux Configs via Matchbox, [migrate](https://www.flatcar.org/docs/latest/provisioning/config-transpiler/) to Butane by prepending:
|
||||
|
||||
```yaml
|
||||
variant: flatcar
|
||||
version: 1.0.0
|
||||
```
|
||||
|
||||
## v0.9.1
|
||||
|
||||
* Add dependabot Go module update automation ([#833](https://github.com/poseidon/matchbox/pull/833))
|
||||
* Build multi-arch container images (amd64, arm64) ([#823](https://github.com/poseidon/matchbox/pull/823))
|
||||
* Update Go version (v1.18.4) and alpine base image (v3.16.1)
|
||||
* Move `dnsmasq` container image to its own [repo](https://github.com/poseidon/dnsmasq) ([#840](https://github.com/poseidon/matchbox/pull/840))
|
||||
* Deprecate rendering Container Linux Configs
|
||||
* Please migrate to serving CoreOS Ignition directly
|
||||
* Use tools like [poseidon/ct](https://github.com/poseidon/terraform-provider-ct) or [butane](https://coreos.github.io/butane/getting-started/) to validate and convert a Butane Config (`focs` or `flatcar`) to Ignition (for Matchbox to serve)
|
||||
|
||||
### Docs/Examples
|
||||
|
||||
* Migrate docs website to GitHub Pages ([#976](https://github.com/poseidon/matchbox/pull/976))
|
||||
* Update Fedora CoreOS images and configuration ([#972](https://github.com/poseidon/matchbox/pull/972))
|
||||
* Update Fedora CoreOS initrd karg for UEFI ([#978](https://github.com/poseidon/matchbox/pull/978))
|
||||
* Update Flatcar Linux examples to use Ignition v3.3.0 ([#980](https://github.com/poseidon/matchbox/pull/980))
|
||||
|
||||
## v0.9.0
|
||||
|
||||
* Refresh docs and examples for Fedora CoreOS and Flatcar Linux ([#815](https://github.com/poseidon/matchbox/pull/815), [#816](https://github.com/poseidon/matchbox/pull/816))
|
||||
@@ -191,7 +238,7 @@ Note: Release signing key [has changed](https://github.com/poseidon/matchbox/blo
|
||||
* Allow Fuze YAML template files for Ignition 2.0.0 (#141)
|
||||
* Stop requiring Ignition templates to use file extensions (#176)
|
||||
* Logging Improvements:
|
||||
* Add structured loggging with Logrus (#254, #268)
|
||||
* Add structured logging with Logrus (#254, #268)
|
||||
* Log requests for bootcfg assets (#214)
|
||||
* Show `bootcfg` message at the home path `/`
|
||||
* Fix http package log messages (#173)
|
||||
|
||||
12
Dockerfile
12
Dockerfile
@@ -1,5 +1,13 @@
|
||||
FROM docker.io/alpine:3.12
|
||||
FROM docker.io/golang:1.22.1 AS builder
|
||||
COPY . src
|
||||
RUN cd src && make build
|
||||
|
||||
FROM docker.io/alpine:3.19.1
|
||||
LABEL maintainer="Dalton Hubble <dghubble@gmail.com>"
|
||||
COPY bin/matchbox /matchbox
|
||||
LABEL org.opencontainers.image.title="Matchbox",
|
||||
LABEL org.opencontainers.image.source="https://github.com/poseidon/matchbox"
|
||||
LABEL org.opencontainers.image.documentation="https://matchbox.psdn.io"
|
||||
LABEL org.opencontainers.image.vendor="Poseidon Labs"
|
||||
COPY --from=builder /go/src/bin/matchbox /matchbox
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["/matchbox"]
|
||||
|
||||
73
Makefile
73
Makefile
@@ -1,6 +1,4 @@
|
||||
export CGO_ENABLED:=0
|
||||
export GO111MODULE=on
|
||||
export GOFLAGS=-mod=vendor
|
||||
|
||||
DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
|
||||
VERSION=$(shell git describe --tags --match=v* --always --dirty)
|
||||
@@ -11,13 +9,11 @@ LOCAL_REPO=poseidon/matchbox
|
||||
IMAGE_REPO=quay.io/poseidon/matchbox
|
||||
|
||||
.PHONY: all
|
||||
all: build test vet lint fmt
|
||||
all: build test vet fmt
|
||||
|
||||
.PHONY: build
|
||||
build: clean bin/matchbox
|
||||
|
||||
bin/%:
|
||||
@go build -o bin/$* -ldflags $(LD_FLAGS) $(REPO)/cmd/$*
|
||||
build:
|
||||
@go build -o bin/matchbox -ldflags $(LD_FLAGS) $(REPO)/cmd/matchbox
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
@@ -27,34 +23,39 @@ test:
|
||||
vet:
|
||||
@go vet -all ./...
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@golint -set_exit_status `go list ./... | grep -v pb`
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@test -z $$(go fmt ./...)
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@golangci-lint run ./...
|
||||
|
||||
.PHONY: image
|
||||
image:
|
||||
@buildah bud -t $(LOCAL_REPO):$(VERSION) .
|
||||
@buildah tag $(LOCAL_REPO):$(VERSION) $(LOCAL_REPO):latest
|
||||
image: \
|
||||
image-amd64 \
|
||||
image-arm64
|
||||
|
||||
.PHONY: push
|
||||
push:
|
||||
@buildah tag $(LOCAL_REPO):$(VERSION) $(IMAGE_REPO):$(VERSION)
|
||||
@buildah tag $(LOCAL_REPO):$(VERSION) $(IMAGE_REPO):latest
|
||||
@buildah push docker://$(IMAGE_REPO):$(VERSION)
|
||||
@buildah push docker://$(IMAGE_REPO):latest
|
||||
image-%:
|
||||
buildah bud -f Dockerfile \
|
||||
-t $(LOCAL_REPO):$(VERSION)-$* \
|
||||
--arch $* --override-arch $* \
|
||||
--format=docker .
|
||||
|
||||
.PHONY: update
|
||||
update:
|
||||
@GOFLAGS="" go get -u
|
||||
@go mod tidy
|
||||
push: \
|
||||
push-amd64
|
||||
push-arm64
|
||||
|
||||
.PHONY: vendor
|
||||
vendor:
|
||||
@go mod vendor
|
||||
push-%:
|
||||
buildah tag $(LOCAL_REPO):$(VERSION)-$* $(IMAGE_REPO):$(VERSION)-$*
|
||||
buildah push --format v2s2 $(IMAGE_REPO):$(VERSION)-$*
|
||||
|
||||
manifest:
|
||||
buildah manifest create $(IMAGE_REPO):$(VERSION)
|
||||
buildah manifest add $(IMAGE_REPO):$(VERSION) docker://$(IMAGE_REPO):$(VERSION)-amd64
|
||||
buildah manifest add --variant v8 $(IMAGE_REPO):$(VERSION) docker://$(IMAGE_REPO):$(VERSION)-arm64
|
||||
buildah manifest inspect $(IMAGE_REPO):$(VERSION)
|
||||
buildah manifest push -f v2s2 $(IMAGE_REPO):$(VERSION) docker://$(IMAGE_REPO):$(VERSION)
|
||||
|
||||
protoc/%:
|
||||
podman run --security-opt label=disable \
|
||||
@@ -80,12 +81,14 @@ release: \
|
||||
_output/matchbox-linux-amd64.tar.gz \
|
||||
_output/matchbox-linux-arm.tar.gz \
|
||||
_output/matchbox-linux-arm64.tar.gz \
|
||||
_output/matchbox-darwin-amd64.tar.gz
|
||||
_output/matchbox-darwin-amd64.tar.gz \
|
||||
_output/matchbox-darwin-arm64.tar.gz
|
||||
|
||||
bin/linux-amd64/matchbox: GOARGS = GOOS=linux GOARCH=amd64
|
||||
bin/linux-arm/matchbox: GOARGS = GOOS=linux GOARCH=arm GOARM=6
|
||||
bin/linux-arm64/matchbox: GOARGS = GOOS=linux GOARCH=arm64
|
||||
bin/darwin-amd64/matchbox: GOARGS = GOOS=darwin GOARCH=amd64
|
||||
bin/darwin-arm64/matchbox: GOARGS = GOOS=darwin GOARCH=arm64
|
||||
bin/linux-ppc64le/matchbox: GOARGS = GOOS=linux GOARCH=ppc64le
|
||||
|
||||
bin/%/matchbox:
|
||||
@@ -102,3 +105,17 @@ _output/matchbox-%.tar.gz: bin/%/matchbox
|
||||
.PHONY: all build clean test release
|
||||
.SECONDARY: _output/matchbox-linux-amd64 _output/matchbox-darwin-amd64
|
||||
|
||||
release-sign:
|
||||
gpg2 --armor --detach-sign _output/matchbox-$(VERSION)-linux-amd64.tar.gz
|
||||
gpg2 --armor --detach-sign _output/matchbox-$(VERSION)-linux-arm.tar.gz
|
||||
gpg2 --armor --detach-sign _output/matchbox-$(VERSION)-linux-arm64.tar.gz
|
||||
gpg2 --armor --detach-sign _output/matchbox-$(VERSION)-darwin-amd64.tar.gz
|
||||
gpg2 --armor --detach-sign _output/matchbox-$(VERSION)-darwin-arm64.tar.gz
|
||||
|
||||
release-verify: NAME=_output/matchbox
|
||||
release-verify:
|
||||
gpg2 --verify $(NAME)-$(VERSION)-linux-amd64.tar.gz.asc $(NAME)-$(VERSION)-linux-amd64.tar.gz
|
||||
gpg2 --verify $(NAME)-$(VERSION)-linux-arm.tar.gz.asc $(NAME)-$(VERSION)-linux-arm.tar.gz
|
||||
gpg2 --verify $(NAME)-$(VERSION)-linux-arm64.tar.gz.asc $(NAME)-$(VERSION)-linux-arm64.tar.gz
|
||||
gpg2 --verify $(NAME)-$(VERSION)-darwin-amd64.tar.gz.asc $(NAME)-$(VERSION)-darwin-amd64.tar.gz
|
||||
gpg2 --verify $(NAME)-$(VERSION)-darwin-arm64.tar.gz.asc $(NAME)-$(VERSION)-darwin-arm64.tar.gz
|
||||
|
||||
10
README.md
10
README.md
@@ -1,4 +1,10 @@
|
||||
# matchbox [](https://github.com/poseidon/matchbox/actions?query=workflow%3Atest+branch%3Amaster) [](https://godoc.org/github.com/poseidon/matchbox) [](https://quay.io/repository/poseidon/matchbox)
|
||||
# matchbox
|
||||
[](https://pkg.go.dev/github.com/poseidon/matchbox)
|
||||
[](https://quay.io/repository/poseidon/matchbox)
|
||||
[](https://github.com/poseidon/matchbox/actions/workflows/build.yaml?query=branch%3Amain)
|
||||

|
||||
[](https://github.com/sponsors/poseidon)
|
||||
[](https://fosstodon.org/@poseidon)
|
||||
|
||||
`matchbox` is a service that matches bare-metal machines to profiles that PXE boot and provision clusters. Machines are matched by labels like MAC or UUID during PXE and profiles specify a kernel/initrd, iPXE config, and Ignition config.
|
||||
|
||||
@@ -12,7 +18,7 @@
|
||||
|
||||
* [Docs](https://matchbox.psdn.io/)
|
||||
* [Configuration](docs/config.md)
|
||||
* [HTTP API](docs/api-http.md) / [gRPC API](docs/grpc-api.md)
|
||||
* [HTTP API](docs/api-http.md) / [gRPC API](docs/api-grpc.md)
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# dnsmasq
|
||||
|
||||
Notable changes image releases. The dnsmasq project [upstream](http://www.thekelleys.org.uk/dnsmasq/doc.html) has its own [changelog](http://www.thekelleys.org.uk/dnsmasq/CHANGELOG).
|
||||
|
||||
## v0.4.1
|
||||
|
||||
* Rebuild with alpine:3.6 base image
|
||||
* Add EXPOSE ports 67 and 69 to Dockerfile
|
||||
|
||||
## v0.4.0
|
||||
|
||||
* `dnsmasq` package version 2.76
|
||||
* Rebuild with alpine:3.5 base image to receive patches
|
||||
* Update CoreOS `grub.efi` to be recent (stable, 1298.7.0)
|
||||
|
||||
## v0.3.0
|
||||
|
||||
* `dnsmasq` package version 2.75
|
||||
@@ -1,6 +0,0 @@
|
||||
FROM docker.io/alpine:3.12
|
||||
LABEL maintainer="Dalton Hubble <dghubble@gmail.com>"
|
||||
RUN apk -U add dnsmasq curl
|
||||
COPY tftpboot /var/lib/tftpboot
|
||||
EXPOSE 53 67 69
|
||||
ENTRYPOINT ["/usr/sbin/dnsmasq"]
|
||||
@@ -1,24 +0,0 @@
|
||||
DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
|
||||
VERSION=$(shell git rev-parse HEAD)
|
||||
|
||||
LOCAL_REPO=poseidon/dnsmasq
|
||||
IMAGE_REPO=quay.io/poseidon/dnsmasq
|
||||
|
||||
.PHONY: all
|
||||
all: image
|
||||
|
||||
.PHONY: tftp
|
||||
tftp:
|
||||
@$(DIR)/get-tftp-files
|
||||
|
||||
.PHONY: image
|
||||
image: tftp
|
||||
@buildah bud -t $(LOCAL_REPO):$(VERSION) .
|
||||
@buildah tag $(LOCAL_REPO):$(VERSION) $(LOCAL_REPO):latest
|
||||
|
||||
.PHONY: push
|
||||
push:
|
||||
@buildah tag $(LOCAL_REPO):$(VERSION) $(IMAGE_REPO):$(VERSION)
|
||||
@buildah tag $(LOCAL_REPO):$(VERSION) $(IMAGE_REPO):latest
|
||||
@buildah push docker://$(IMAGE_REPO):$(VERSION)
|
||||
@buildah push docker://$(IMAGE_REPO):latest
|
||||
@@ -1,57 +1,4 @@
|
||||
# dnsmasq [](https://quay.io/repository/poseidon/dnsmasq)
|
||||
# dnsmasq
|
||||
|
||||
`dnsmasq` provides a container image for running DHCP, proxy DHCP, DNS, and/or TFTP with [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html). Use it to test different network setups with clusters of network bootable machines.
|
||||
|
||||
The image bundles `undionly.kpxe`, `ipxe.efi`, and `grub.efi` (experimental) for chainloading BIOS and UEFI clients to iPXE.
|
||||
|
||||
## Usage
|
||||
|
||||
Run the container image as a DHCP, DNS, and TFTP service.
|
||||
|
||||
```sh
|
||||
sudo docker run --rm --cap-add=NET_ADMIN --net=host quay.io/poseidon/dnsmasq \
|
||||
-d -q \
|
||||
--dhcp-range=192.168.1.3,192.168.1.254 \
|
||||
--enable-tftp --tftp-root=/var/lib/tftpboot \
|
||||
--dhcp-match=set:bios,option:client-arch,0 \
|
||||
--dhcp-boot=tag:bios,undionly.kpxe \
|
||||
--dhcp-match=set:efi32,option:client-arch,6 \
|
||||
--dhcp-boot=tag:efi32,ipxe.efi \
|
||||
--dhcp-match=set:efibc,option:client-arch,7 \
|
||||
--dhcp-boot=tag:efibc,ipxe.efi \
|
||||
--dhcp-match=set:efi64,option:client-arch,9 \
|
||||
--dhcp-boot=tag:efi64,ipxe.efi \
|
||||
--dhcp-userclass=set:ipxe,iPXE \
|
||||
--dhcp-boot=tag:ipxe,http://matchbox.example.com:8080/boot.ipxe \
|
||||
--address=/matchbox.example.com/192.168.1.2 \
|
||||
--log-queries \
|
||||
--log-dhcp
|
||||
```
|
||||
|
||||
Press ctrl-C to stop the Docker container.
|
||||
|
||||
## Configuration Flags
|
||||
|
||||
Configuration arguments can be provided as flags. Check the dnsmasq [man pages](http://www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html) for a complete list.
|
||||
|
||||
| flag | description | example |
|
||||
|----------|-------------|---------|
|
||||
| --dhcp-range | Enable DHCP, lease given range | `172.18.0.50,172.18.0.99`, `192.168.1.1,proxy,255.255.255.0` |
|
||||
| --dhcp-boot | DHCP next server option | `http://matchbox.foo:8080/boot.ipxe` |
|
||||
| --enable-tftp | Enable serving from tftp-root over TFTP | NA |
|
||||
| --address | IP address for a domain name | /matchbox.foo/172.18.0.2 |
|
||||
|
||||
## Development
|
||||
|
||||
Build a container image locally.
|
||||
|
||||
```
|
||||
make docker-image
|
||||
```
|
||||
|
||||
Run the image with Docker on the `docker0` bridge (default).
|
||||
|
||||
```
|
||||
sudo docker run --rm --cap-add=NET_ADMIN poseidon/dnsmasq -d -q
|
||||
```
|
||||
Moved to [dnsmasq](https://github.com/poseidon/dnsmasq).
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
DEST=${1:-"tftpboot"}
|
||||
|
||||
if [ ! -d $DEST ]; then
|
||||
echo "Creating directory $DEST"
|
||||
mkdir -p $DEST
|
||||
fi
|
||||
|
||||
curl -s -o $DEST/undionly.kpxe http://boot.ipxe.org/undionly.kpxe
|
||||
cp $DEST/undionly.kpxe $DEST/undionly.kpxe.0
|
||||
curl -s -o $DEST/ipxe.efi http://boot.ipxe.org/ipxe.efi
|
||||
@@ -20,7 +20,7 @@ spec:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: matchbox
|
||||
image: quay.io/poseidon/matchbox:v0.9.0
|
||||
image: quay.io/poseidon/matchbox:v0.10.0
|
||||
env:
|
||||
- name: MATCHBOX_ADDRESS
|
||||
value: "0.0.0.0:8080"
|
||||
|
||||
1
docs/CNAME
Normal file
1
docs/CNAME
Normal file
@@ -0,0 +1 @@
|
||||
matchbox.psdn.io
|
||||
@@ -101,9 +101,9 @@ coreos:
|
||||
command: start
|
||||
```
|
||||
|
||||
## Container Linux Config / Ignition Config
|
||||
## Ignition Config
|
||||
|
||||
Finds the profile matching the machine and renders the corresponding Ignition Config with group metadata, selectors, and query params.
|
||||
Finds the profile matching the machine and renders the corresponding Ignition for machine consumption.
|
||||
|
||||
```
|
||||
GET http://matchbox.foo/ignition?label=value
|
||||
@@ -121,11 +121,11 @@ GET http://matchbox.foo/ignition?label=value
|
||||
|
||||
```json
|
||||
{
|
||||
"ignition": { "version": "2.0.0" },
|
||||
"ignition": { "version": "3.3.0" },
|
||||
"systemd": {
|
||||
"units": [{
|
||||
"name": "example.service",
|
||||
"enable": true,
|
||||
"enabled": true,
|
||||
"contents": "[Service]\nType=oneshot\nExecStart=/usr/bin/echo Hello World\n\n[Install]\nWantedBy=multi-user.target"
|
||||
}]
|
||||
}
|
||||
@@ -191,7 +191,7 @@ REQUEST_RAW_QUERY=mac=52-54-00-a1-9c-ae&foo=bar&count=3&gate=true
|
||||
|
||||
## OpenPGP signatures
|
||||
|
||||
OpenPGPG signature endpoints serve detached binary and ASCII armored signatures of rendered configs, if enabled. See [OpenPGP Signing](openpgp.md).
|
||||
OpenPGP signature endpoints serve detached binary and ASCII armored signatures of rendered configs, if enabled. See [OpenPGP Signing](openpgp.md).
|
||||
|
||||
| Endpoint | Signature Endpoint | ASCII Signature Endpoint |
|
||||
|------------|--------------------|-------------------------|
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Cloud Config
|
||||
|
||||
!!! warning
|
||||
Migrate to [Container Linux Configs](container-linux-config.md). Cloud-Config support will be removed in the future.
|
||||
Migrate to [Ignition configs](ignition.md). Cloud-Config support will be removed in the future.
|
||||
|
||||
CoreOS Cloud-Config is a system for configuring machines with a Cloud-Config file or executable script from user-data. Cloud-Config runs in userspace on each boot and implements a subset of the [cloud-init spec](http://cloudinit.readthedocs.org/en/latest/topics/format.html#cloud-config-data). See the cloud-config [docs](https://coreos.com/os/docs/latest/cloud-config.html) for details.
|
||||
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
# Container Linux Configs
|
||||
|
||||
A Container Linux Config is a YAML document which declares how Container Linux instances' disks should be provisioned on network boot and first-boot from disk. Configs can declare disk partitions, write files (regular files, systemd units, networkd units, etc.), and configure users. See the Container Linux Config [spec](https://coreos.com/os/docs/latest/configuration.html).
|
||||
|
||||
### Ignition
|
||||
|
||||
Container Linux Configs are validated and converted to *machine-friendly* Ignition configs (JSON) by matchbox when serving to booting machines. [Ignition](https://coreos.com/ignition/docs/latest/), the provisioning utility shipped in Container Linux, will parse and execute the Ignition config to realize the desired configuration. Matchbox users usually only need to write Container Linux Configs.
|
||||
|
||||
*Note: Container Linux directory names are still named "ignition" for historical reasons as outlined below. A future breaking change will rename to "container-linux-config".*
|
||||
|
||||
## Adding Container Linux Configs
|
||||
|
||||
Container Linux Config templates can be added to the `/var/lib/matchbox/ignition` directory or in an `ignition` subdirectory of a custom `-data-path`. Template files may contain [Go template](https://golang.org/pkg/text/template/) elements which will be evaluated with group metadata, selectors, and query params.
|
||||
|
||||
```
|
||||
/var/lib/matchbox
|
||||
├── cloud
|
||||
├── ignition
|
||||
│ └── k8s-controller.yaml
|
||||
│ └── etcd.yaml
|
||||
│ └── k8s-worker.yaml
|
||||
│ └── raw.ign
|
||||
└── profiles
|
||||
```
|
||||
|
||||
## Referencing in Profiles
|
||||
|
||||
Profiles can include a Container Linux Config for provisioning machines. Specify the Container Linux Config in a [Profile](matchbox.md#profiles) with `ignition_id`. When PXE booting, use the kernel option `coreos.first_boot=1` and `coreos.config.url` to point to the `matchbox` [Ignition endpoint](api-http.md#ignition-config).
|
||||
|
||||
## Examples
|
||||
|
||||
Here is an example Container Linux Config template. Variables will be interpreted using group metadata, selectors, and query params. Matchbox will convert the config to Ignition to serve Container Linux machines.
|
||||
|
||||
ignition/format-disk.yaml.tmpl:
|
||||
|
||||
<!-- {% raw %} -->
|
||||
```yaml
|
||||
|
||||
---
|
||||
storage:
|
||||
disks:
|
||||
- device: /dev/sda
|
||||
wipe_table: true
|
||||
partitions:
|
||||
- label: ROOT
|
||||
filesystems:
|
||||
- name: root
|
||||
mount:
|
||||
device: "/dev/sda1"
|
||||
format: "ext4"
|
||||
create:
|
||||
force: true
|
||||
options:
|
||||
- "-LROOT"
|
||||
files:
|
||||
- filesystem: root
|
||||
path: /home/core/foo
|
||||
mode: 0644
|
||||
user:
|
||||
id: 500
|
||||
group:
|
||||
id: 500
|
||||
contents:
|
||||
inline: |
|
||||
{{.example_contents}}
|
||||
{{ if index . "ssh_authorized_keys" }}
|
||||
passwd:
|
||||
users:
|
||||
- name: core
|
||||
ssh_authorized_keys:
|
||||
{{ range $element := .ssh_authorized_keys }}
|
||||
- {{$element}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
```
|
||||
<!-- {% endraw %} -->
|
||||
|
||||
The Ignition config response (formatted) to a query `/ignition?label=value` for a Container Linux instance supporting Ignition 2.0.0 would be:
|
||||
|
||||
```json
|
||||
{
|
||||
"ignition": {
|
||||
"version": "2.0.0",
|
||||
"config": {}
|
||||
},
|
||||
"storage": {
|
||||
"disks": [
|
||||
{
|
||||
"device": "/dev/sda",
|
||||
"wipeTable": true,
|
||||
"partitions": [
|
||||
{
|
||||
"label": "ROOT",
|
||||
"number": 0,
|
||||
"size": 0,
|
||||
"start": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"filesystems": [
|
||||
{
|
||||
"name": "root",
|
||||
"mount": {
|
||||
"device": "/dev/sda1",
|
||||
"format": "ext4",
|
||||
"create": {
|
||||
"force": true,
|
||||
"options": [
|
||||
"-LROOT"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"files": [
|
||||
{
|
||||
"filesystem": "root",
|
||||
"path": "/home/core/foo",
|
||||
"contents": {
|
||||
"source": "data:,Example%20file%20contents%0A",
|
||||
"verification": {}
|
||||
},
|
||||
"mode": 420,
|
||||
"user": {
|
||||
"id": 500
|
||||
},
|
||||
"group": {
|
||||
"id": 500
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"systemd": {},
|
||||
"networkd": {},
|
||||
"passwd": {}
|
||||
}
|
||||
```
|
||||
|
||||
See [examples/ignition](../examples/ignition) for numerous Container Linux Config template examples.
|
||||
|
||||
### Raw Ignition
|
||||
|
||||
If you prefer to design your own templating solution, raw Ignition files (suffixed with `.ign` or `.ignition`) are served directly.
|
||||
@@ -17,23 +17,23 @@ Choose one of the supported installation options:
|
||||
Download the latest Matchbox [release](https://github.com/poseidon/matchbox/releases).
|
||||
|
||||
```sh
|
||||
$ wget https://github.com/poseidon/matchbox/releases/download/v0.9.0/matchbox-v0.9.0-linux-amd64.tar.gz
|
||||
$ wget https://github.com/poseidon/matchbox/releases/download/v0.9.0/matchbox-v0.9.0-linux-amd64.tar.gz.asc
|
||||
$ wget https://github.com/poseidon/matchbox/releases/download/v0.10.0/matchbox-v0.10.0-linux-amd64.tar.gz
|
||||
$ wget https://github.com/poseidon/matchbox/releases/download/v0.10.0/matchbox-v0.10.0-linux-amd64.tar.gz.asc
|
||||
```
|
||||
|
||||
Verify the release has been signed by Dalton Hubble's GPG [Key](https://keyserver.ubuntu.com/pks/lookup?search=0x8F515AD1602065C8&op=vindex)'s signing subkey.
|
||||
|
||||
```sh
|
||||
$ gpg --keyserver keyserver.ubuntu.com --recv-key 2E3D92BF07D9DDCCB3BAE4A48F515AD1602065C8
|
||||
$ gpg --verify matchbox-v0.9.0-linux-amd64.tar.gz.asc matchbox-v0.9.0-linux-amd64.tar.gz
|
||||
$ gpg --verify matchbox-v0.10.0-linux-amd64.tar.gz.asc matchbox-v0.10.0-linux-amd64.tar.gz
|
||||
gpg: Good signature from "Dalton Hubble <dghubble@gmail.com>"
|
||||
```
|
||||
|
||||
Untar the release.
|
||||
|
||||
```sh
|
||||
$ tar xzvf matchbox-v0.9.0-linux-amd64.tar.gz
|
||||
$ cd matchbox-v0.9.0-linux-amd64
|
||||
$ tar xzvf matchbox-v0.10.0-linux-amd64.tar.gz
|
||||
$ cd matchbox-v0.10.0-linux-amd64
|
||||
```
|
||||
|
||||
## Install
|
||||
@@ -110,14 +110,14 @@ Run the container image with Podman,
|
||||
|
||||
```
|
||||
mkdir -p /var/lib/matchbox/assets
|
||||
podman run --net=host --rm -v /var/lib/matchbox:/var/lib/matchbox:Z -v /etc/matchbox:/etc/matchbox:Z,ro quay.io/poseidon/matchbox:v0.9.0 -address=0.0.0.0:8080 -rpc-address=0.0.0.0:8081 -log-level=debug
|
||||
podman run --net=host --rm -v /var/lib/matchbox:/var/lib/matchbox:Z -v /etc/matchbox:/etc/matchbox:Z,ro quay.io/poseidon/matchbox:v0.10.0 -address=0.0.0.0:8080 -rpc-address=0.0.0.0:8081 -log-level=debug
|
||||
```
|
||||
|
||||
Or with Docker,
|
||||
|
||||
```
|
||||
mkdir -p /var/lib/matchbox/assets
|
||||
sudo docker run --net=host --rm -v /var/lib/matchbox:/var/lib/matchbox:Z -v /etc/matchbox:/etc/matchbox:Z,ro quay.io/poseidon/matchbox:v0.9.0 -address=0.0.0.0:8080 -rpc-address=0.0.0.0:8081 -log-level=debug
|
||||
sudo docker run --net=host --rm -v /var/lib/matchbox:/var/lib/matchbox:Z -v /etc/matchbox:/etc/matchbox:Z,ro quay.io/poseidon/matchbox:v0.10.0 -address=0.0.0.0:8080 -rpc-address=0.0.0.0:8081 -log-level=debug
|
||||
```
|
||||
|
||||
Create machine profiles, groups, or Ignition configs by adding files to `/var/lib/matchbox`.
|
||||
@@ -133,7 +133,7 @@ NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
matchbox 10.3.0.145 <none> 8080/TCP,8081/TCP 46m
|
||||
```
|
||||
|
||||
Example manifests in [contrib/k8s](../contrib/k8s) enable the gRPC API to allow client apps to update matchbox objects. Generate TLS server credentials for `matchbox-rpc.example.com` [as shown](#generate-tls-credentials) and create a Kubernetes secret. Alternately, edit the example manifests if you don't need the gRPC API enabled.
|
||||
Example manifests in [contrib/k8s](../contrib/k8s) enable the gRPC API to allow client apps to update matchbox objects. Generate TLS server certificates for `matchbox-rpc.example.com` [as shown](#generate-tls-certificates) and create a Kubernetes secret. Alternately, edit the example manifests if you don't need the gRPC API enabled.
|
||||
|
||||
```sh
|
||||
$ kubectl create secret generic matchbox-rpc --from-file=ca.crt --from-file=server.crt --from-file=server.key
|
||||
@@ -226,7 +226,7 @@ matchbox
|
||||
If you enabled the gRPC API,
|
||||
|
||||
```sh
|
||||
$ openssl s_client -connect matchbox.example.com:8081 -CAfile /etc/matchbox/ca.crt -cert scripts/tls/client.crt -key scripts/tls/client.key
|
||||
$ openssl s_client -connect matchbox.example.com:8081 -CAfile scripts/tls/ca.crt -cert scripts/tls/client.crt -key scripts/tls/client.key
|
||||
CONNECTED(00000003)
|
||||
depth=1 CN = fake-ca
|
||||
verify return:1
|
||||
@@ -247,22 +247,20 @@ Matchbox can serve OS images in development or lab environments to reduce bandwi
|
||||
Download a recent Fedora CoreOS or Flatcar Linux release.
|
||||
|
||||
```
|
||||
$ ./scripts/get-fedora-coreos stable 32.20200923.3.0 .
|
||||
$ ./scripts/get-flatcar stable 2605.6.0 .
|
||||
$ ./scripts/get-fedora-coreos stable 36.20220906.3.2 .
|
||||
$ ./scripts/get-flatcar stable 3227.2.0 .
|
||||
```
|
||||
|
||||
Move the images to `/var/lib/matchbox/assets`,
|
||||
|
||||
```
|
||||
/var/lib/matchbox/assets/fedora-coreos/
|
||||
├── fedora-coreos-32.20200923.3.0-live-initramfs.x86_64.img
|
||||
├── fedora-coreos-32.20200923.3.0-live-kernel-x86_64
|
||||
├── fedora-coreos-32.20200923.3.0-live-rootfs.x86_64.img
|
||||
├── fedora-coreos-32.20200923.3.0-metal.x86_64.raw.xz
|
||||
└── fedora-coreos-32.20200923.3.0-metal.x86_64.raw.xz.sig
|
||||
├── fedora-coreos-36.20220906.3.2-live-initramfs.x86_64.img
|
||||
├── fedora-coreos-36.20220906.3.2-live-kernel-x86_64
|
||||
├── fedora-coreos-36.20220906.3.2-live-rootfs.x86_64.img
|
||||
|
||||
/var/lib/matchbox/assets/flatcar/
|
||||
└── 2605.6.0
|
||||
└── 3227.2.0
|
||||
├── Flatcar_Image_Signing_Key.asc
|
||||
├── flatcar_production_image.bin.bz2
|
||||
├── flatcar_production_image.bin.bz2.sig
|
||||
@@ -273,7 +271,7 @@ Move the images to `/var/lib/matchbox/assets`,
|
||||
└── version.txt
|
||||
```
|
||||
|
||||
and verify the images are acessible.
|
||||
and verify the images are accessible.
|
||||
|
||||
```sh
|
||||
$ curl http://matchbox.example.com:8080/assets/fedora-coreos/
|
||||
|
||||
@@ -8,7 +8,7 @@ This guide covers releasing new versions of matchbox.
|
||||
Create a release commit which updates old version references.
|
||||
|
||||
```sh
|
||||
$ export VERSION=v0.9.0
|
||||
$ export VERSION=v0.10.0
|
||||
```
|
||||
|
||||
## Tag
|
||||
@@ -18,7 +18,7 @@ Tag, sign the release version, and push it to Github.
|
||||
```sh
|
||||
$ git tag -s vX.Y.Z -m 'vX.Y.Z'
|
||||
$ git push origin --tags
|
||||
$ git push origin master
|
||||
$ git push origin main
|
||||
```
|
||||
|
||||
## Images
|
||||
@@ -44,7 +44,7 @@ $ make release
|
||||
Verify the reported version.
|
||||
|
||||
```
|
||||
./_output/matchbox-v0.9.0-linux-amd64/matchbox -version
|
||||
./_output/matchbox-v0.10.0-linux-amd64/matchbox -version
|
||||
```
|
||||
|
||||
## Signing
|
||||
@@ -52,20 +52,13 @@ Verify the reported version.
|
||||
Release tarballs are signed by Dalton Hubble's GPG [Key](/docs/deployment.md#download)
|
||||
|
||||
```sh
|
||||
cd _output
|
||||
gpg2 --armor --detach-sign matchbox-$VERSION-linux-amd64.tar.gz
|
||||
gpg2 --armor --detach-sign matchbox-$VERSION-darwin-amd64.tar.gz
|
||||
gpg2 --armor --detach-sign matchbox-$VERSION-linux-arm.tar.gz
|
||||
gpg2 --armor --detach-sign matchbox-$VERSION-linux-arm64.tar.gz
|
||||
make release-sign
|
||||
```
|
||||
|
||||
Verify the signatures.
|
||||
|
||||
```sh
|
||||
gpg2 --verify matchbox-$VERSION-linux-amd64.tar.gz.asc matchbox-$VERSION-linux-amd64.tar.gz
|
||||
gpg2 --verify matchbox-$VERSION-darwin-amd64.tar.gz.asc matchbox-$VERSION-darwin-amd64.tar.gz
|
||||
gpg2 --verify matchbox-$VERSION-linux-arm.tar.gz.asc matchbox-$VERSION-linux-arm.tar.gz
|
||||
gpg2 --verify matchbox-$VERSION-linux-arm64.tar.gz.asc matchbox-$VERSION-linux-arm64.tar.gz
|
||||
make release-verify
|
||||
```
|
||||
|
||||
## Publish
|
||||
|
||||
@@ -29,8 +29,8 @@ $ cd matchbox
|
||||
Download Fedora CoreOS or Flatcar Linux image assets to `examples/assets`.
|
||||
|
||||
```sh
|
||||
$ ./scripts/get-fedora-coreos stable 32.20200923.3.0 ./examples/assets
|
||||
$ ./scripts/get-flatcar stable 2605.6.0 ./examples/assets
|
||||
$ ./scripts/get-fedora-coreos stable 36.20220906.3.2 ./examples/assets
|
||||
$ ./scripts/get-flatcar stable 3227.2.0 ./examples/assets
|
||||
```
|
||||
|
||||
For development convenience, add `/etc/hosts` entries for nodes so they may be referenced by name.
|
||||
@@ -82,13 +82,18 @@ Create QEMU/KVM VMs which have known hardware attributes. The nodes will be atta
|
||||
$ sudo ./scripts/libvirt create
|
||||
```
|
||||
|
||||
You can connect to the serial console of any node (ctrl+] to exit). If you provisioned nodes with an SSH key, you can SSH after bring-up.
|
||||
If you provisioned nodes with an SSH key, you can SSH after bring-up.
|
||||
|
||||
```sh
|
||||
$ sudo virsh console node1
|
||||
$ ssh core@node1.example.com
|
||||
```
|
||||
|
||||
If you set a `console=ttyS0` kernel arg, you can connect to the serial console of any node (ctrl+] to exit).
|
||||
|
||||
```
|
||||
$ sudo virsh console node1
|
||||
```
|
||||
|
||||
You can also use `virt-manager` to watch the console.
|
||||
|
||||
```sh
|
||||
|
||||
@@ -30,11 +30,11 @@ $ openssl s_client -connect matchbox.example.com:8081 \
|
||||
|
||||
## Terraform
|
||||
|
||||
Install [Terraform][https://www.terraform.io/downloads.html] v0.13+ on your system.
|
||||
Install [Terraform](https://www.terraform.io/downloads.html) v0.13+ on your system.
|
||||
|
||||
```sh
|
||||
$ terraform version
|
||||
Terraform v0.13.3
|
||||
Terraform v1.1.8
|
||||
```
|
||||
|
||||
### Examples
|
||||
@@ -79,11 +79,11 @@ terraform {
|
||||
required_providers {
|
||||
ct = {
|
||||
source = "poseidon/ct"
|
||||
version = "0.6.1"
|
||||
version = "0.10.0"
|
||||
}
|
||||
matchbox = {
|
||||
source = "poseidon/matchbox"
|
||||
version = "0.4.1"
|
||||
version = "0.5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,40 +91,33 @@ terraform {
|
||||
|
||||
### Profiles
|
||||
|
||||
Machine profiles specify the kernel, initrd, kernel args, Ignition Config, and other configs (e.g. templated Container Linux Config, Cloud-config, generic) used to network boot and provision a bare-metal machine. The profile below would PXE boot machines using a Fedora CoreOS kernel and initrd (see [assets](api-http.md#assets) to learn about caching for speed), perform a disk install, reboot (first boot from disk), and use a [Fedora CoreOS Config](https://github.com/coreos/fcct/blob/master/docs/configuration-v1_1.md) to generate an Ignition config to provision.
|
||||
Machine profiles specify the kernel, initrd, kernel args, Ignition Config, or other configs (e.g. templated Butane Config, generic) used to network boot and provision a bare-metal machine. The profile below would PXE boot machines using a Fedora CoreOS kernel and initrd (see [assets](api-http.md#assets) to learn about caching for speed), perform a disk install, reboot (first boot from disk), and use a [Fedora CoreOS Config](https://github.com/coreos/fcct/blob/master/docs/configuration-v1_1.md) to generate an Ignition config to provision.
|
||||
|
||||
```tf
|
||||
// Fedora CoreOS profile
|
||||
resource "matchbox_profile" "fedora-coreos-install" {
|
||||
name = "worker"
|
||||
kernel = "https://builds.coreos.fedoraproject.org/prod/streams/${var.os_stream}/builds/${var.os_version}/x86_64/fedora-coreos-${var.os_version}-live-kernel-x86_64"
|
||||
|
||||
initrd = [
|
||||
"https://builds.coreos.fedoraproject.org/prod/streams/${var.os_stream}/builds/${var.os_version}/x86_64/fedora-coreos-${var.os_version}-live-initramfs.x86_64.img",
|
||||
"https://builds.coreos.fedoraproject.org/prod/streams/${var.os_stream}/builds/${var.os_version}/x86_64/fedora-coreos-${var.os_version}-live-rootfs.x86_64.img"
|
||||
"--name main https://builds.coreos.fedoraproject.org/prod/streams/${var.os_stream}/builds/${var.os_version}/x86_64/fedora-coreos-${var.os_version}-live-initramfs.x86_64.img"
|
||||
]
|
||||
|
||||
args = [
|
||||
"rd.neednet=1",
|
||||
"initrd=main",
|
||||
"coreos.live.rootfs_url=https://builds.coreos.fedoraproject.org/prod/streams/${var.os_stream}/builds/${var.os_version}/x86_64/fedora-coreos-${var.os_version}-live-rootfs.x86_64.img",
|
||||
"coreos.inst.install_dev=/dev/sda",
|
||||
"coreos.inst.ignition_url=${var.matchbox_http_endpoint}/ignition?uuid=$${uuid}&mac=$${mac:hexhyp}",
|
||||
"coreos.inst.image_url=https://builds.coreos.fedoraproject.org/prod/streams/${var.os_stream}/builds/${var.os_version}/x86_64/fedora-coreos-${var.os_version}-metal.x86_64.raw.xz",
|
||||
"console=tty0",
|
||||
"console=ttyS0",
|
||||
"coreos.inst.ignition_url=${var.matchbox_http_endpoint}/ignition?uuid=$${uuid}&mac=$${mac:hexhyp}"
|
||||
]
|
||||
|
||||
raw_ignition = data.ct_config.worker-ignition.rendered
|
||||
raw_ignition = data.ct_config.worker.rendered
|
||||
}
|
||||
|
||||
data "ct_config" "worker-ignition" {
|
||||
content = data.template_file.worker-config.rendered
|
||||
strict = true
|
||||
}
|
||||
|
||||
data "template_file" "worker-config" {
|
||||
template = file("fcc/fedora-coreos.yaml")
|
||||
vars = {
|
||||
ssh_authorized_key = var.ssh_authorized_key
|
||||
}
|
||||
data "ct_config" "worker" {
|
||||
content = templatefile("fcc/fedora-coreos.yaml", {
|
||||
ssh_authorized_key = var.ssh_authorized_key
|
||||
})
|
||||
strict = true
|
||||
}
|
||||
```
|
||||
|
||||
@@ -150,8 +143,9 @@ cp terraform.tfvars.example terraform.tfvars
|
||||
|
||||
```tf
|
||||
matchbox_http_endpoint = "http://matchbox.example.com:8080"
|
||||
matchbox_rpc_endpoint = "matchbox.example.com:8081"
|
||||
ssh_authorized_key = "YOUR_SSH_KEY"
|
||||
matchbox_rpc_endpoint = "matchbox.example.com:8081"
|
||||
os_version = "36.20220906.3.2"
|
||||
ssh_authorized_key = "YOUR_SSH_KEY"
|
||||
```
|
||||
|
||||
### Apply
|
||||
@@ -194,7 +188,7 @@ If you've never setup a PXE-enabled network before or you're trying to setup a h
|
||||
|
||||
## Boot
|
||||
|
||||
Its time to network boot your machines. Use the BMC's remote management capablities (may be vendor-specific) to set the boot device (on the next boot only) to PXE and power on each machine.
|
||||
Its time to network boot your machines. Use the BMC's remote management capabilities (may be vendor-specific) to set the boot device (on the next boot only) to PXE and power on each machine.
|
||||
|
||||
```sh
|
||||
$ ipmitool -H node1.example.com -U USER -P PASS power off
|
||||
@@ -216,5 +210,5 @@ To re-provision the machine for another purpose, run `terraform apply` and PXE b
|
||||
|
||||
Matchbox can be used to provision multi-node Fedora CoreOS or Flatcar Linux clusters at one or many on-premise sites if deployed in an HA way. Machines can be matched individually by MAC address, UUID, region, or other labels you choose. Installs can be made much faster by caching images in the built-in HTTP [assets](api-http.md#assets) server.
|
||||
|
||||
[Ignition](https://github.com/coreos/ignition) can be used to partition disks and filesystems, write systemd units, write networkd configs or regular files, and create users. Nodes can be network provisioned into a complete cluster system that meets your needs. For example, see [Typhoon](https://typhoon.psdn.io/fedora-coreos/bare-metal/).
|
||||
[Ignition](https://github.com/coreos/ignition) can be used to partition disks, create file systems, write systemd units, write networkd configs or regular files, and create users. Nodes can be network provisioned into a complete cluster system that meets your needs. For example, see [Typhoon](https://typhoon.psdn.io/fedora-coreos/bare-metal/).
|
||||
|
||||
|
||||
160
docs/ignition.md
Normal file
160
docs/ignition.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# Ignition Configs
|
||||
|
||||
[Ignition](https://coreos.github.io/ignition/) configs define how disks should be provisioned (on network boot and first-boot from disk) to partition disks, write files (regular files, systemd units, networkd units, etc.), and configure users. Ignition is used by:
|
||||
|
||||
* Fedora CoreOS
|
||||
* RHEL CoreOS
|
||||
* Flatcar Linux
|
||||
|
||||
See the Ignition Config v3.x [specs](https://coreos.github.io/ignition/specs/) for details.
|
||||
|
||||
## Usage
|
||||
|
||||
Ignition configs can be added to the `/var/lib/matchbox/ignition` directory or in an `ignition` subdirectory of a custom `-data-path`. Ignition configs must end in `.ign` or `ignition`.
|
||||
|
||||
```
|
||||
/var/lib/matchbox
|
||||
├── ignition
|
||||
│ └── k8s-controller.ign
|
||||
│ └── k8s-worker.ign
|
||||
└── profiles
|
||||
```
|
||||
|
||||
Matchbox Profiles can set an Ignition config for provisioning machines. Specify the Ignition config in a [Profile](matchbox.md#profiles) with `ignition_id`.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "worker",
|
||||
"name": "My Profile",
|
||||
"boot": {
|
||||
...
|
||||
},
|
||||
"ignition_id": "my-ignition.ign"
|
||||
}
|
||||
```
|
||||
|
||||
When PXE booting, set kernel arguments depending on the OS (e.g. `ignition.firstboot` on FCOS, `flatcar.first_boot=yes` on Flatcar).
|
||||
|
||||
* [Fedora CoreOS](https://github.com/poseidon/matchbox/blob/main/examples/profiles/fedora-coreos.json)
|
||||
* [Flatcar Linux](https://github.com/poseidon/matchbox/blob/main/examples/profiles/flatcar.json)
|
||||
|
||||
Point the `ignition.config.url` or `flatcar.config.url` to point to the `matchbox` [Ignition endpoint](api-http.md#ignition-config).
|
||||
|
||||
Matchbox parses Ignition configs (e.g. `.ign` or `.ignition`) at spec v3.3 or below and renders to the current supported version (v3.3). This relies on Ignition's [forward compatibility](https://github.com/coreos/ignition/blob/main/config/v3_3/config.go#L61).
|
||||
|
||||
## Writing Configs
|
||||
|
||||
Ignition configs can be prepared externally and loaded via the gRPC API, rather than writing Ignition by hand.
|
||||
|
||||
### Terraform
|
||||
|
||||
Terraform can be used to prepare Ignition configs, while providing integrations with external systems and rich templating. Using tools like [poseidon/terraform-provider-ct](https://github.com/poseidon/terraform-provider-ct), you can write Butane config (an easier YAML format), validate configs, and load Ignition into Matchbox ([examples](https://github.com/poseidon/matchbox/tree/main/examples/terraform)).
|
||||
|
||||
Define a Butane config for Fedora CoreOS or Flatcar Linux:
|
||||
|
||||
```yaml
|
||||
variant: fcos
|
||||
version: 1.5.0
|
||||
passwd:
|
||||
users:
|
||||
- name: core
|
||||
ssh_authorized_keys:
|
||||
- ssh-key foo
|
||||
```
|
||||
|
||||
```yaml
|
||||
variant: flatcar
|
||||
version: 1.1.0
|
||||
passwd:
|
||||
users:
|
||||
- name: core
|
||||
ssh_authorized_keys:
|
||||
- ssh-key foo
|
||||
```
|
||||
|
||||
Define a `ct_config` data source with strict validation. Optionally use Terraform [templating](https://github.com/poseidon/terraform-provider-ct).
|
||||
|
||||
```tf
|
||||
data "ct_config" "worker" {
|
||||
content = file("worker.yaml")
|
||||
strict = true
|
||||
pretty_print = false
|
||||
|
||||
snippets = [
|
||||
file("units.yaml"),
|
||||
file("storage.yaml"),
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Then render the Butane config to Ignition and use it in a Matchbox Profile.
|
||||
|
||||
```tf
|
||||
resource "matchbox_profile" "fedora-coreos-install" {
|
||||
name = "worker"
|
||||
kernel = "/assets/fedora-coreos/fedora-coreos-${var.os_version}-live-kernel-x86_64"
|
||||
initrd = [
|
||||
"--name main /assets/fedora-coreos/fedora-coreos-${var.os_version}-live-initramfs.x86_64.img"
|
||||
]
|
||||
|
||||
args = [
|
||||
"initrd=main",
|
||||
"coreos.live.rootfs_url=${var.matchbox_http_endpoint}/assets/fedora-coreos/fedora-coreos-${var.os_version}-live-rootfs.x86_64.img",
|
||||
"coreos.inst.install_dev=/dev/vda",
|
||||
"coreos.inst.ignition_url=${var.matchbox_http_endpoint}/ignition?uuid=$${uuid}&mac=$${mac:hexhyp}",
|
||||
]
|
||||
|
||||
raw_ignition = data.ct_config.worker.rendered
|
||||
}
|
||||
```
|
||||
|
||||
See the Terraform [examples](https://github.com/poseidon/matchbox/tree/main/examples#terraform-examples) for details.
|
||||
|
||||
### Butane
|
||||
|
||||
The [Butane](https://coreos.github.io/butane/) command line tool can be used to convert Butane configs (an easier YAML format) to Ignition. Then you can use the Matchbox gRPC API to upload the rendered Ignition to Matchbox for serving to machines on boot.
|
||||
|
||||
See [examples/ignition](../examples/ignition) for Butane config examples.
|
||||
|
||||
### Matchbox Rendering
|
||||
|
||||
While Matchbox recommends preparing Ignition configs externally (e.g. using Terraform's rich templating), Matchbox does still support limited templating and translation features with a builtin Butane converter.
|
||||
|
||||
Specify a Butane config in a [Profile](matchbox.md#profiles) with `ignition_id` (file must not end in `.ign` or `.ignition`).
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "worker",
|
||||
"name": "My Profile",
|
||||
"boot": {
|
||||
...
|
||||
},
|
||||
"ignition_id": "butane.yaml"
|
||||
}
|
||||
```
|
||||
|
||||
Here is an example Butane config with Matchbox template elements. Template files may contain [Go template](https://golang.org/pkg/text/template/) elements which will be interpreted using group metadata, selectors, and query params.
|
||||
|
||||
```yaml
|
||||
variant: flatcar
|
||||
version: 1.1.0
|
||||
storage:
|
||||
files:
|
||||
- path: /var/home/core/foo
|
||||
mode: 0644
|
||||
contents:
|
||||
inline: |
|
||||
{{.example_contents}}
|
||||
|
||||
{{ if index . "ssh_authorized_keys" }}
|
||||
passwd:
|
||||
users:
|
||||
- name: core
|
||||
ssh_authorized_keys:
|
||||
{{ range $element := .ssh_authorized_keys }}
|
||||
- {{$element}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
```
|
||||
|
||||
Matchbox will use the Butane library to config to the current supported Ignition version. This relies on Ignition's [forward compatibility](https://github.com/coreos/ignition/blob/main/config/v3_3/config.go#L61).
|
||||
@@ -20,8 +20,8 @@ Matchbox can be installed from a binary or a container image.
|
||||
Start provisioning machines with Fedora CoreOS or Flatcar Linux.
|
||||
|
||||
* [Terraform Usage](getting-started.md)
|
||||
* Fedora CoreOS (live PXE or PXE install to disk)
|
||||
* Flatcar Linux (live PXE or PXE install to disk)
|
||||
* Fedora CoreOS (live PXE or PXE install to disk)
|
||||
* Flatcar Linux (live PXE or PXE install to disk)
|
||||
* [Local QEMU/KVM](getting-started-docker.md)
|
||||
* Fedora CoreOS (live PXE or PXE install to disk)
|
||||
* Flatcar Linux (live PXE or PXE install to disk)
|
||||
|
||||
@@ -33,9 +33,9 @@ Prepare `/var/lib/matchbox` with `groups`, `profile`, `ignition`, `cloud`, and `
|
||||
│ ├── cloud.yaml.tmpl
|
||||
│ └── worker.sh.tmpl
|
||||
├── ignition
|
||||
│ └── raw.ign
|
||||
│ └── etcd.yaml.tmpl
|
||||
│ └── simple.yaml.tmpl
|
||||
│ └── worker.ign
|
||||
│ └── butane.yaml.tmpl
|
||||
│ └── butane.yaml
|
||||
├── generic
|
||||
│ └── config.yaml
|
||||
│ └── setup.cfg
|
||||
@@ -53,14 +53,14 @@ The [examples](../examples) directory is a valid data directory with some pre-de
|
||||
|
||||
### Profiles
|
||||
|
||||
Profiles reference an Ignition config, Cloud-Config, and/or generic config by name and define network boot settings.
|
||||
Profiles reference an Ignition config, Butane Config, Cloud-Config, and/or generic config by name and define network boot settings.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "etcd",
|
||||
"name": "Container Linux with etcd2",
|
||||
"cloud_id": "",
|
||||
"ignition_id": "etcd.yaml",
|
||||
"ignition_id": "worker.ign",
|
||||
"generic_id": "some-service.cfg",
|
||||
"boot": {
|
||||
"kernel": "/assets/coreos/1967.3.0/coreos_production_pxe.vmlinuz",
|
||||
@@ -128,16 +128,16 @@ Group selectors can use any key/value pairs you find useful. However, several la
|
||||
|
||||
### Config templates
|
||||
|
||||
Profiles can reference various templated configs. Ignition JSON configs can be generated from [Container Linux Config](https://github.com/coreos/container-linux-config-transpiler/blob/master/doc/configuration.md) template files. Cloud-Config templates files can be used to render a script or Cloud-Config. Generic template files can be used to render arbitrary untyped configs (experimental). Each template may contain [Go template](https://golang.org/pkg/text/template/) elements which will be rendered with machine group metadata, selectors, and query params.
|
||||
Profiles can reference various templated configs. Ignition configs can be provided directly or rendered fro [Butane Config](https://coreos.github.io/butane/) template files. Cloud-Config templates files can be used to render a script or Cloud-Config. Generic template files can be used to render arbitrary untyped configs (experimental). Each template may contain [Go template](https://golang.org/pkg/text/template/) elements which will be rendered with machine group metadata, selectors, and query params.
|
||||
|
||||
For details and examples:
|
||||
|
||||
* [Container Linux Config](container-linux-config.md)
|
||||
* [Ignition (or Butane)](ignition.md)
|
||||
* [Cloud-Config](cloud-config.md)
|
||||
|
||||
#### Variables
|
||||
|
||||
Within Container Linux Config templates, Cloud-Config templates, or generic templates, you can use group metadata, selectors, or request-scoped query params. For example, a request `/generic?mac=52-54-00-89-d8-10&foo=some-param&bar=b` would match the `node1.json` machine group shown above. If the group's profile ("etcd") referenced a generic template, the following variables could be used.
|
||||
Within Butane Config templates, Cloud-Config templates, or generic templates, you can use group metadata, selectors, or request-scoped query params. For example, a request `/generic?mac=52-54-00-89-d8-10&foo=some-param&bar=b` would match the `node1.json` machine group shown above. If the group's profile ("etcd") referenced a generic template, the following variables could be used.
|
||||
|
||||
<!-- {% raw %} -->
|
||||
```
|
||||
|
||||
@@ -88,7 +88,7 @@ dhcp-boot=tag:ipxe,http://matchbox.example.com:8080/boot.ipxe
|
||||
log-queries
|
||||
log-dhcp
|
||||
|
||||
# static DNS assignements
|
||||
# static DNS assignments
|
||||
address=/matchbox.example.com/192.168.1.100
|
||||
|
||||
# (optional) disable DNS and specify alternate
|
||||
@@ -138,7 +138,7 @@ $ sudo firewall-cmd --add-service=dhcp --add-service=tftp [--add-service=dns]
|
||||
$ sudo firewall-cmd --list-services
|
||||
```
|
||||
|
||||
See [dnsmasq](#coreosdnsmasq) below to run dnsmasq with a container.
|
||||
See [dnsmasq](#poseidon/dnsmasq) below to run dnsmasq with a container.
|
||||
|
||||
### Configurable TFTP
|
||||
|
||||
|
||||
@@ -21,41 +21,42 @@ These examples mount raw Matchbox objects into a Matchbox server's `/var/lib/mat
|
||||
|
||||
| Name | Description | FS | Docs |
|
||||
|---------------|------------------------------|-----|-------|
|
||||
| fedora-coreos | Fedora CoreOS live PXE | RAM | [docs](https://docs.fedoraproject.org/en-US/fedora-coreos/live-booting-ipxe/) |
|
||||
| fedora-coreos | Fedora CoreOS live PXE | RAM | [docs](https://docs.fedoraproject.org/en-US/fedora-coreos/live-booting/) |
|
||||
| fedora-coreos-install | Fedora CoreOS install | Disk | [docs](https://docs.fedoraproject.org/en-US/fedora-coreos/bare-metal/) |
|
||||
| flatcar | Flatcar Linux live PXE | RAM | [docs](https://docs.flatcar-linux.org/os/booting-with-ipxe/) |
|
||||
| flatcar-install | Flatcar Linux install | Disk | [docs](https://docs.flatcar-linux.org/os/booting-with-ipxe/) |
|
||||
|
||||
### Customization
|
||||
### SSH Access
|
||||
|
||||
For Fedora CoreOS, add an SSH authorized key to Fedora CoreOS Config (`ignition/fedora-coreos.yaml`) and regenerate the Ignition Config.
|
||||
For Fedora CoreOS, add an SSH authorized key to the Butane Config (`ignition/fedora-coreos.yaml`) and regenerate the Ignition Config.
|
||||
|
||||
```yaml
|
||||
variant: fcos
|
||||
version: 1.1.0
|
||||
version: 1.5.0
|
||||
passwd:
|
||||
users:
|
||||
- name: core
|
||||
ssh_authorized_keys:
|
||||
- ssh-rsa pub-key-goes-here
|
||||
- ssh-ed25519 SET_PUBKEY_HERE
|
||||
```
|
||||
|
||||
```
|
||||
podman run -i --rm quay.io/coreos/fcct:release --pretty --strict < fedora-coreos.yaml > fedora-coreos.ign
|
||||
```
|
||||
|
||||
For Flatcar Linux, add a Matchbox variable to a Group (`groups/flatcar-install/flatcar.json`) to set the SSH authorized key (or directly update the Container Linux Config).
|
||||
For Flatcar Linux, add an SSH authorized key to the Butane config (`ignition/flatcar.yaml` or `ignition/flatcar-install.yaml`) and regenerate the Ignition Config.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "stage-1",
|
||||
"name": "Flatcar Linux",
|
||||
"profile": "flatcar",
|
||||
"selector": {
|
||||
"os": "installed"
|
||||
},
|
||||
"metadata": {
|
||||
"ssh_authorized_keys": ["ssh-rsa pub-key-goes-here"]
|
||||
}
|
||||
}
|
||||
```yaml
|
||||
variant: flatcar
|
||||
version: 1.1.0
|
||||
passwd:
|
||||
users:
|
||||
- name: core
|
||||
ssh_authorized_keys:
|
||||
- ssh-ed25519 SET_PUBKEY_HERE
|
||||
```
|
||||
|
||||
```
|
||||
podman run -i --rm quay.io/coreos/fcct:release --pretty --strict < flatcar.yaml > flatcar.ign
|
||||
podman run -i --rm quay.io/coreos/fcct:release --pretty --strict < flatcar-install.yaml > flatcar-install.ign
|
||||
```
|
||||
|
||||
@@ -5,6 +5,5 @@
|
||||
"selector": {
|
||||
"os": "installed"
|
||||
},
|
||||
"metadata": {
|
||||
}
|
||||
"metadata": {}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,5 @@
|
||||
"id": "stage-0",
|
||||
"name": "Flatcar Linux install",
|
||||
"profile": "flatcar-install",
|
||||
"metadata": {
|
||||
"os_channel": "stable",
|
||||
"os_version": "2605.6.0",
|
||||
"ignition_endpoint": "http://matchbox.example.com:8080/ignition",
|
||||
"baseurl": "http://matchbox.example.com:8080/assets/flatcar"
|
||||
}
|
||||
"metadata": {}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"ignition": {
|
||||
"version": "3.1.0"
|
||||
"version": "3.4.0"
|
||||
},
|
||||
"passwd": {
|
||||
"users": [
|
||||
{
|
||||
"name": "core",
|
||||
"sshAuthorizedKeys": [
|
||||
"ssh-rsa SET_PUBKEY_HERE"
|
||||
"ssh-ed25519 SET_PUBKEY_HERE"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
variant: fcos
|
||||
version: 1.1.0
|
||||
version: 1.5.0
|
||||
passwd:
|
||||
users:
|
||||
- name: core
|
||||
ssh_authorized_keys:
|
||||
- ssh-rsa SET_PUBKEY_HERE
|
||||
- ssh-ed25519 SET_PUBKEY_HERE
|
||||
|
||||
|
||||
36
examples/ignition/flatcar-install.ign
Normal file
36
examples/ignition/flatcar-install.ign
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"ignition": {
|
||||
"version": "3.4.0"
|
||||
},
|
||||
"passwd": {
|
||||
"users": [
|
||||
{
|
||||
"name": "core",
|
||||
"sshAuthorizedKeys": [
|
||||
"ssh-ed25519 SET_PUBKEY_HERE"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"storage": {
|
||||
"files": [
|
||||
{
|
||||
"path": "/opt/installer",
|
||||
"contents": {
|
||||
"compression": "gzip",
|
||||
"source": "data:;base64,H4sIAAAAAAAC/4SOsU4DMRBEe3/FEmrbx1EQRUIU/AMVzdrecEZrb+TdnC5/T3GhoaF8mhnNe3yIqfaYUBfwtLl8HQzeD7Jxg6cJvD9jZTgsZpdTjA0tL0m2QBu2C1PI0k7H6TjF+tWrVelvoq+1qyEzlQN4gd8kfKt0d2a0jMPfO/DpAHyBWGiNa8Gd30ENE9NOH/A8zy9hDtPOCf61QVUyjfezfVb/mFwLrVgaKJkxOb2pUcvGMCiJmPsJAAD//1GtasgbAQAA"
|
||||
},
|
||||
"mode": 320
|
||||
}
|
||||
]
|
||||
},
|
||||
"systemd": {
|
||||
"units": [
|
||||
{
|
||||
"contents": "[Unit]\nRequires=network-online.target\nAfter=network-online.target\n[Service]\nType=simple\nExecStart=/opt/installer\n[Install]\nWantedBy=multi-user.target\n",
|
||||
"enabled": true,
|
||||
"name": "installer.service"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
---
|
||||
variant: flatcar
|
||||
version: 1.1.0
|
||||
systemd:
|
||||
units:
|
||||
- name: installer.service
|
||||
enable: true
|
||||
enabled: true
|
||||
contents: |
|
||||
[Unit]
|
||||
Requires=network-online.target
|
||||
@@ -15,28 +17,22 @@ systemd:
|
||||
storage:
|
||||
files:
|
||||
- path: /opt/installer
|
||||
filesystem: root
|
||||
mode: 0500
|
||||
contents:
|
||||
inline: |
|
||||
#!/bin/bash -ex
|
||||
curl --retry 10 --fail "{{.ignition_endpoint}}?{{.request.raw_query}}&os=installed" -o ignition.json
|
||||
curl --retry 10 --fail "http://matchbox.example.com:8080/ignition?os=installed" -o ignition.json
|
||||
flatcar-install \
|
||||
-d /dev/sda \
|
||||
-C {{.os_channel}} \
|
||||
-V {{.os_version}} \
|
||||
{{- if index . "baseurl"}}-b {{.baseurl}} \{{end}}
|
||||
-d /dev/vda \
|
||||
-C stable \
|
||||
-V 3227.2.0 \
|
||||
-b http://matchbox.example.com:8080/assets/flatcar \
|
||||
-i ignition.json
|
||||
udevadm settle
|
||||
systemctl reboot
|
||||
|
||||
{{ if index . "ssh_authorized_keys" }}
|
||||
passwd:
|
||||
users:
|
||||
- name: core
|
||||
ssh_authorized_keys:
|
||||
{{ range $element := .ssh_authorized_keys }}
|
||||
- {{$element}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
- ssh-ed25519 SET_PUBKEY_HERE
|
||||
|
||||
15
examples/ignition/flatcar.ign
Normal file
15
examples/ignition/flatcar.ign
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"ignition": {
|
||||
"version": "3.4.0"
|
||||
},
|
||||
"passwd": {
|
||||
"users": [
|
||||
{
|
||||
"name": "core",
|
||||
"sshAuthorizedKeys": [
|
||||
"ssh-ed25519 SET_PUBKEY_HERE"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
---
|
||||
{{ if index . "ssh_authorized_keys" }}
|
||||
variant: flatcar
|
||||
version: 1.1.0
|
||||
passwd:
|
||||
users:
|
||||
- name: core
|
||||
ssh_authorized_keys:
|
||||
{{ range $element := .ssh_authorized_keys }}
|
||||
- {{$element}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
- ssh-ed25519 SET_PUBKEY_HERE
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
"id": "fedora-coreos-install",
|
||||
"name": "Fedora CoreOS install to disk",
|
||||
"boot": {
|
||||
"kernel": "/assets/fedora-coreos/fedora-coreos-32.20200923.3.0-live-kernel-x86_64",
|
||||
"kernel": "/assets/fedora-coreos/fedora-coreos-36.20220906.3.2-live-kernel-x86_64",
|
||||
"initrd": [
|
||||
"/assets/fedora-coreos/fedora-coreos-32.20200923.3.0-live-initramfs.x86_64.img",
|
||||
"/assets/fedora-coreos/fedora-coreos-32.20200923.3.0-live-rootfs.x86_64.img"
|
||||
"--name main /assets/fedora-coreos/fedora-coreos-36.20220906.3.2-live-initramfs.x86_64.img"
|
||||
],
|
||||
"args": [
|
||||
"coreos.inst.install_dev=/dev/sda",
|
||||
"coreos.inst.ignition_url=http://matchbox.example.com:8080/ignition?uuid=$${uuid}&mac=$${mac:hexhyp}",
|
||||
"coreos.inst.image_url=http://matchbox.example.com:8080/assets/fedora-coreos/fedora-coreos-32.20200923.3.0-metal.x86_64.raw.xz",
|
||||
"console=tty0",
|
||||
"console=ttyS0"
|
||||
"initrd=main",
|
||||
"coreos.live.rootfs_url=http://matchbox.example.com:8080/assets/fedora-coreos/fedora-coreos-36.20220906.3.2-live-rootfs.x86_64.img",
|
||||
"coreos.inst.install_dev=/dev/vda",
|
||||
"coreos.inst.ignition_url=http://matchbox.example.com:8080/ignition?uuid=${uuid}&mac=${mac:hexhyp}"
|
||||
]
|
||||
},
|
||||
"ignition_id": "fedora-coreos.ign"
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
"id": "fedora-coreos",
|
||||
"name": "Fedora CoreOS",
|
||||
"boot": {
|
||||
"kernel": "/assets/fedora-coreos/fedora-coreos-32.20200923.3.0-live-kernel-x86_64",
|
||||
"kernel": "/assets/fedora-coreos/fedora-coreos-36.20220906.3.2-live-kernel-x86_64",
|
||||
"initrd": [
|
||||
"/assets/fedora-coreos/fedora-coreos-32.20200923.3.0-live-initramfs.x86_64.img",
|
||||
"/assets/fedora-coreos/fedora-coreos-32.20200923.3.0-live-rootfs.x86_64.img"
|
||||
"--name main /assets/fedora-coreos/fedora-coreos-36.20220906.3.2-live-initramfs.x86_64.img"
|
||||
],
|
||||
"args": [
|
||||
"initrd=main",
|
||||
"coreos.live.rootfs_url=http://matchbox.example.com:8080/assets/fedora-coreos/fedora-coreos-36.20220906.3.2-live-rootfs.x86_64.img",
|
||||
"ignition.firstboot",
|
||||
"ignition.platform.id=metal",
|
||||
"ignition.config.url=http://matchbox.example.com:8080/ignition?uuid=${uuid}&mac=${mac:hexhyp}",
|
||||
"systemd.unified_cgroup_hierarchy=0",
|
||||
"console=tty0",
|
||||
"console=ttyS0"
|
||||
"ignition.config.url=http://matchbox.example.com:8080/ignition?uuid=${uuid}&mac=${mac:hexhyp}"
|
||||
]
|
||||
},
|
||||
"ignition_id": "fedora-coreos.ign"
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
"id": "flatcar-install",
|
||||
"name": "Flatcar Linux install to disk",
|
||||
"boot": {
|
||||
"kernel": "/assets/flatcar/2605.6.0/flatcar_production_pxe.vmlinuz",
|
||||
"kernel": "/assets/flatcar/3227.2.0/flatcar_production_pxe.vmlinuz",
|
||||
"initrd": [
|
||||
"/assets/flatcar/2605.6.0/flatcar_production_pxe_image.cpio.gz"
|
||||
"/assets/flatcar/3227.2.0/flatcar_production_pxe_image.cpio.gz"
|
||||
],
|
||||
"args": [
|
||||
"initrd=flatcar_production_pxe_image.cpio.gz",
|
||||
"flatcar.config.url=http://matchbox.example.com:8080/ignition?uuid=${uuid}&mac=${mac:hexhyp}",
|
||||
"flatcar.first_boot=yes",
|
||||
"console=tty0",
|
||||
"console=ttyS0",
|
||||
"flatcar.autologin"
|
||||
]
|
||||
},
|
||||
"ignition_id": "flatcar-install.yaml"
|
||||
"ignition_id": "flatcar-install.ign"
|
||||
}
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
"id": "flatcar",
|
||||
"name": "Flatcar Linux",
|
||||
"boot": {
|
||||
"kernel": "/assets/flatcar/2605.6.0/flatcar_production_pxe.vmlinuz",
|
||||
"kernel": "/assets/flatcar/3227.2.0/flatcar_production_pxe.vmlinuz",
|
||||
"initrd": [
|
||||
"/assets/flatcar/2605.6.0/flatcar_production_pxe_image.cpio.gz"
|
||||
"/assets/flatcar/3227.2.0/flatcar_production_pxe_image.cpio.gz"
|
||||
],
|
||||
"args": [
|
||||
"initrd=flatcar_production_pxe_image.cpio.gz",
|
||||
"flatcar.config.url=http://matchbox.example.com:8080/ignition?uuid=${uuid}&mac=${mac:hexhyp}",
|
||||
"flatcar.first_boot=yes",
|
||||
"console=tty0",
|
||||
"console=ttyS0",
|
||||
"flatcar.autologin"
|
||||
]
|
||||
},
|
||||
"ignition_id": "flatcar.yaml"
|
||||
"ignition_id": "flatcar.ign"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
variant: fcos
|
||||
version: 1.1.0
|
||||
version: 1.5.0
|
||||
passwd:
|
||||
users:
|
||||
- name: core
|
||||
|
||||
@@ -1,34 +1,24 @@
|
||||
// Fedora CoreOS profile
|
||||
resource "matchbox_profile" "fedora-coreos-install" {
|
||||
name = "worker"
|
||||
kernel = "https://builds.coreos.fedoraproject.org/prod/streams/${var.os_stream}/builds/${var.os_version}/x86_64/fedora-coreos-${var.os_version}-live-kernel-x86_64"
|
||||
kernel = "/assets/fedora-coreos/fedora-coreos-${var.os_version}-live-kernel-x86_64"
|
||||
initrd = [
|
||||
"https://builds.coreos.fedoraproject.org/prod/streams/${var.os_stream}/builds/${var.os_version}/x86_64/fedora-coreos-${var.os_version}-live-initramfs.x86_64.img",
|
||||
"https://builds.coreos.fedoraproject.org/prod/streams/${var.os_stream}/builds/${var.os_version}/x86_64/fedora-coreos-${var.os_version}-live-rootfs.x86_64.img"
|
||||
"--name main /assets/fedora-coreos/fedora-coreos-${var.os_version}-live-initramfs.x86_64.img"
|
||||
]
|
||||
|
||||
args = [
|
||||
"rd.neednet=1",
|
||||
"coreos.inst.install_dev=/dev/sda",
|
||||
"initrd=main",
|
||||
"coreos.live.rootfs_url=${var.matchbox_http_endpoint}/assets/fedora-coreos/fedora-coreos-${var.os_version}-live-rootfs.x86_64.img",
|
||||
"coreos.inst.install_dev=/dev/vda",
|
||||
"coreos.inst.ignition_url=${var.matchbox_http_endpoint}/ignition?uuid=$${uuid}&mac=$${mac:hexhyp}",
|
||||
"coreos.inst.image_url=https://builds.coreos.fedoraproject.org/prod/streams/${var.os_stream}/builds/${var.os_version}/x86_64/fedora-coreos-${var.os_version}-metal.x86_64.raw.xz",
|
||||
"console=tty0",
|
||||
"console=ttyS0",
|
||||
]
|
||||
|
||||
raw_ignition = data.ct_config.worker-ignition.rendered
|
||||
raw_ignition = data.ct_config.worker.rendered
|
||||
}
|
||||
|
||||
data "ct_config" "worker-ignition" {
|
||||
content = data.template_file.worker-config.rendered
|
||||
strict = true
|
||||
}
|
||||
|
||||
data "template_file" "worker-config" {
|
||||
template = file("fcc/fedora-coreos.yaml")
|
||||
vars = {
|
||||
data "ct_config" "worker" {
|
||||
content = templatefile("fcc/fedora-coreos.yaml", {
|
||||
ssh_authorized_key = var.ssh_authorized_key
|
||||
}
|
||||
})
|
||||
strict = true
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@ terraform {
|
||||
required_providers {
|
||||
ct = {
|
||||
source = "poseidon/ct"
|
||||
version = "0.6.1"
|
||||
version = "0.11.0"
|
||||
}
|
||||
matchbox = {
|
||||
source = "poseidon/matchbox"
|
||||
version = "0.4.1"
|
||||
version = "0.5.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
matchbox_http_endpoint = "http://matchbox.example.com:8080"
|
||||
matchbox_rpc_endpoint = "matchbox.example.com:8081"
|
||||
os_version = "32.20200923.3.0"
|
||||
os_version = "36.20220906.3.2"
|
||||
ssh_authorized_key = "YOUR_SSH_KEY"
|
||||
|
||||
@@ -16,7 +16,7 @@ variable "os_stream" {
|
||||
|
||||
variable "os_version" {
|
||||
type = string
|
||||
description = "Fedora CoreOS version to PXE and install (e.g. 32.20200923.3.0)"
|
||||
description = "Fedora CoreOS version to PXE and install (e.g. 36.20220906.3.2)"
|
||||
}
|
||||
|
||||
variable "ssh_authorized_key" {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
---
|
||||
variant: flatcar
|
||||
version: 1.1.0
|
||||
systemd:
|
||||
units:
|
||||
- name: installer.service
|
||||
enable: true
|
||||
enabled: true
|
||||
contents: |
|
||||
[Unit]
|
||||
Requires=network-online.target
|
||||
@@ -15,17 +17,14 @@ systemd:
|
||||
storage:
|
||||
files:
|
||||
- path: /opt/installer
|
||||
filesystem: root
|
||||
mode: 0500
|
||||
contents:
|
||||
inline: |
|
||||
#!/bin/bash -ex
|
||||
curl --retry 10 "{{.ignition_endpoint}}?{{.request.raw_query}}&os=installed" -o ignition.json
|
||||
curl --retry 10 "${matchbox_http_endpoint}/ignition?os=installed" -o ignition.json
|
||||
flatcar-install \
|
||||
-d /dev/sda \
|
||||
-C stable \
|
||||
-V current \
|
||||
{{- if index . "baseurl"}}-b {{.baseurl}} \{{end}}
|
||||
-d /dev/vda \
|
||||
-b ${matchbox_http_endpoint}/assets/flatcar \
|
||||
-i ignition.json
|
||||
udevadm settle
|
||||
systemctl reboot
|
||||
@@ -33,4 +32,4 @@ passwd:
|
||||
users:
|
||||
- name: core
|
||||
ssh_authorized_keys:
|
||||
- {{.ssh_authorized_key}}
|
||||
- ${ssh_authorized_key}
|
||||
@@ -1,6 +1,8 @@
|
||||
---
|
||||
variant: flatcar
|
||||
version: 1.1.0
|
||||
passwd:
|
||||
users:
|
||||
- name: core
|
||||
ssh_authorized_keys:
|
||||
- {{.ssh_authorized_key}}
|
||||
- ${ssh_authorized_key}
|
||||
@@ -2,24 +2,14 @@
|
||||
resource "matchbox_group" "default" {
|
||||
name = "default"
|
||||
profile = matchbox_profile.flatcar-install.name
|
||||
|
||||
# no selector means all machines can be matched
|
||||
metadata = {
|
||||
ignition_endpoint = "${var.matchbox_http_endpoint}/ignition"
|
||||
ssh_authorized_key = var.ssh_authorized_key
|
||||
}
|
||||
}
|
||||
|
||||
// Match machines which have CoreOS Container Linux installed
|
||||
resource "matchbox_group" "node1" {
|
||||
name = "node1"
|
||||
// Match install stage Flatcar Linux machines
|
||||
resource "matchbox_group" "stage-1" {
|
||||
name = "worker"
|
||||
profile = matchbox_profile.worker.name
|
||||
|
||||
selector = {
|
||||
os = "installed"
|
||||
}
|
||||
|
||||
metadata = {
|
||||
ssh_authorized_key = var.ssh_authorized_key
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,38 @@
|
||||
// Create a flatcar-install profile
|
||||
resource "matchbox_profile" "flatcar-install" {
|
||||
name = "flatcar-install"
|
||||
kernel = "http://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_pxe.vmlinuz"
|
||||
kernel = "/assets/flatcar/3227.2.0/flatcar_production_pxe.vmlinuz"
|
||||
initrd = [
|
||||
"http://stable.release.flatcar-linux.net/amd64-usr/current/flatcar_production_pxe_image.cpio.gz",
|
||||
"/assets/flatcar/3227.2.0/flatcar_production_pxe_image.cpio.gz",
|
||||
]
|
||||
|
||||
args = [
|
||||
"initrd=flatcar_production_pxe_image.cpio.gz",
|
||||
"flatcar.config.url=${var.matchbox_http_endpoint}/ignition?uuid=$${uuid}&mac=$${mac:hexhyp}",
|
||||
"flatcar.first_boot=yes",
|
||||
"console=tty0",
|
||||
"console=ttyS0",
|
||||
]
|
||||
|
||||
container_linux_config = file("./clc/flatcar-install.yaml")
|
||||
raw_ignition = data.ct_config.install.rendered
|
||||
}
|
||||
|
||||
data "ct_config" "install" {
|
||||
content = templatefile("butane/flatcar-install.yaml", {
|
||||
matchbox_http_endpoint = var.matchbox_http_endpoint
|
||||
ssh_authorized_key = var.ssh_authorized_key
|
||||
})
|
||||
strict = true
|
||||
}
|
||||
|
||||
|
||||
// Profile to set an SSH authorized key on first boot from disk
|
||||
resource "matchbox_profile" "worker" {
|
||||
name = "worker"
|
||||
container_linux_config = file("./clc/flatcar.yaml")
|
||||
name = "worker"
|
||||
raw_ignition = data.ct_config.worker.rendered
|
||||
}
|
||||
|
||||
data "ct_config" "worker" {
|
||||
content = templatefile("butane/flatcar.yaml", {
|
||||
ssh_authorized_key = var.ssh_authorized_key
|
||||
})
|
||||
strict = true
|
||||
}
|
||||
|
||||
@@ -8,9 +8,13 @@ provider "matchbox" {
|
||||
|
||||
terraform {
|
||||
required_providers {
|
||||
ct = {
|
||||
source = "poseidon/ct"
|
||||
version = "0.11.0"
|
||||
}
|
||||
matchbox = {
|
||||
source = "poseidon/matchbox"
|
||||
version = "0.4.1"
|
||||
version = "0.5.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
64
go.mod
64
go.mod
@@ -1,33 +1,41 @@
|
||||
module github.com/poseidon/matchbox
|
||||
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/ajeddeloh/go-json v0.0.0-20160803184958-73d058cf8437 // indirect
|
||||
github.com/ajeddeloh/yaml v0.0.0-20170912190910-6b94386aeefd // indirect
|
||||
github.com/alecthomas/units v0.0.0-20150109002421-6b4e7dc5e314 // indirect
|
||||
github.com/coreos/container-linux-config-transpiler v0.9.0
|
||||
github.com/coreos/coreos-cloudinit v1.13.0
|
||||
github.com/coreos/go-systemd v0.0.0-20160826104600-43e4800a6165 // indirect
|
||||
github.com/coreos/ignition v0.33.0
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f
|
||||
github.com/coreos/yaml v0.0.0-20141224210557-6b16a5714269 // indirect
|
||||
github.com/golang/protobuf v1.3.2
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.2.0 // indirect
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/vincent-petithory/dataurl v0.0.0-20160330182126-9a301d65acbb // indirect
|
||||
go4.org v0.0.0-20160314031811-03efcb870d84 // indirect
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c
|
||||
golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20191115221424-83cc0476cb11 // indirect
|
||||
google.golang.org/grpc v1.25.1
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.7 // indirect
|
||||
github.com/coreos/butane v0.20.0
|
||||
github.com/coreos/coreos-cloudinit v1.14.0
|
||||
github.com/coreos/ignition/v2 v2.18.0
|
||||
github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf
|
||||
github.com/golang/protobuf v1.5.4
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/net v0.22.0
|
||||
google.golang.org/grpc v1.62.1
|
||||
)
|
||||
|
||||
go 1.13
|
||||
require (
|
||||
github.com/aws/aws-sdk-go v1.50.25 // indirect
|
||||
github.com/clarketm/json v1.17.1 // indirect
|
||||
github.com/coreos/go-json v0.0.0-20230131223807-18775e0fb4fb // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/coreos/vcontext v0.0.0-20230201181013-d72178a18687 // indirect
|
||||
github.com/coreos/yaml v0.0.0-20141224210557-6b16a5714269 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
||||
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace // indirect
|
||||
github.com/vincent-petithory/dataurl v1.0.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
replace gopkg.in/yaml.v2 => gopkg.in/yaml.v2 v2.4.0
|
||||
|
||||
229
go.sum
229
go.sum
@@ -1,175 +1,74 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/ajeddeloh/go-json v0.0.0-20160803184958-73d058cf8437 h1:gZCtZ+Hh/e3CGEX8q/yAcp8wWu5ZS6NMk6VGzpQhI3s=
|
||||
github.com/ajeddeloh/go-json v0.0.0-20160803184958-73d058cf8437/go.mod h1:otnto4/Icqn88WCcM4bhIJNSgsh9VLBuspyyCfvof9c=
|
||||
github.com/ajeddeloh/yaml v0.0.0-20170912190910-6b94386aeefd h1:NlKlOv3aVJ5ODMC0JWPvddw05KENkL3cZttIuu8kJRo=
|
||||
github.com/ajeddeloh/yaml v0.0.0-20170912190910-6b94386aeefd/go.mod h1:idhzw68Q7v4j+rQ2AGyq3OlZW2Jij9mdmGA4/Sk6J0E=
|
||||
github.com/alecthomas/units v0.0.0-20150109002421-6b4e7dc5e314 h1:NWdGjCRUTTqEqDwJRojRLiwXDW1ZJM2DSiyGfBWx874=
|
||||
github.com/alecthomas/units v0.0.0-20150109002421-6b4e7dc5e314/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/container-linux-config-transpiler v0.9.0 h1:UBGpT8qWqzi48hNLrzMAgAUNJsR0LW8Gk5/dR/caI8U=
|
||||
github.com/coreos/container-linux-config-transpiler v0.9.0/go.mod h1:SlcxXZQ2c42knj8pezMiQsM1f+ADxFMjGetuMKR/YSQ=
|
||||
github.com/coreos/coreos-cloudinit v1.13.0 h1:NAJMy7cdj722Nm6+THzKx9fH/vCb39Z1JndsKS4TS6c=
|
||||
github.com/coreos/coreos-cloudinit v1.13.0/go.mod h1:hV3swhSwq+bRX5apuk57gG+3fsQacgbrZVxjPTqo0zo=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.0.0-20170209201757-5e3acbb5668c h1:+wuUamXDDSjSegI1+8PkeQ0oY7azHxu0jlLJ9Cc/oLE=
|
||||
github.com/coreos/go-semver v0.0.0-20170209201757-5e3acbb5668c/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20160826104600-43e4800a6165 h1:vjIPZX2iMVczdBreXAY5t+nGoR4z3rb4M1W1F1aGwsE=
|
||||
github.com/coreos/go-systemd v0.0.0-20160826104600-43e4800a6165/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/ignition v0.31.0 h1:e6ktS2Flcxq6SIKc1JN9NV1gQxmH2gaVwpdgtJIGep4=
|
||||
github.com/coreos/ignition v0.31.0/go.mod h1:WJQapxzEn9DE0ryxsGvm8QnBajm/XsS/PkrDqSpz+bA=
|
||||
github.com/coreos/ignition v0.33.0 h1:rYJoGv5v/5rCJAzyMaE9gU8pn7w7pv0M4rDzHvDK6T4=
|
||||
github.com/coreos/ignition v0.33.0/go.mod h1:WJQapxzEn9DE0ryxsGvm8QnBajm/XsS/PkrDqSpz+bA=
|
||||
github.com/coreos/pkg v0.0.0-20160221035341-66fe44ad037c h1:YYKUGUx+21jb01m4f292UtXWBauM5oQH6tywwQNZxdU=
|
||||
github.com/coreos/pkg v0.0.0-20160221035341-66fe44ad037c/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/aws/aws-sdk-go v1.50.25 h1:vhiHtLYybv1Nhx3Kv18BBC6L0aPJHaG9aeEsr92W99c=
|
||||
github.com/aws/aws-sdk-go v1.50.25/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/clarketm/json v1.17.1 h1:U1IxjqJkJ7bRK4L6dyphmoO840P6bdhPdbbLySourqI=
|
||||
github.com/clarketm/json v1.17.1/go.mod h1:ynr2LRfb0fQU34l07csRNBTcivjySLLiY1YzQqKVfdo=
|
||||
github.com/coreos/butane v0.20.0 h1:lKdyBDazlM5cenywmAT6Fcwp5B2rVeYtIqZDSbYu3MA=
|
||||
github.com/coreos/butane v0.20.0/go.mod h1:JPxQB/3omxqSg1ZWAOg5x3aslzx4nDvmb7Wyv8gC150=
|
||||
github.com/coreos/coreos-cloudinit v1.14.0 h1:3bQRJaie3QC8EovAVbxiimLQgdxM6DxP1vJfUCEEl9w=
|
||||
github.com/coreos/coreos-cloudinit v1.14.0/go.mod h1:hV3swhSwq+bRX5apuk57gG+3fsQacgbrZVxjPTqo0zo=
|
||||
github.com/coreos/go-json v0.0.0-20230131223807-18775e0fb4fb h1:rmqyI19j3Z/74bIRhuC59RB442rXUazKNueVpfJPxg4=
|
||||
github.com/coreos/go-json v0.0.0-20230131223807-18775e0fb4fb/go.mod h1:rcFZM3uxVvdyNmsAV2jopgPD1cs5SPWJWU5dOz2LUnw=
|
||||
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/ignition/v2 v2.18.0 h1:sPSGGsxaCuFMpKOMBQ71I9RIR20SIF4dWnoTomcPEYQ=
|
||||
github.com/coreos/ignition/v2 v2.18.0/go.mod h1:TURPHDqWUWTmej8c+CEMBENMU3N/Lt6GfreHJuoDMbA=
|
||||
github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf h1:GOPo6vn/vTN+3IwZBvXX0y5doJfSC7My0cdzelyOCsQ=
|
||||
github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/coreos/vcontext v0.0.0-20230201181013-d72178a18687 h1:uSmlDgJGbUB0bwQBcZomBTottKwEDF5fF8UjSwKSzWM=
|
||||
github.com/coreos/vcontext v0.0.0-20230201181013-d72178a18687/go.mod h1:Salmysdw7DAVuobBW/LwsKKgpyCPHUhjyJoMJD+ZJiI=
|
||||
github.com/coreos/yaml v0.0.0-20141224210557-6b16a5714269 h1:/1sjrpK5Mb6IwyFOKd+u7321tXfNAsj0Ci8CivZmSlo=
|
||||
github.com/coreos/yaml v0.0.0-20141224210557-6b16a5714269/go.mod h1:Bl1D/T9QJhVdu6eFoLrGxN90+admDLGaLz2HXH/VzDc=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v0.0.0-20170331031902-2bba0603135d h1:KmiEmEGA5sqizMpKnexwioxj8zEUSBc7p9UTQu36lpQ=
|
||||
github.com/golang/protobuf v0.0.0-20170331031902-2bba0603135d/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.0-20160208220627-65a708cee0a4 h1:YWnu4r9zC05ic4XVJR+X7zxHI/lv5Ha9QPYx3gZvQHw=
|
||||
github.com/spf13/cobra v0.0.0-20160208220627-65a708cee0a4/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20151218134703-7f60f83a2c81 h1:e8OMOPK+iXlzdnq5GOtSZDnw9HJi1faEKhCoEIxVUrY=
|
||||
github.com/spf13/pflag v0.0.0-20151218134703-7f60f83a2c81/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace h1:9PNP1jnUjRhfmGMlkXHjYPishpcw4jpSt/V/xYY3FMA=
|
||||
github.com/spf13/pflag v1.0.6-0.20210604193023-d5e0c0615ace/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/vincent-petithory/dataurl v0.0.0-20160330182126-9a301d65acbb h1:lyL3z7vYwTWXf4/bI+A01+cCSnfhKIBhy+SQ46Z/ml8=
|
||||
github.com/vincent-petithory/dataurl v0.0.0-20160330182126-9a301d65acbb/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go4.org v0.0.0-20160314031811-03efcb870d84 h1:WZkGC1qzoax/QSt84wmvIxk+ZOmGIChsTzdrv9t4nvk=
|
||||
go4.org v0.0.0-20160314031811-03efcb870d84/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4=
|
||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20160412225635-fb93926129b8 h1:5Lew4KHjm6QAzkboL0Sdg03Nc3VtVcno+Mdcvclquco=
|
||||
golang.org/x/net v0.0.0-20160412225635-fb93926129b8/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914 h1:MlY3mEfbnWGmUi4rtHOtNnnnN4UJRGSyLPx+DXA5Sq4=
|
||||
golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503 h1:5SvYFrOM3W8Mexn9/oA44Ji7vhXAZQ9hiP+1Q/DMrWg=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20191115221424-83cc0476cb11 h1:51D++eCgOHufw5VfDE9Uzqyyc+OyQIjb9hkYy9LN5Fk=
|
||||
google.golang.org/genproto v0.0.0-20191115221424-83cc0476cb11/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/grpc v1.2.1 h1:uXbsPclX7+aYIzPCched4e5D5dguzKH7wW8cyJ9r2Pk=
|
||||
google.golang.org/grpc v1.2.1/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI=
|
||||
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.0.0-20151201162745-f7716cbe52ba h1:zs5kJPNmBQxScpRIdp+2eZySRHFWheLSbXfME4zAxgw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20151201162745-f7716cbe52ba/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
pb "github.com/poseidon/matchbox/matchbox/server/serverpb"
|
||||
@@ -36,7 +37,7 @@ func runGenericPutCmd(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
client := mustClientFromCmd(cmd)
|
||||
config, err := ioutil.ReadFile(flagFilename)
|
||||
config, err := os.ReadFile(flagFilename)
|
||||
if err != nil {
|
||||
exitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
pb "github.com/poseidon/matchbox/matchbox/server/serverpb"
|
||||
@@ -49,7 +49,7 @@ func runGroupPutCmd(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
func loadGroup(filename string) (*storagepb.Group, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
pb "github.com/poseidon/matchbox/matchbox/server/serverpb"
|
||||
@@ -36,7 +37,7 @@ func runIgnitionPutCmd(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
client := mustClientFromCmd(cmd)
|
||||
config, err := ioutil.ReadFile(flagFilename)
|
||||
config, err := os.ReadFile(flagFilename)
|
||||
if err != nil {
|
||||
exitWithError(ExitError, err)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
pb "github.com/poseidon/matchbox/matchbox/server/serverpb"
|
||||
@@ -57,7 +57,7 @@ func validateArgs(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
func loadProfile(filename string) (*storagepb.Profile, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
data, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
//
|
||||
// Create a matchbox gRPC client using `client.New`:
|
||||
//
|
||||
// cfg := &client.Config{
|
||||
// Endpoints: []string{"127.0.0.1:8081"},
|
||||
// DialTimeout: 10 * time.Second,
|
||||
// }
|
||||
// client, err := client.New(cfg)
|
||||
// defer client.Close()
|
||||
// cfg := &client.Config{
|
||||
// Endpoints: []string{"127.0.0.1:8081"},
|
||||
// DialTimeout: 10 * time.Second,
|
||||
// }
|
||||
// client, err := client.New(cfg)
|
||||
// defer client.Close()
|
||||
//
|
||||
// Callers must Close the client after use.
|
||||
//
|
||||
package client
|
||||
|
||||
@@ -5,8 +5,9 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
ct "github.com/coreos/container-linux-config-transpiler/config"
|
||||
ignition "github.com/coreos/ignition/config/v2_2"
|
||||
butane "github.com/coreos/butane/config"
|
||||
"github.com/coreos/butane/config/common"
|
||||
ignition "github.com/coreos/ignition/v2/config/v3_4"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/poseidon/matchbox/matchbox/server"
|
||||
@@ -60,17 +61,17 @@ func (s *Server) ignitionHandler(core server.Server) http.Handler {
|
||||
"profile": profile.Id,
|
||||
}).Debug("Matched an Ignition or Container Linux Config template")
|
||||
|
||||
// Skip rendering if raw Ignition JSON is provided
|
||||
// Serve Ignition (e.g. *.ign) content directly
|
||||
if isIgnition(profile.IgnitionId) {
|
||||
_, report, err := ignition.Parse([]byte(contents))
|
||||
ign, report, err := ignition.ParseCompatibleVersion([]byte(contents))
|
||||
if err != nil {
|
||||
s.logger.Warningf("warning parsing Ignition JSON: %s", report.String())
|
||||
s.logger.Warningf("warning parsing Ignition: %s", report.String())
|
||||
}
|
||||
s.writeJSON(w, []byte(contents))
|
||||
s.renderJSON(w, ign)
|
||||
return
|
||||
}
|
||||
|
||||
// Container Linux Config template
|
||||
// Butane Config template (discouraged)
|
||||
|
||||
// collect data for rendering
|
||||
data, err := collectVariables(req, group)
|
||||
@@ -80,7 +81,7 @@ func (s *Server) ignitionHandler(core server.Server) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
// render the template for an Ignition config with data
|
||||
// render the template
|
||||
var buf bytes.Buffer
|
||||
err = s.renderTemplate(&buf, data, contents)
|
||||
if err != nil {
|
||||
@@ -88,24 +89,20 @@ func (s *Server) ignitionHandler(core server.Server) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
// Parse bytes into a Container Linux Config
|
||||
config, ast, report := ct.Parse(buf.Bytes())
|
||||
if report.IsFatal() {
|
||||
s.logger.Errorf("error parsing Container Linux config: %s", report.String())
|
||||
// translate to Ignition
|
||||
ignBytes, report, err := butane.TranslateBytes(buf.Bytes(), common.TranslateBytesOptions{})
|
||||
if err != nil {
|
||||
s.logger.Errorf("error translating Butane Config: %s", report.String())
|
||||
http.NotFound(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
// Convert Container Linux Config into an Ignition Config
|
||||
ign, report := ct.Convert(config, "", ast)
|
||||
if report.IsFatal() {
|
||||
s.logger.Errorf("error converting Container Linux config: %s", report.String())
|
||||
http.NotFound(w, req)
|
||||
return
|
||||
// validate
|
||||
ign, report, err := ignition.ParseCompatibleVersion(ignBytes)
|
||||
if err != nil {
|
||||
s.logger.Warningf("warning parsing Ignition: %s", report.String())
|
||||
}
|
||||
|
||||
s.renderJSON(w, ign)
|
||||
return
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"context"
|
||||
|
||||
logtest "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -14,8 +15,8 @@ import (
|
||||
fake "github.com/poseidon/matchbox/matchbox/storage/testfakes"
|
||||
)
|
||||
|
||||
func TestIgnitionHandler_V2_1_JSON(t *testing.T) {
|
||||
content := `{"ignition":{"version":"2.1.0","config":{}},"storage":{},"systemd":{"units":[{"name":"etcd2.service","enable":true}]},"networkd":{},"passwd":{}}`
|
||||
func TestIgnitionHandler_v3_4(t *testing.T) {
|
||||
const content = `{"ignition":{"config":{"replace":{"verification":{}}},"proxy":{},"security":{"tls":{}},"timeouts":{},"version":"3.4.0"},"kernelArguments":{},"passwd":{"users":[{"name":"core","sshAuthorizedKeys":["key"]}]},"storage":{},"systemd":{"units":[{"enabled":false,"name":"docker.service"}]}}`
|
||||
profile := &storagepb.Profile{
|
||||
Id: fake.Group.Profile,
|
||||
IgnitionId: "file.ign",
|
||||
@@ -26,123 +27,145 @@ func TestIgnitionHandler_V2_1_JSON(t *testing.T) {
|
||||
}
|
||||
logger, _ := logtest.NewNullLogger()
|
||||
srv := NewServer(&Config{Logger: logger})
|
||||
c := server.NewServer(&server.Config{Store: store})
|
||||
h := srv.ignitionHandler(c)
|
||||
core := server.NewServer(&server.Config{Store: store})
|
||||
h := srv.ignitionHandler(core)
|
||||
|
||||
ctx := withGroup(context.Background(), fake.Group)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
h.ServeHTTP(w, req.WithContext(ctx))
|
||||
req, _ := http.NewRequestWithContext(ctx, "GET", "/", nil)
|
||||
h.ServeHTTP(w, req)
|
||||
// assert that:
|
||||
// - raw Ignition config served directly
|
||||
// - serve Ignition JSON
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, jsonContentType, w.HeaderMap.Get(contentType))
|
||||
assert.Equal(t, jsonContentType, w.Header().Get(contentType))
|
||||
assert.Equal(t, content, w.Body.String())
|
||||
}
|
||||
|
||||
func TestIgnitionHandler_V2_2_JSON(t *testing.T) {
|
||||
content := `{"ignition":{"version":"2.2.0","config":{}},"storage":{},"systemd":{"units":[{"name":"etcd2.service","enable":true}]},"networkd":{},"passwd":{}}`
|
||||
func TestIgnitionHandler_v3_1(t *testing.T) {
|
||||
const ign31 = `{"ignition":{"config":{"replace":{"verification":{}}},"proxy":{},"security":{"tls":{}},"timeouts":{},"version":"3.1.0"},"kernelArguments":{},"passwd":{"users":[{"name":"core","sshAuthorizedKeys":["key"]}]},"storage":{},"systemd":{"units":[{"enabled":false,"name":"docker.service"}]}}`
|
||||
const ign34 = `{"ignition":{"config":{"replace":{"verification":{}}},"proxy":{},"security":{"tls":{}},"timeouts":{},"version":"3.4.0"},"kernelArguments":{},"passwd":{"users":[{"name":"core","sshAuthorizedKeys":["key"]}]},"storage":{},"systemd":{"units":[{"enabled":false,"name":"docker.service"}]}}`
|
||||
profile := &storagepb.Profile{
|
||||
Id: fake.Group.Profile,
|
||||
IgnitionId: "file.ign",
|
||||
}
|
||||
store := &fake.FixedStore{
|
||||
Profiles: map[string]*storagepb.Profile{fake.Group.Profile: profile},
|
||||
IgnitionConfigs: map[string]string{"file.ign": content},
|
||||
Profiles: map[string]*storagepb.Profile{
|
||||
fake.Group.Profile: profile,
|
||||
},
|
||||
IgnitionConfigs: map[string]string{
|
||||
"file.ign": ign31,
|
||||
},
|
||||
}
|
||||
logger, _ := logtest.NewNullLogger()
|
||||
srv := NewServer(&Config{Logger: logger})
|
||||
c := server.NewServer(&server.Config{Store: store})
|
||||
h := srv.ignitionHandler(c)
|
||||
core := server.NewServer(&server.Config{Store: store})
|
||||
h := srv.ignitionHandler(core)
|
||||
|
||||
ctx := withGroup(context.Background(), fake.Group)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
h.ServeHTTP(w, req.WithContext(ctx))
|
||||
req, _ := http.NewRequestWithContext(ctx, "GET", "/", nil)
|
||||
h.ServeHTTP(w, req)
|
||||
// assert that:
|
||||
// - raw Ignition config served directly
|
||||
// - older Ignition v3.x converted to compatible latest version (e.g. v3.3)
|
||||
// - serve Ignition JSON
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, jsonContentType, w.HeaderMap.Get(contentType))
|
||||
assert.Equal(t, content, w.Body.String())
|
||||
assert.Equal(t, jsonContentType, w.Header().Get(contentType))
|
||||
assert.Equal(t, ign34, w.Body.String())
|
||||
}
|
||||
|
||||
func TestIgnitionHandler_CL_YAML(t *testing.T) {
|
||||
// exercise templating features, not a realistic Container Linux Config template
|
||||
content := `
|
||||
func TestIgnitionHandler_MissingIgnition(t *testing.T) {
|
||||
logger, _ := logtest.NewNullLogger()
|
||||
srv := NewServer(&Config{Logger: logger})
|
||||
core := server.NewServer(&server.Config{Store: &fake.EmptyStore{}})
|
||||
h := srv.ignitionHandler(core)
|
||||
|
||||
ctx := withProfile(context.Background(), fake.Profile)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequestWithContext(ctx, "GET", "/", nil)
|
||||
h.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func TestIgnitionHandler_Butane(t *testing.T) {
|
||||
// exercise templating features, not a realistic Butane template
|
||||
butane := `
|
||||
variant: flatcar
|
||||
version: 1.1.0
|
||||
systemd:
|
||||
units:
|
||||
- name: {{.service_name}}.service
|
||||
enable: true
|
||||
- name: {{.uuid}}.service
|
||||
enable: true
|
||||
enabled: true
|
||||
contents: {{.pod_network}}
|
||||
- name: {{.request.query.foo}}.service
|
||||
enable: true
|
||||
enabled: true
|
||||
contents: {{.request.raw_query}}
|
||||
`
|
||||
expectedIgnition := `{"ignition":{"config":{},"security":{"tls":{}},"timeouts":{},"version":"2.2.0"},"networkd":{},"passwd":{},"storage":{},"systemd":{"units":[{"enable":true,"name":"etcd2.service"},{"enable":true,"name":"a1b2c3d4.service"},{"contents":"foo=some-param\u0026bar=b","enable":true,"name":"some-param.service"}]}}`
|
||||
expectedIgnition := `{"ignition":{"config":{"replace":{"verification":{}}},"proxy":{},"security":{"tls":{}},"timeouts":{},"version":"3.4.0"},"kernelArguments":{},"passwd":{},"storage":{},"systemd":{"units":[{"contents":"10.2.0.0/16","enabled":true,"name":"a1b2c3d4.service"},{"contents":"foo=some-param\u0026bar=b","enabled":true,"name":"some-param.service"}]}}`
|
||||
|
||||
store := &fake.FixedStore{
|
||||
Profiles: map[string]*storagepb.Profile{fake.Group.Profile: testProfileIgnitionYAML},
|
||||
IgnitionConfigs: map[string]string{testProfileIgnitionYAML.IgnitionId: content},
|
||||
Profiles: map[string]*storagepb.Profile{
|
||||
fake.Group.Profile: testProfileWithButane,
|
||||
},
|
||||
IgnitionConfigs: map[string]string{
|
||||
testProfileWithButane.IgnitionId: butane,
|
||||
},
|
||||
}
|
||||
logger, _ := logtest.NewNullLogger()
|
||||
srv := NewServer(&Config{Logger: logger})
|
||||
c := server.NewServer(&server.Config{Store: store})
|
||||
h := srv.ignitionHandler(c)
|
||||
core := server.NewServer(&server.Config{Store: store})
|
||||
h := srv.ignitionHandler(core)
|
||||
|
||||
ctx := withGroup(context.Background(), fake.Group)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/?foo=some-param&bar=b", nil)
|
||||
h.ServeHTTP(w, req.WithContext(ctx))
|
||||
req, _ := http.NewRequestWithContext(ctx, "GET", "/?foo=some-param&bar=b", nil)
|
||||
h.ServeHTTP(w, req)
|
||||
// assert that:
|
||||
// - Container Linux Config template rendered with Group selectors, metadata, and query variables
|
||||
// - Transformed to an Ignition config (JSON)
|
||||
// - Template rendered with Group selectors, metadata, and query variables
|
||||
// - Butane translated to an Ignition config
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, jsonContentType, w.HeaderMap.Get(contentType))
|
||||
assert.Equal(t, jsonContentType, w.Header().Get(contentType))
|
||||
assert.Equal(t, expectedIgnition, w.Body.String())
|
||||
}
|
||||
|
||||
func TestIgnitionHandler_MissingCtxProfile(t *testing.T) {
|
||||
logger, _ := logtest.NewNullLogger()
|
||||
srv := NewServer(&Config{Logger: logger})
|
||||
c := server.NewServer(&server.Config{Store: &fake.EmptyStore{}})
|
||||
h := srv.ignitionHandler(c)
|
||||
core := server.NewServer(&server.Config{Store: &fake.EmptyStore{}})
|
||||
h := srv.ignitionHandler(core)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
h.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func TestIgnitionHandler_MissingIgnitionConfig(t *testing.T) {
|
||||
logger, _ := logtest.NewNullLogger()
|
||||
srv := NewServer(&Config{Logger: logger})
|
||||
c := server.NewServer(&server.Config{Store: &fake.EmptyStore{}})
|
||||
h := srv.ignitionHandler(c)
|
||||
ctx := withProfile(context.Background(), fake.Profile)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
h.ServeHTTP(w, req.WithContext(ctx))
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
func TestIgnitionHandler_MissingTemplateMetadata(t *testing.T) {
|
||||
content := `
|
||||
ignition_version: 1
|
||||
butane := `
|
||||
variant: flatcar
|
||||
version: 1.1.0
|
||||
systemd:
|
||||
units:
|
||||
- name: {{.missing_key}}
|
||||
enable: true
|
||||
enabled: true
|
||||
`
|
||||
store := &fake.FixedStore{
|
||||
Profiles: map[string]*storagepb.Profile{fake.Group.Profile: fake.Profile},
|
||||
IgnitionConfigs: map[string]string{fake.Profile.IgnitionId: content},
|
||||
Profiles: map[string]*storagepb.Profile{
|
||||
fake.Group.Profile: fake.Profile,
|
||||
},
|
||||
IgnitionConfigs: map[string]string{
|
||||
fake.Profile.IgnitionId: butane,
|
||||
},
|
||||
}
|
||||
logger, _ := logtest.NewNullLogger()
|
||||
srv := NewServer(&Config{Logger: logger})
|
||||
c := server.NewServer(&server.Config{Store: store})
|
||||
h := srv.ignitionHandler(c)
|
||||
core := server.NewServer(&server.Config{Store: store})
|
||||
h := srv.ignitionHandler(core)
|
||||
|
||||
ctx := withGroup(context.Background(), fake.Group)
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
h.ServeHTTP(w, req.WithContext(ctx))
|
||||
req, _ := http.NewRequestWithContext(ctx, "GET", "/", nil)
|
||||
h.ServeHTTP(w, req)
|
||||
// assert that:
|
||||
// - Ignition template rendering errors because "missing_key" is not
|
||||
// present in the template variables
|
||||
// - Template rendering errors because "missing_key" is not present
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
const ipxeBootstrap = `#!ipxe
|
||||
chain ipxe?uuid=${uuid}&mac=${mac:hexhyp}&domain=${domain}&hostname=${hostname}&serial=${serial}
|
||||
chain ipxe?uuid=${uuid}&mac=${mac:hexhyp}&domain=${domain}&hostname=${hostname}&serial=${serial}&arch=${buildarch:uristring}
|
||||
`
|
||||
|
||||
var ipxeTemplate = template.Must(template.New("iPXE config").Parse(`#!ipxe
|
||||
@@ -25,7 +25,7 @@ boot
|
||||
// client machine data and chainload to the ipxeHandler.
|
||||
func ipxeInspect() http.Handler {
|
||||
fn := func(w http.ResponseWriter, req *http.Request) {
|
||||
fmt.Fprintf(w, ipxeBootstrap)
|
||||
fmt.Fprint(w, ipxeBootstrap)
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"context"
|
||||
|
||||
logtest "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
@@ -49,7 +50,7 @@ func TestMetadataHandler(t *testing.T) {
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
// convert response (random order) to map (tests compare in order)
|
||||
assert.Equal(t, expectedLines, metadataToMap(w.Body.String()))
|
||||
assert.Equal(t, plainContentType, w.HeaderMap.Get(contentType))
|
||||
assert.Equal(t, plainContentType, w.Header().Get(contentType))
|
||||
}
|
||||
|
||||
func TestMetadataHandler_MetadataEdgeCases(t *testing.T) {
|
||||
@@ -75,7 +76,7 @@ func TestMetadataHandler_MetadataEdgeCases(t *testing.T) {
|
||||
// - key/value pairs are newline separated
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Contains(t, w.Body.String(), c.expected)
|
||||
assert.Equal(t, plainContentType, w.HeaderMap.Get(contentType))
|
||||
assert.Equal(t, plainContentType, w.Header().Get(contentType))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,11 @@ func TestRenderJSON(t *testing.T) {
|
||||
srv := NewServer(&Config{Logger: logger})
|
||||
w := httptest.NewRecorder()
|
||||
data := map[string][]string{
|
||||
"a": []string{"b", "c"},
|
||||
"a": {"b", "c"},
|
||||
}
|
||||
srv.renderJSON(w, data)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, jsonContentType, w.HeaderMap.Get(contentType))
|
||||
assert.Equal(t, jsonContentType, w.Header().Get(contentType))
|
||||
assert.Equal(t, `{"a":["b","c"]}`, w.Body.String())
|
||||
}
|
||||
|
||||
|
||||
@@ -7,20 +7,8 @@ import (
|
||||
var (
|
||||
validMACStr = "52:da:00:89:d8:10"
|
||||
|
||||
testProfileIgnitionYAML = &storagepb.Profile{
|
||||
testProfileWithButane = &storagepb.Profile{
|
||||
Id: "g1h2i3j4",
|
||||
IgnitionId: "ignition.yaml",
|
||||
}
|
||||
|
||||
testProfileGeneric = &storagepb.Profile{
|
||||
Id: "g1h2i3j4",
|
||||
IgnitionId: "generic.tmpl",
|
||||
}
|
||||
|
||||
testGroupWithMAC = &storagepb.Group{
|
||||
Id: "test-group",
|
||||
Name: "test group",
|
||||
Profile: "g1h2i3j4",
|
||||
Selector: map[string]string{"mac": validMACStr},
|
||||
IgnitionId: "butane.yaml",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
@@ -30,7 +29,7 @@ func TestSignatureHandler(t *testing.T) {
|
||||
h.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
assert.Equal(t, expectedBody, w.Body.String())
|
||||
assert.NotEqual(t, "application/json", w.HeaderMap.Get("Content-Type"))
|
||||
assert.NotEqual(t, "application/json", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestSignatureHandler_ErrorStatusCode(t *testing.T) {
|
||||
@@ -75,7 +74,7 @@ func TestSignatureHandler_SignatureError(t *testing.T) {
|
||||
type upperSigner struct{}
|
||||
|
||||
func (s *upperSigner) Sign(w io.Writer, message io.Reader) error {
|
||||
b, err := ioutil.ReadAll(message)
|
||||
b, err := io.ReadAll(message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package storage
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -244,7 +243,7 @@ func TestCloudGet(t *testing.T) {
|
||||
// for testing. Returns the directory tree root. The caller must remove the
|
||||
// temp directory when finished.
|
||||
func setup(fixedStore *fake.FixedStore) (root string, err error) {
|
||||
root, err = ioutil.TempDir("", "data")
|
||||
root, err = os.MkdirTemp("", "data")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -264,7 +263,7 @@ func setup(fixedStore *fake.FixedStore) (root string, err error) {
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
err = ioutil.WriteFile(profileFile, []byte(data), defaultFileMode)
|
||||
err = os.WriteFile(profileFile, []byte(data), defaultFileMode)
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
@@ -279,28 +278,28 @@ func setup(fixedStore *fake.FixedStore) (root string, err error) {
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
err = ioutil.WriteFile(groupFile, []byte(data), defaultFileMode)
|
||||
err = os.WriteFile(groupFile, []byte(data), defaultFileMode)
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
}
|
||||
for name, content := range fixedStore.IgnitionConfigs {
|
||||
ignitionFile := filepath.Join(ignitionDir, name)
|
||||
err = ioutil.WriteFile(ignitionFile, []byte(content), defaultFileMode)
|
||||
err = os.WriteFile(ignitionFile, []byte(content), defaultFileMode)
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
}
|
||||
for name, content := range fixedStore.GenericConfigs {
|
||||
genericFile := filepath.Join(genericDir, name)
|
||||
err = ioutil.WriteFile(genericFile, []byte(content), defaultFileMode)
|
||||
err = os.WriteFile(genericFile, []byte(content), defaultFileMode)
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
}
|
||||
for name, content := range fixedStore.CloudConfigs {
|
||||
cloudConfigFile := filepath.Join(cloudDir, name)
|
||||
err = ioutil.WriteFile(cloudConfigFile, []byte(content), defaultFileMode)
|
||||
err = os.WriteFile(cloudConfigFile, []byte(content), defaultFileMode)
|
||||
if err != nil {
|
||||
return root, err
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package storage
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -28,17 +28,17 @@ func (d Dir) readFile(path string) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.ReadFile(path)
|
||||
return os.ReadFile(path)
|
||||
}
|
||||
|
||||
// readDir reads the directory named by the given path and returns a list of
|
||||
// sorted directory entries. Restricted to a specified directory tree.
|
||||
func (d Dir) readDir(dirname string) ([]os.FileInfo, error) {
|
||||
func (d Dir) readDir(dirname string) ([]fs.DirEntry, error) {
|
||||
path, err := d.sanitize(dirname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ioutil.ReadDir(path)
|
||||
return os.ReadDir(path)
|
||||
}
|
||||
|
||||
// writeFile writes the data as a file at given path, restricted to a specific
|
||||
@@ -52,7 +52,7 @@ func (d Dir) writeFile(path string, data []byte) error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), defaultDirectoryMode); err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(path, data, defaultFileMode)
|
||||
return os.WriteFile(path, data, defaultFileMode)
|
||||
}
|
||||
|
||||
// deleteFile removes the file at the given path, restricted to a specific
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -21,7 +20,7 @@ func TestDir(t *testing.T) {
|
||||
{"d/e/ff", "d/e/ff"},
|
||||
{"d/e/ff/../gg", "d/e/gg"},
|
||||
}
|
||||
tdir, err := ioutil.TempDir("", "matchbox")
|
||||
tdir, err := os.MkdirTemp("", "matchbox")
|
||||
assert.Nil(t, err)
|
||||
defer os.RemoveAll(tdir)
|
||||
|
||||
@@ -29,7 +28,8 @@ func TestDir(t *testing.T) {
|
||||
dir := Dir(tdir)
|
||||
// write files rooted in the dir
|
||||
for _, c := range cases {
|
||||
dir.writeFile(c.path, []byte(c.expected))
|
||||
err = dir.writeFile(c.path, []byte(c.expected))
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
// ensure expected files were created
|
||||
for _, c := range cases {
|
||||
|
||||
@@ -3,7 +3,7 @@ package tlsutil
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// NewCertPool creates x509 certPool with provided CA files.
|
||||
@@ -11,7 +11,7 @@ func NewCertPool(CAFiles []string) (*x509.CertPool, error) {
|
||||
certPool := x509.NewCertPool()
|
||||
|
||||
for _, CAFile := range CAFiles {
|
||||
pemByte, err := ioutil.ReadFile(CAFile)
|
||||
pemByte, err := os.ReadFile(CAFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ nav:
|
||||
- Home: 'index.md'
|
||||
- 'Matchbox':
|
||||
- 'Concepts': 'matchbox.md'
|
||||
- 'Container Linux Config': 'container-linux-config.md'
|
||||
- 'Ignition': 'ignition.md'
|
||||
- 'Cloud-Config': 'cloud-config.md'
|
||||
- 'HTTP API': 'api-http.md'
|
||||
- 'gRPC API': 'api-grpc.md'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
mkdocs==1.1.2
|
||||
mkdocs-material==6.0.2
|
||||
pygments==2.6.1
|
||||
pymdown-extensions==7.1.0
|
||||
mkdocs==1.6.0
|
||||
mkdocs-material==9.5.19
|
||||
pygments==2.17.2
|
||||
pymdown-extensions==10.7.1
|
||||
|
||||
@@ -13,11 +13,9 @@ This will create:
|
||||
|
||||
```
|
||||
examples/assets/fedora-coreos/
|
||||
├── fedora-coreos-32.20200923.3.0-live-initramfs.x86_64.img
|
||||
├── fedora-coreos-32.20200923.3.0-live-kernel-x86_64
|
||||
├── fedora-coreos-32.20200923.3.0-live-rootfs.x86_64.img
|
||||
├── fedora-coreos-32.20200923.3.0-metal.x86_64.raw.xz
|
||||
└── fedora-coreos-32.20200923.3.0-metal.x86_64.raw.xz.sig
|
||||
├── fedora-coreos-36.20220906.3.2-live-initramfs.x86_64.img
|
||||
├── fedora-coreos-36.20220906.3.2-live-kernel-x86_64
|
||||
├── fedora-coreos-36.20220906.3.2-live-rootfs.x86_64.img
|
||||
```
|
||||
|
||||
## get-flatcar
|
||||
@@ -33,7 +31,7 @@ This will create:
|
||||
|
||||
```
|
||||
examples/assets/flatcar/
|
||||
└── 2605.6.0
|
||||
└── 3227.2.0
|
||||
├── Flatcar_Image_Signing_Key.asc
|
||||
├── flatcar_production_image.bin.bz2
|
||||
├── flatcar_production_image.bin.bz2.sig
|
||||
@@ -57,17 +55,3 @@ Create QEMU/KVM VMs which are configured to boot from the network. The `scripts/
|
||||
shutdown shutdown the QEMU/KVM nodes
|
||||
poweroff poweroff the QEMU/KVM nodes
|
||||
destroy destroy the QEMU/KVM nodes
|
||||
|
||||
## k8s-certgen
|
||||
|
||||
Generate TLS certificates needed for a multi-node Kubernetes cluster. See the [examples](../examples/README.md#assets).
|
||||
|
||||
$ ./scripts/tls/k8s-certgen -h
|
||||
Usage: k8s-certgen
|
||||
Options:
|
||||
-d DEST Destination for generated files (default: .examples/assets/tls)
|
||||
-s SERVER Reachable Server IP for kubeconfig (e.g. node1.example.com)
|
||||
-m MASTERS Controller Node Names/Addresses in SAN format (e.g. IP.1=10.3.0.1,DNS.1=node1.example.com)
|
||||
-w WORKERS Worker Node Names/Addresses in SAN format (e.g. DNS.1=node2.example.com,DNS.2=node3.example.com)
|
||||
-h Show help
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
set -eou pipefail
|
||||
|
||||
STREAM=${1:-"stable"}
|
||||
VERSION=${2:-"32.20200923.3.0"}
|
||||
VERSION=${2:-"36.20220906.3.2"}
|
||||
DEST_DIR=${3:-"$PWD/examples/assets"}
|
||||
DEST=$DEST_DIR/fedora-coreos
|
||||
BASE_URL=https://builds.coreos.fedoraproject.org/prod/streams/$STREAM/builds/$VERSION/x86_64
|
||||
@@ -34,10 +34,3 @@ curl -# $BASE_URL/fedora-coreos-$VERSION-live-initramfs.x86_64.img -o $DEST/fedo
|
||||
# rootfs
|
||||
echo "fedora-coreos-$VERSION-live-rootfs.x86_64.img"
|
||||
curl -# $BASE_URL/fedora-coreos-$VERSION-live-rootfs.x86_64.img -o $DEST/fedora-coreos-$VERSION-live-rootfs.x86_64.img
|
||||
|
||||
# Install image
|
||||
echo "fedora-coreos-$VERSION-metal.x86_64.raw.xz"
|
||||
curl -# $BASE_URL/fedora-coreos-$VERSION-metal.x86_64.raw.xz -o $DEST/fedora-coreos-$VERSION-metal.x86_64.raw.xz
|
||||
echo "fedora-coreos-$VERSION-metal.x86_64.raw.xz.sig"
|
||||
curl -# $BASE_URL/fedora-coreos-$VERSION-metal.x86_64.raw.xz.sig -o $DEST/fedora-coreos-$VERSION-metal.x86_64.raw.xz.sig
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ fi
|
||||
echo "Downloading Flatcar Linux $CHANNEL $VERSION images and sigs to $DEST"
|
||||
|
||||
echo "Flatcar Linux Image Signing Key"
|
||||
curl -# https://www.flatcar-linux.org/security/image-signing-key/Flatcar_Image_Signing_Key.asc -o "${DEST}/Flatcar_Image_Signing_Key.asc"
|
||||
curl -# https://www.flatcar.org/security/image-signing-key/Flatcar_Image_Signing_Key.asc -o "${DEST}/Flatcar_Image_Signing_Key.asc"
|
||||
$GPG --import <"$DEST/Flatcar_Image_Signing_Key.asc" || true
|
||||
|
||||
# Version
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# Manage VM nodes which have a specific set of hardware attributes.
|
||||
|
||||
VM_MEMORY=${VM_MEMORY:-3072}
|
||||
VM_MEMORY=${VM_MEMORY:-3048}
|
||||
VM_DISK=${VM_DISK:-10}
|
||||
|
||||
if [ "$EUID" -ne 0 ]
|
||||
@@ -35,7 +35,9 @@ function usage {
|
||||
echo -e "\tdestroy\t\tdestroy the QEMU/KVM nodes"
|
||||
}
|
||||
|
||||
COMMON_VIRT_OPTS="--memory=${VM_MEMORY} --vcpus=1 --disk pool=default,size=${VM_DISK} --os-type=linux --os-variant=generic --noautoconsole --events on_poweroff=preserve"
|
||||
# --install=no_install=yes is a workaround for a virt-manager 4.1.0 regression
|
||||
# https://github.com/virt-manager/virt-manager/issues/426
|
||||
COMMON_VIRT_OPTS="--memory=${VM_MEMORY} --vcpus=2 --disk pool=default,size=${VM_DISK} --os-variant=fedora-coreos-stable --noautoconsole --install=no_install=yes"
|
||||
|
||||
NODE1_NAME=node1
|
||||
NODE1_MAC=52:54:00:a1:9c:ae
|
||||
@@ -49,10 +51,10 @@ NODE3_MAC=52:54:00:c3:61:77
|
||||
function create_docker {
|
||||
virt-install --name $NODE1_NAME --network=bridge:docker0,mac=$NODE1_MAC $COMMON_VIRT_OPTS --boot=hd,network
|
||||
virt-install --name $NODE2_NAME --network=bridge:docker0,mac=$NODE2_MAC $COMMON_VIRT_OPTS --boot=hd,network
|
||||
virt-install --name $NODE3_NAME --network=bridge:docker0,mac=$NODE3_MAC $COMMON_VIRT_OPTS --boot=hd,network
|
||||
# virt-install --name $NODE3_NAME --network=bridge:docker0,mac=$NODE3_MAC $COMMON_VIRT_OPTS --boot=hd,network
|
||||
}
|
||||
|
||||
nodes=(node1 node2 node3)
|
||||
nodes=(node1 node2)
|
||||
|
||||
function start {
|
||||
for node in ${nodes[@]}; do
|
||||
|
||||
@@ -6,7 +6,7 @@ rm -f ca.key ca.crt server.key server.csr server.crt client.key client.csr clien
|
||||
rm -rf certs crl newcerts
|
||||
|
||||
if [ -z $SAN ]
|
||||
then echo "Set SAN with a DNS or IP for matchbox (e.g. export SAN=DNS.1:matchbox.example.com,IP.1:192.168.1.42)."
|
||||
then echo "Set SAN with a DNS or IP for matchbox (e.g. export SAN=DNS.1:matchbox.example.com,IP.1:172.17.0.2)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
10
vendor/github.com/ajeddeloh/go-json/README
generated
vendored
10
vendor/github.com/ajeddeloh/go-json/README
generated
vendored
@@ -1,10 +0,0 @@
|
||||
This is a fork of go's encoding/json library. It adds the a third target for unmarshalling, json.Node.
|
||||
Unmarshalling to a Node behaves similarilarly to unmarshalling to an interface{}, except it also records
|
||||
the offsets for the start and end of the value that was unmarshalled and, if the value was part of a json
|
||||
object, it also records the offsets of the start and end of the object's key. The Value field of the Node
|
||||
will be unmarshalled to the same types as if it were an interface{}, except in the case of arrays and
|
||||
objects. In those case it will be unmarshalled to a []Node or map[string]Node instead []interface{} or
|
||||
map[string]interface{} for arrays and objects, respectively.
|
||||
|
||||
There are two branchs, go15 and go16. go15 contains the modified go1.5 library and go16 contains the
|
||||
modified go1.6 library.
|
||||
1226
vendor/github.com/ajeddeloh/go-json/decode.go
generated
vendored
1226
vendor/github.com/ajeddeloh/go-json/decode.go
generated
vendored
File diff suppressed because it is too large
Load Diff
1194
vendor/github.com/ajeddeloh/go-json/encode.go
generated
vendored
1194
vendor/github.com/ajeddeloh/go-json/encode.go
generated
vendored
File diff suppressed because it is too large
Load Diff
143
vendor/github.com/ajeddeloh/go-json/fold.go
generated
vendored
143
vendor/github.com/ajeddeloh/go-json/fold.go
generated
vendored
@@ -1,143 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
caseMask = ^byte(0x20) // Mask to ignore case in ASCII.
|
||||
kelvin = '\u212a'
|
||||
smallLongEss = '\u017f'
|
||||
)
|
||||
|
||||
// foldFunc returns one of four different case folding equivalence
|
||||
// functions, from most general (and slow) to fastest:
|
||||
//
|
||||
// 1) bytes.EqualFold, if the key s contains any non-ASCII UTF-8
|
||||
// 2) equalFoldRight, if s contains special folding ASCII ('k', 'K', 's', 'S')
|
||||
// 3) asciiEqualFold, no special, but includes non-letters (including _)
|
||||
// 4) simpleLetterEqualFold, no specials, no non-letters.
|
||||
//
|
||||
// The letters S and K are special because they map to 3 runes, not just 2:
|
||||
// * S maps to s and to U+017F 'ſ' Latin small letter long s
|
||||
// * k maps to K and to U+212A 'K' Kelvin sign
|
||||
// See https://play.golang.org/p/tTxjOc0OGo
|
||||
//
|
||||
// The returned function is specialized for matching against s and
|
||||
// should only be given s. It's not curried for performance reasons.
|
||||
func foldFunc(s []byte) func(s, t []byte) bool {
|
||||
nonLetter := false
|
||||
special := false // special letter
|
||||
for _, b := range s {
|
||||
if b >= utf8.RuneSelf {
|
||||
return bytes.EqualFold
|
||||
}
|
||||
upper := b & caseMask
|
||||
if upper < 'A' || upper > 'Z' {
|
||||
nonLetter = true
|
||||
} else if upper == 'K' || upper == 'S' {
|
||||
// See above for why these letters are special.
|
||||
special = true
|
||||
}
|
||||
}
|
||||
if special {
|
||||
return equalFoldRight
|
||||
}
|
||||
if nonLetter {
|
||||
return asciiEqualFold
|
||||
}
|
||||
return simpleLetterEqualFold
|
||||
}
|
||||
|
||||
// equalFoldRight is a specialization of bytes.EqualFold when s is
|
||||
// known to be all ASCII (including punctuation), but contains an 's',
|
||||
// 'S', 'k', or 'K', requiring a Unicode fold on the bytes in t.
|
||||
// See comments on foldFunc.
|
||||
func equalFoldRight(s, t []byte) bool {
|
||||
for _, sb := range s {
|
||||
if len(t) == 0 {
|
||||
return false
|
||||
}
|
||||
tb := t[0]
|
||||
if tb < utf8.RuneSelf {
|
||||
if sb != tb {
|
||||
sbUpper := sb & caseMask
|
||||
if 'A' <= sbUpper && sbUpper <= 'Z' {
|
||||
if sbUpper != tb&caseMask {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
t = t[1:]
|
||||
continue
|
||||
}
|
||||
// sb is ASCII and t is not. t must be either kelvin
|
||||
// sign or long s; sb must be s, S, k, or K.
|
||||
tr, size := utf8.DecodeRune(t)
|
||||
switch sb {
|
||||
case 's', 'S':
|
||||
if tr != smallLongEss {
|
||||
return false
|
||||
}
|
||||
case 'k', 'K':
|
||||
if tr != kelvin {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
t = t[size:]
|
||||
|
||||
}
|
||||
if len(t) > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// asciiEqualFold is a specialization of bytes.EqualFold for use when
|
||||
// s is all ASCII (but may contain non-letters) and contains no
|
||||
// special-folding letters.
|
||||
// See comments on foldFunc.
|
||||
func asciiEqualFold(s, t []byte) bool {
|
||||
if len(s) != len(t) {
|
||||
return false
|
||||
}
|
||||
for i, sb := range s {
|
||||
tb := t[i]
|
||||
if sb == tb {
|
||||
continue
|
||||
}
|
||||
if ('a' <= sb && sb <= 'z') || ('A' <= sb && sb <= 'Z') {
|
||||
if sb&caseMask != tb&caseMask {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// simpleLetterEqualFold is a specialization of bytes.EqualFold for
|
||||
// use when s is all ASCII letters (no underscores, etc) and also
|
||||
// doesn't contain 'k', 'K', 's', or 'S'.
|
||||
// See comments on foldFunc.
|
||||
func simpleLetterEqualFold(s, t []byte) bool {
|
||||
if len(s) != len(t) {
|
||||
return false
|
||||
}
|
||||
for i, b := range s {
|
||||
if b&caseMask != t[i]&caseMask {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
137
vendor/github.com/ajeddeloh/go-json/indent.go
generated
vendored
137
vendor/github.com/ajeddeloh/go-json/indent.go
generated
vendored
@@ -1,137 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package json
|
||||
|
||||
import "bytes"
|
||||
|
||||
// Compact appends to dst the JSON-encoded src with
|
||||
// insignificant space characters elided.
|
||||
func Compact(dst *bytes.Buffer, src []byte) error {
|
||||
return compact(dst, src, false)
|
||||
}
|
||||
|
||||
func compact(dst *bytes.Buffer, src []byte, escape bool) error {
|
||||
origLen := dst.Len()
|
||||
var scan scanner
|
||||
scan.reset()
|
||||
start := 0
|
||||
for i, c := range src {
|
||||
if escape && (c == '<' || c == '>' || c == '&') {
|
||||
if start < i {
|
||||
dst.Write(src[start:i])
|
||||
}
|
||||
dst.WriteString(`\u00`)
|
||||
dst.WriteByte(hex[c>>4])
|
||||
dst.WriteByte(hex[c&0xF])
|
||||
start = i + 1
|
||||
}
|
||||
// Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
|
||||
if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
|
||||
if start < i {
|
||||
dst.Write(src[start:i])
|
||||
}
|
||||
dst.WriteString(`\u202`)
|
||||
dst.WriteByte(hex[src[i+2]&0xF])
|
||||
start = i + 3
|
||||
}
|
||||
v := scan.step(&scan, int(c))
|
||||
if v >= scanSkipSpace {
|
||||
if v == scanError {
|
||||
break
|
||||
}
|
||||
if start < i {
|
||||
dst.Write(src[start:i])
|
||||
}
|
||||
start = i + 1
|
||||
}
|
||||
}
|
||||
if scan.eof() == scanError {
|
||||
dst.Truncate(origLen)
|
||||
return scan.err
|
||||
}
|
||||
if start < len(src) {
|
||||
dst.Write(src[start:])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newline(dst *bytes.Buffer, prefix, indent string, depth int) {
|
||||
dst.WriteByte('\n')
|
||||
dst.WriteString(prefix)
|
||||
for i := 0; i < depth; i++ {
|
||||
dst.WriteString(indent)
|
||||
}
|
||||
}
|
||||
|
||||
// Indent appends to dst an indented form of the JSON-encoded src.
|
||||
// Each element in a JSON object or array begins on a new,
|
||||
// indented line beginning with prefix followed by one or more
|
||||
// copies of indent according to the indentation nesting.
|
||||
// The data appended to dst does not begin with the prefix nor
|
||||
// any indentation, and has no trailing newline, to make it
|
||||
// easier to embed inside other formatted JSON data.
|
||||
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
|
||||
origLen := dst.Len()
|
||||
var scan scanner
|
||||
scan.reset()
|
||||
needIndent := false
|
||||
depth := 0
|
||||
for _, c := range src {
|
||||
scan.bytes++
|
||||
v := scan.step(&scan, int(c))
|
||||
if v == scanSkipSpace {
|
||||
continue
|
||||
}
|
||||
if v == scanError {
|
||||
break
|
||||
}
|
||||
if needIndent && v != scanEndObject && v != scanEndArray {
|
||||
needIndent = false
|
||||
depth++
|
||||
newline(dst, prefix, indent, depth)
|
||||
}
|
||||
|
||||
// Emit semantically uninteresting bytes
|
||||
// (in particular, punctuation in strings) unmodified.
|
||||
if v == scanContinue {
|
||||
dst.WriteByte(c)
|
||||
continue
|
||||
}
|
||||
|
||||
// Add spacing around real punctuation.
|
||||
switch c {
|
||||
case '{', '[':
|
||||
// delay indent so that empty object and array are formatted as {} and [].
|
||||
needIndent = true
|
||||
dst.WriteByte(c)
|
||||
|
||||
case ',':
|
||||
dst.WriteByte(c)
|
||||
newline(dst, prefix, indent, depth)
|
||||
|
||||
case ':':
|
||||
dst.WriteByte(c)
|
||||
dst.WriteByte(' ')
|
||||
|
||||
case '}', ']':
|
||||
if needIndent {
|
||||
// suppress indent in empty object/array
|
||||
needIndent = false
|
||||
} else {
|
||||
depth--
|
||||
newline(dst, prefix, indent, depth)
|
||||
}
|
||||
dst.WriteByte(c)
|
||||
|
||||
default:
|
||||
dst.WriteByte(c)
|
||||
}
|
||||
}
|
||||
if scan.eof() == scanError {
|
||||
dst.Truncate(origLen)
|
||||
return scan.err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
630
vendor/github.com/ajeddeloh/go-json/scanner.go
generated
vendored
630
vendor/github.com/ajeddeloh/go-json/scanner.go
generated
vendored
@@ -1,630 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package json
|
||||
|
||||
// JSON value parser state machine.
|
||||
// Just about at the limit of what is reasonable to write by hand.
|
||||
// Some parts are a bit tedious, but overall it nicely factors out the
|
||||
// otherwise common code from the multiple scanning functions
|
||||
// in this package (Compact, Indent, checkValid, nextValue, etc).
|
||||
//
|
||||
// This file starts with two simple examples using the scanner
|
||||
// before diving into the scanner itself.
|
||||
|
||||
import "strconv"
|
||||
|
||||
// checkValid verifies that data is valid JSON-encoded data.
|
||||
// scan is passed in for use by checkValid to avoid an allocation.
|
||||
func checkValid(data []byte, scan *scanner) error {
|
||||
scan.reset()
|
||||
for _, c := range data {
|
||||
scan.bytes++
|
||||
if scan.step(scan, int(c)) == scanError {
|
||||
return scan.err
|
||||
}
|
||||
}
|
||||
if scan.eof() == scanError {
|
||||
return scan.err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// nextValue splits data after the next whole JSON value,
|
||||
// returning that value and the bytes that follow it as separate slices.
|
||||
// scan is passed in for use by nextValue to avoid an allocation.
|
||||
func nextValue(data []byte, scan *scanner) (value, rest []byte, err error) {
|
||||
scan.reset()
|
||||
for i, c := range data {
|
||||
v := scan.step(scan, int(c))
|
||||
if v >= scanEndObject {
|
||||
switch v {
|
||||
// probe the scanner with a space to determine whether we will
|
||||
// get scanEnd on the next character. Otherwise, if the next character
|
||||
// is not a space, scanEndTop allocates a needless error.
|
||||
case scanEndObject, scanEndArray:
|
||||
if scan.step(scan, ' ') == scanEnd {
|
||||
return data[:i+1], data[i+1:], nil
|
||||
}
|
||||
case scanError:
|
||||
return nil, nil, scan.err
|
||||
case scanEnd:
|
||||
return data[0:i], data[i:], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if scan.eof() == scanError {
|
||||
return nil, nil, scan.err
|
||||
}
|
||||
return data, nil, nil
|
||||
}
|
||||
|
||||
// A SyntaxError is a description of a JSON syntax error.
|
||||
type SyntaxError struct {
|
||||
msg string // description of error
|
||||
Offset int64 // error occurred after reading Offset bytes
|
||||
}
|
||||
|
||||
func (e *SyntaxError) Error() string { return e.msg }
|
||||
|
||||
// A scanner is a JSON scanning state machine.
|
||||
// Callers call scan.reset() and then pass bytes in one at a time
|
||||
// by calling scan.step(&scan, c) for each byte.
|
||||
// The return value, referred to as an opcode, tells the
|
||||
// caller about significant parsing events like beginning
|
||||
// and ending literals, objects, and arrays, so that the
|
||||
// caller can follow along if it wishes.
|
||||
// The return value scanEnd indicates that a single top-level
|
||||
// JSON value has been completed, *before* the byte that
|
||||
// just got passed in. (The indication must be delayed in order
|
||||
// to recognize the end of numbers: is 123 a whole value or
|
||||
// the beginning of 12345e+6?).
|
||||
type scanner struct {
|
||||
// The step is a func to be called to execute the next transition.
|
||||
// Also tried using an integer constant and a single func
|
||||
// with a switch, but using the func directly was 10% faster
|
||||
// on a 64-bit Mac Mini, and it's nicer to read.
|
||||
step func(*scanner, int) int
|
||||
|
||||
// Reached end of top-level value.
|
||||
endTop bool
|
||||
|
||||
// Stack of what we're in the middle of - array values, object keys, object values.
|
||||
parseState []int
|
||||
|
||||
// Error that happened, if any.
|
||||
err error
|
||||
|
||||
// 1-byte redo (see undo method)
|
||||
redo bool
|
||||
redoCode int
|
||||
redoState func(*scanner, int) int
|
||||
|
||||
// total bytes consumed, updated by decoder.Decode
|
||||
bytes int64
|
||||
}
|
||||
|
||||
// These values are returned by the state transition functions
|
||||
// assigned to scanner.state and the method scanner.eof.
|
||||
// They give details about the current state of the scan that
|
||||
// callers might be interested to know about.
|
||||
// It is okay to ignore the return value of any particular
|
||||
// call to scanner.state: if one call returns scanError,
|
||||
// every subsequent call will return scanError too.
|
||||
const (
|
||||
// Continue.
|
||||
scanContinue = iota // uninteresting byte
|
||||
scanBeginLiteral // end implied by next result != scanContinue
|
||||
scanBeginObject // begin object
|
||||
scanObjectKey // just finished object key (string)
|
||||
scanObjectValue // just finished non-last object value
|
||||
scanEndObject // end object (implies scanObjectValue if possible)
|
||||
scanBeginArray // begin array
|
||||
scanArrayValue // just finished array value
|
||||
scanEndArray // end array (implies scanArrayValue if possible)
|
||||
scanSkipSpace // space byte; can skip; known to be last "continue" result
|
||||
|
||||
// Stop.
|
||||
scanEnd // top-level value ended *before* this byte; known to be first "stop" result
|
||||
scanError // hit an error, scanner.err.
|
||||
)
|
||||
|
||||
// These values are stored in the parseState stack.
|
||||
// They give the current state of a composite value
|
||||
// being scanned. If the parser is inside a nested value
|
||||
// the parseState describes the nested state, outermost at entry 0.
|
||||
const (
|
||||
parseObjectKey = iota // parsing object key (before colon)
|
||||
parseObjectValue // parsing object value (after colon)
|
||||
parseArrayValue // parsing array value
|
||||
)
|
||||
|
||||
// reset prepares the scanner for use.
|
||||
// It must be called before calling s.step.
|
||||
func (s *scanner) reset() {
|
||||
s.step = stateBeginValue
|
||||
s.parseState = s.parseState[0:0]
|
||||
s.err = nil
|
||||
s.redo = false
|
||||
s.endTop = false
|
||||
}
|
||||
|
||||
// eof tells the scanner that the end of input has been reached.
|
||||
// It returns a scan status just as s.step does.
|
||||
func (s *scanner) eof() int {
|
||||
if s.err != nil {
|
||||
return scanError
|
||||
}
|
||||
if s.endTop {
|
||||
return scanEnd
|
||||
}
|
||||
s.step(s, ' ')
|
||||
if s.endTop {
|
||||
return scanEnd
|
||||
}
|
||||
if s.err == nil {
|
||||
s.err = &SyntaxError{"unexpected end of JSON input", s.bytes}
|
||||
}
|
||||
return scanError
|
||||
}
|
||||
|
||||
// pushParseState pushes a new parse state p onto the parse stack.
|
||||
func (s *scanner) pushParseState(p int) {
|
||||
s.parseState = append(s.parseState, p)
|
||||
}
|
||||
|
||||
// popParseState pops a parse state (already obtained) off the stack
|
||||
// and updates s.step accordingly.
|
||||
func (s *scanner) popParseState() {
|
||||
n := len(s.parseState) - 1
|
||||
s.parseState = s.parseState[0:n]
|
||||
s.redo = false
|
||||
if n == 0 {
|
||||
s.step = stateEndTop
|
||||
s.endTop = true
|
||||
} else {
|
||||
s.step = stateEndValue
|
||||
}
|
||||
}
|
||||
|
||||
func isSpace(c rune) bool {
|
||||
return c == ' ' || c == '\t' || c == '\r' || c == '\n'
|
||||
}
|
||||
|
||||
// stateBeginValueOrEmpty is the state after reading `[`.
|
||||
func stateBeginValueOrEmpty(s *scanner, c int) int {
|
||||
if c <= ' ' && isSpace(rune(c)) {
|
||||
return scanSkipSpace
|
||||
}
|
||||
if c == ']' {
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
return stateBeginValue(s, c)
|
||||
}
|
||||
|
||||
// stateBeginValue is the state at the beginning of the input.
|
||||
func stateBeginValue(s *scanner, c int) int {
|
||||
if c <= ' ' && isSpace(rune(c)) {
|
||||
return scanSkipSpace
|
||||
}
|
||||
switch c {
|
||||
case '{':
|
||||
s.step = stateBeginStringOrEmpty
|
||||
s.pushParseState(parseObjectKey)
|
||||
return scanBeginObject
|
||||
case '[':
|
||||
s.step = stateBeginValueOrEmpty
|
||||
s.pushParseState(parseArrayValue)
|
||||
return scanBeginArray
|
||||
case '"':
|
||||
s.step = stateInString
|
||||
return scanBeginLiteral
|
||||
case '-':
|
||||
s.step = stateNeg
|
||||
return scanBeginLiteral
|
||||
case '0': // beginning of 0.123
|
||||
s.step = state0
|
||||
return scanBeginLiteral
|
||||
case 't': // beginning of true
|
||||
s.step = stateT
|
||||
return scanBeginLiteral
|
||||
case 'f': // beginning of false
|
||||
s.step = stateF
|
||||
return scanBeginLiteral
|
||||
case 'n': // beginning of null
|
||||
s.step = stateN
|
||||
return scanBeginLiteral
|
||||
}
|
||||
if '1' <= c && c <= '9' { // beginning of 1234.5
|
||||
s.step = state1
|
||||
return scanBeginLiteral
|
||||
}
|
||||
return s.error(c, "looking for beginning of value")
|
||||
}
|
||||
|
||||
// stateBeginStringOrEmpty is the state after reading `{`.
|
||||
func stateBeginStringOrEmpty(s *scanner, c int) int {
|
||||
if c <= ' ' && isSpace(rune(c)) {
|
||||
return scanSkipSpace
|
||||
}
|
||||
if c == '}' {
|
||||
n := len(s.parseState)
|
||||
s.parseState[n-1] = parseObjectValue
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
return stateBeginString(s, c)
|
||||
}
|
||||
|
||||
// stateBeginString is the state after reading `{"key": value,`.
|
||||
func stateBeginString(s *scanner, c int) int {
|
||||
if c <= ' ' && isSpace(rune(c)) {
|
||||
return scanSkipSpace
|
||||
}
|
||||
if c == '"' {
|
||||
s.step = stateInString
|
||||
return scanBeginLiteral
|
||||
}
|
||||
return s.error(c, "looking for beginning of object key string")
|
||||
}
|
||||
|
||||
// stateEndValue is the state after completing a value,
|
||||
// such as after reading `{}` or `true` or `["x"`.
|
||||
func stateEndValue(s *scanner, c int) int {
|
||||
n := len(s.parseState)
|
||||
if n == 0 {
|
||||
// Completed top-level before the current byte.
|
||||
s.step = stateEndTop
|
||||
s.endTop = true
|
||||
return stateEndTop(s, c)
|
||||
}
|
||||
if c <= ' ' && isSpace(rune(c)) {
|
||||
s.step = stateEndValue
|
||||
return scanSkipSpace
|
||||
}
|
||||
ps := s.parseState[n-1]
|
||||
switch ps {
|
||||
case parseObjectKey:
|
||||
if c == ':' {
|
||||
s.parseState[n-1] = parseObjectValue
|
||||
s.step = stateBeginValue
|
||||
return scanObjectKey
|
||||
}
|
||||
return s.error(c, "after object key")
|
||||
case parseObjectValue:
|
||||
if c == ',' {
|
||||
s.parseState[n-1] = parseObjectKey
|
||||
s.step = stateBeginString
|
||||
return scanObjectValue
|
||||
}
|
||||
if c == '}' {
|
||||
s.popParseState()
|
||||
return scanEndObject
|
||||
}
|
||||
return s.error(c, "after object key:value pair")
|
||||
case parseArrayValue:
|
||||
if c == ',' {
|
||||
s.step = stateBeginValue
|
||||
return scanArrayValue
|
||||
}
|
||||
if c == ']' {
|
||||
s.popParseState()
|
||||
return scanEndArray
|
||||
}
|
||||
return s.error(c, "after array element")
|
||||
}
|
||||
return s.error(c, "")
|
||||
}
|
||||
|
||||
// stateEndTop is the state after finishing the top-level value,
|
||||
// such as after reading `{}` or `[1,2,3]`.
|
||||
// Only space characters should be seen now.
|
||||
func stateEndTop(s *scanner, c int) int {
|
||||
if c != ' ' && c != '\t' && c != '\r' && c != '\n' {
|
||||
// Complain about non-space byte on next call.
|
||||
s.error(c, "after top-level value")
|
||||
}
|
||||
return scanEnd
|
||||
}
|
||||
|
||||
// stateInString is the state after reading `"`.
|
||||
func stateInString(s *scanner, c int) int {
|
||||
if c == '"' {
|
||||
s.step = stateEndValue
|
||||
return scanContinue
|
||||
}
|
||||
if c == '\\' {
|
||||
s.step = stateInStringEsc
|
||||
return scanContinue
|
||||
}
|
||||
if c < 0x20 {
|
||||
return s.error(c, "in string literal")
|
||||
}
|
||||
return scanContinue
|
||||
}
|
||||
|
||||
// stateInStringEsc is the state after reading `"\` during a quoted string.
|
||||
func stateInStringEsc(s *scanner, c int) int {
|
||||
switch c {
|
||||
case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
|
||||
s.step = stateInString
|
||||
return scanContinue
|
||||
}
|
||||
if c == 'u' {
|
||||
s.step = stateInStringEscU
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in string escape code")
|
||||
}
|
||||
|
||||
// stateInStringEscU is the state after reading `"\u` during a quoted string.
|
||||
func stateInStringEscU(s *scanner, c int) int {
|
||||
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||
s.step = stateInStringEscU1
|
||||
return scanContinue
|
||||
}
|
||||
// numbers
|
||||
return s.error(c, "in \\u hexadecimal character escape")
|
||||
}
|
||||
|
||||
// stateInStringEscU1 is the state after reading `"\u1` during a quoted string.
|
||||
func stateInStringEscU1(s *scanner, c int) int {
|
||||
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||
s.step = stateInStringEscU12
|
||||
return scanContinue
|
||||
}
|
||||
// numbers
|
||||
return s.error(c, "in \\u hexadecimal character escape")
|
||||
}
|
||||
|
||||
// stateInStringEscU12 is the state after reading `"\u12` during a quoted string.
|
||||
func stateInStringEscU12(s *scanner, c int) int {
|
||||
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||
s.step = stateInStringEscU123
|
||||
return scanContinue
|
||||
}
|
||||
// numbers
|
||||
return s.error(c, "in \\u hexadecimal character escape")
|
||||
}
|
||||
|
||||
// stateInStringEscU123 is the state after reading `"\u123` during a quoted string.
|
||||
func stateInStringEscU123(s *scanner, c int) int {
|
||||
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
|
||||
s.step = stateInString
|
||||
return scanContinue
|
||||
}
|
||||
// numbers
|
||||
return s.error(c, "in \\u hexadecimal character escape")
|
||||
}
|
||||
|
||||
// stateNeg is the state after reading `-` during a number.
|
||||
func stateNeg(s *scanner, c int) int {
|
||||
if c == '0' {
|
||||
s.step = state0
|
||||
return scanContinue
|
||||
}
|
||||
if '1' <= c && c <= '9' {
|
||||
s.step = state1
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in numeric literal")
|
||||
}
|
||||
|
||||
// state1 is the state after reading a non-zero integer during a number,
|
||||
// such as after reading `1` or `100` but not `0`.
|
||||
func state1(s *scanner, c int) int {
|
||||
if '0' <= c && c <= '9' {
|
||||
s.step = state1
|
||||
return scanContinue
|
||||
}
|
||||
return state0(s, c)
|
||||
}
|
||||
|
||||
// state0 is the state after reading `0` during a number.
|
||||
func state0(s *scanner, c int) int {
|
||||
if c == '.' {
|
||||
s.step = stateDot
|
||||
return scanContinue
|
||||
}
|
||||
if c == 'e' || c == 'E' {
|
||||
s.step = stateE
|
||||
return scanContinue
|
||||
}
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
|
||||
// stateDot is the state after reading the integer and decimal point in a number,
|
||||
// such as after reading `1.`.
|
||||
func stateDot(s *scanner, c int) int {
|
||||
if '0' <= c && c <= '9' {
|
||||
s.step = stateDot0
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "after decimal point in numeric literal")
|
||||
}
|
||||
|
||||
// stateDot0 is the state after reading the integer, decimal point, and subsequent
|
||||
// digits of a number, such as after reading `3.14`.
|
||||
func stateDot0(s *scanner, c int) int {
|
||||
if '0' <= c && c <= '9' {
|
||||
s.step = stateDot0
|
||||
return scanContinue
|
||||
}
|
||||
if c == 'e' || c == 'E' {
|
||||
s.step = stateE
|
||||
return scanContinue
|
||||
}
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
|
||||
// stateE is the state after reading the mantissa and e in a number,
|
||||
// such as after reading `314e` or `0.314e`.
|
||||
func stateE(s *scanner, c int) int {
|
||||
if c == '+' {
|
||||
s.step = stateESign
|
||||
return scanContinue
|
||||
}
|
||||
if c == '-' {
|
||||
s.step = stateESign
|
||||
return scanContinue
|
||||
}
|
||||
return stateESign(s, c)
|
||||
}
|
||||
|
||||
// stateESign is the state after reading the mantissa, e, and sign in a number,
|
||||
// such as after reading `314e-` or `0.314e+`.
|
||||
func stateESign(s *scanner, c int) int {
|
||||
if '0' <= c && c <= '9' {
|
||||
s.step = stateE0
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in exponent of numeric literal")
|
||||
}
|
||||
|
||||
// stateE0 is the state after reading the mantissa, e, optional sign,
|
||||
// and at least one digit of the exponent in a number,
|
||||
// such as after reading `314e-2` or `0.314e+1` or `3.14e0`.
|
||||
func stateE0(s *scanner, c int) int {
|
||||
if '0' <= c && c <= '9' {
|
||||
s.step = stateE0
|
||||
return scanContinue
|
||||
}
|
||||
return stateEndValue(s, c)
|
||||
}
|
||||
|
||||
// stateT is the state after reading `t`.
|
||||
func stateT(s *scanner, c int) int {
|
||||
if c == 'r' {
|
||||
s.step = stateTr
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal true (expecting 'r')")
|
||||
}
|
||||
|
||||
// stateTr is the state after reading `tr`.
|
||||
func stateTr(s *scanner, c int) int {
|
||||
if c == 'u' {
|
||||
s.step = stateTru
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal true (expecting 'u')")
|
||||
}
|
||||
|
||||
// stateTru is the state after reading `tru`.
|
||||
func stateTru(s *scanner, c int) int {
|
||||
if c == 'e' {
|
||||
s.step = stateEndValue
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal true (expecting 'e')")
|
||||
}
|
||||
|
||||
// stateF is the state after reading `f`.
|
||||
func stateF(s *scanner, c int) int {
|
||||
if c == 'a' {
|
||||
s.step = stateFa
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal false (expecting 'a')")
|
||||
}
|
||||
|
||||
// stateFa is the state after reading `fa`.
|
||||
func stateFa(s *scanner, c int) int {
|
||||
if c == 'l' {
|
||||
s.step = stateFal
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal false (expecting 'l')")
|
||||
}
|
||||
|
||||
// stateFal is the state after reading `fal`.
|
||||
func stateFal(s *scanner, c int) int {
|
||||
if c == 's' {
|
||||
s.step = stateFals
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal false (expecting 's')")
|
||||
}
|
||||
|
||||
// stateFals is the state after reading `fals`.
|
||||
func stateFals(s *scanner, c int) int {
|
||||
if c == 'e' {
|
||||
s.step = stateEndValue
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal false (expecting 'e')")
|
||||
}
|
||||
|
||||
// stateN is the state after reading `n`.
|
||||
func stateN(s *scanner, c int) int {
|
||||
if c == 'u' {
|
||||
s.step = stateNu
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal null (expecting 'u')")
|
||||
}
|
||||
|
||||
// stateNu is the state after reading `nu`.
|
||||
func stateNu(s *scanner, c int) int {
|
||||
if c == 'l' {
|
||||
s.step = stateNul
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal null (expecting 'l')")
|
||||
}
|
||||
|
||||
// stateNul is the state after reading `nul`.
|
||||
func stateNul(s *scanner, c int) int {
|
||||
if c == 'l' {
|
||||
s.step = stateEndValue
|
||||
return scanContinue
|
||||
}
|
||||
return s.error(c, "in literal null (expecting 'l')")
|
||||
}
|
||||
|
||||
// stateError is the state after reaching a syntax error,
|
||||
// such as after reading `[1}` or `5.1.2`.
|
||||
func stateError(s *scanner, c int) int {
|
||||
return scanError
|
||||
}
|
||||
|
||||
// error records an error and switches to the error state.
|
||||
func (s *scanner) error(c int, context string) int {
|
||||
s.step = stateError
|
||||
s.err = &SyntaxError{"invalid character " + quoteChar(c) + " " + context, s.bytes}
|
||||
return scanError
|
||||
}
|
||||
|
||||
// quoteChar formats c as a quoted character literal
|
||||
func quoteChar(c int) string {
|
||||
// special cases - different from quoted strings
|
||||
if c == '\'' {
|
||||
return `'\''`
|
||||
}
|
||||
if c == '"' {
|
||||
return `'"'`
|
||||
}
|
||||
|
||||
// use quoted string with different quotation marks
|
||||
s := strconv.Quote(string(c))
|
||||
return "'" + s[1:len(s)-1] + "'"
|
||||
}
|
||||
|
||||
// undo causes the scanner to return scanCode from the next state transition.
|
||||
// This gives callers a simple 1-byte undo mechanism.
|
||||
func (s *scanner) undo(scanCode int) {
|
||||
if s.redo {
|
||||
panic("json: invalid use of scanner")
|
||||
}
|
||||
s.redoCode = scanCode
|
||||
s.redoState = s.step
|
||||
s.step = stateRedo
|
||||
s.redo = true
|
||||
}
|
||||
|
||||
// stateRedo helps implement the scanner's 1-byte undo.
|
||||
func stateRedo(s *scanner, c int) int {
|
||||
s.redo = false
|
||||
s.step = s.redoState
|
||||
return s.redoCode
|
||||
}
|
||||
480
vendor/github.com/ajeddeloh/go-json/stream.go
generated
vendored
480
vendor/github.com/ajeddeloh/go-json/stream.go
generated
vendored
@@ -1,480 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// A Decoder reads and decodes JSON objects from an input stream.
|
||||
type Decoder struct {
|
||||
r io.Reader
|
||||
buf []byte
|
||||
d decodeState
|
||||
scanp int // start of unread data in buf
|
||||
scan scanner
|
||||
err error
|
||||
|
||||
tokenState int
|
||||
tokenStack []int
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder that reads from r.
|
||||
//
|
||||
// The decoder introduces its own buffering and may
|
||||
// read data from r beyond the JSON values requested.
|
||||
func NewDecoder(r io.Reader) *Decoder {
|
||||
return &Decoder{r: r}
|
||||
}
|
||||
|
||||
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
|
||||
// Number instead of as a float64.
|
||||
func (dec *Decoder) UseNumber() { dec.d.useNumber = true }
|
||||
|
||||
// Decode reads the next JSON-encoded value from its
|
||||
// input and stores it in the value pointed to by v.
|
||||
//
|
||||
// See the documentation for Unmarshal for details about
|
||||
// the conversion of JSON into a Go value.
|
||||
func (dec *Decoder) Decode(v interface{}) error {
|
||||
if dec.err != nil {
|
||||
return dec.err
|
||||
}
|
||||
|
||||
if err := dec.tokenPrepareForDecode(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !dec.tokenValueAllowed() {
|
||||
return &SyntaxError{msg: "not at beginning of value"}
|
||||
}
|
||||
|
||||
// Read whole value into buffer.
|
||||
n, err := dec.readValue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec.d.init(dec.buf[dec.scanp : dec.scanp+n])
|
||||
dec.scanp += n
|
||||
|
||||
// Don't save err from unmarshal into dec.err:
|
||||
// the connection is still usable since we read a complete JSON
|
||||
// object from it before the error happened.
|
||||
err = dec.d.unmarshal(v)
|
||||
|
||||
// fixup token streaming state
|
||||
dec.tokenValueEnd()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Buffered returns a reader of the data remaining in the Decoder's
|
||||
// buffer. The reader is valid until the next call to Decode.
|
||||
func (dec *Decoder) Buffered() io.Reader {
|
||||
return bytes.NewReader(dec.buf[dec.scanp:])
|
||||
}
|
||||
|
||||
// readValue reads a JSON value into dec.buf.
|
||||
// It returns the length of the encoding.
|
||||
func (dec *Decoder) readValue() (int, error) {
|
||||
dec.scan.reset()
|
||||
|
||||
scanp := dec.scanp
|
||||
var err error
|
||||
Input:
|
||||
for {
|
||||
// Look in the buffer for a new value.
|
||||
for i, c := range dec.buf[scanp:] {
|
||||
dec.scan.bytes++
|
||||
v := dec.scan.step(&dec.scan, int(c))
|
||||
if v == scanEnd {
|
||||
scanp += i
|
||||
break Input
|
||||
}
|
||||
// scanEnd is delayed one byte.
|
||||
// We might block trying to get that byte from src,
|
||||
// so instead invent a space byte.
|
||||
if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd {
|
||||
scanp += i + 1
|
||||
break Input
|
||||
}
|
||||
if v == scanError {
|
||||
dec.err = dec.scan.err
|
||||
return 0, dec.scan.err
|
||||
}
|
||||
}
|
||||
scanp = len(dec.buf)
|
||||
|
||||
// Did the last read have an error?
|
||||
// Delayed until now to allow buffer scan.
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
if dec.scan.step(&dec.scan, ' ') == scanEnd {
|
||||
break Input
|
||||
}
|
||||
if nonSpace(dec.buf) {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
}
|
||||
dec.err = err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n := scanp - dec.scanp
|
||||
err = dec.refill()
|
||||
scanp = dec.scanp + n
|
||||
}
|
||||
return scanp - dec.scanp, nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) refill() error {
|
||||
// Make room to read more into the buffer.
|
||||
// First slide down data already consumed.
|
||||
if dec.scanp > 0 {
|
||||
n := copy(dec.buf, dec.buf[dec.scanp:])
|
||||
dec.buf = dec.buf[:n]
|
||||
dec.scanp = 0
|
||||
}
|
||||
|
||||
// Grow buffer if not large enough.
|
||||
const minRead = 512
|
||||
if cap(dec.buf)-len(dec.buf) < minRead {
|
||||
newBuf := make([]byte, len(dec.buf), 2*cap(dec.buf)+minRead)
|
||||
copy(newBuf, dec.buf)
|
||||
dec.buf = newBuf
|
||||
}
|
||||
|
||||
// Read. Delay error for next iteration (after scan).
|
||||
n, err := dec.r.Read(dec.buf[len(dec.buf):cap(dec.buf)])
|
||||
dec.buf = dec.buf[0 : len(dec.buf)+n]
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func nonSpace(b []byte) bool {
|
||||
for _, c := range b {
|
||||
if !isSpace(rune(c)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// An Encoder writes JSON objects to an output stream.
|
||||
type Encoder struct {
|
||||
w io.Writer
|
||||
err error
|
||||
}
|
||||
|
||||
// NewEncoder returns a new encoder that writes to w.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
return &Encoder{w: w}
|
||||
}
|
||||
|
||||
// Encode writes the JSON encoding of v to the stream,
|
||||
// followed by a newline character.
|
||||
//
|
||||
// See the documentation for Marshal for details about the
|
||||
// conversion of Go values to JSON.
|
||||
func (enc *Encoder) Encode(v interface{}) error {
|
||||
if enc.err != nil {
|
||||
return enc.err
|
||||
}
|
||||
e := newEncodeState()
|
||||
err := e.marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Terminate each value with a newline.
|
||||
// This makes the output look a little nicer
|
||||
// when debugging, and some kind of space
|
||||
// is required if the encoded value was a number,
|
||||
// so that the reader knows there aren't more
|
||||
// digits coming.
|
||||
e.WriteByte('\n')
|
||||
|
||||
if _, err = enc.w.Write(e.Bytes()); err != nil {
|
||||
enc.err = err
|
||||
}
|
||||
encodeStatePool.Put(e)
|
||||
return err
|
||||
}
|
||||
|
||||
// RawMessage is a raw encoded JSON object.
|
||||
// It implements Marshaler and Unmarshaler and can
|
||||
// be used to delay JSON decoding or precompute a JSON encoding.
|
||||
type RawMessage []byte
|
||||
|
||||
// MarshalJSON returns *m as the JSON encoding of m.
|
||||
func (m *RawMessage) MarshalJSON() ([]byte, error) {
|
||||
return *m, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON sets *m to a copy of data.
|
||||
func (m *RawMessage) UnmarshalJSON(data []byte) error {
|
||||
if m == nil {
|
||||
return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
|
||||
}
|
||||
*m = append((*m)[0:0], data...)
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ Marshaler = (*RawMessage)(nil)
|
||||
var _ Unmarshaler = (*RawMessage)(nil)
|
||||
|
||||
// A Token holds a value of one of these types:
|
||||
//
|
||||
// Delim, for the four JSON delimiters [ ] { }
|
||||
// bool, for JSON booleans
|
||||
// float64, for JSON numbers
|
||||
// Number, for JSON numbers
|
||||
// string, for JSON string literals
|
||||
// nil, for JSON null
|
||||
//
|
||||
type Token interface{}
|
||||
|
||||
const (
|
||||
tokenTopValue = iota
|
||||
tokenArrayStart
|
||||
tokenArrayValue
|
||||
tokenArrayComma
|
||||
tokenObjectStart
|
||||
tokenObjectKey
|
||||
tokenObjectColon
|
||||
tokenObjectValue
|
||||
tokenObjectComma
|
||||
)
|
||||
|
||||
// advance tokenstate from a separator state to a value state
|
||||
func (dec *Decoder) tokenPrepareForDecode() error {
|
||||
// Note: Not calling peek before switch, to avoid
|
||||
// putting peek into the standard Decode path.
|
||||
// peek is only called when using the Token API.
|
||||
switch dec.tokenState {
|
||||
case tokenArrayComma:
|
||||
c, err := dec.peek()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c != ',' {
|
||||
return &SyntaxError{"expected comma after array element", 0}
|
||||
}
|
||||
dec.scanp++
|
||||
dec.tokenState = tokenArrayValue
|
||||
case tokenObjectColon:
|
||||
c, err := dec.peek()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c != ':' {
|
||||
return &SyntaxError{"expected colon after object key", 0}
|
||||
}
|
||||
dec.scanp++
|
||||
dec.tokenState = tokenObjectValue
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dec *Decoder) tokenValueAllowed() bool {
|
||||
switch dec.tokenState {
|
||||
case tokenTopValue, tokenArrayStart, tokenArrayValue, tokenObjectValue:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (dec *Decoder) tokenValueEnd() {
|
||||
switch dec.tokenState {
|
||||
case tokenArrayStart, tokenArrayValue:
|
||||
dec.tokenState = tokenArrayComma
|
||||
case tokenObjectValue:
|
||||
dec.tokenState = tokenObjectComma
|
||||
}
|
||||
}
|
||||
|
||||
// A Delim is a JSON array or object delimiter, one of [ ] { or }.
|
||||
type Delim rune
|
||||
|
||||
func (d Delim) String() string {
|
||||
return string(d)
|
||||
}
|
||||
|
||||
// Token returns the next JSON token in the input stream.
|
||||
// At the end of the input stream, Token returns nil, io.EOF.
|
||||
//
|
||||
// Token guarantees that the delimiters [ ] { } it returns are
|
||||
// properly nested and matched: if Token encounters an unexpected
|
||||
// delimiter in the input, it will return an error.
|
||||
//
|
||||
// The input stream consists of basic JSON values—bool, string,
|
||||
// number, and null—along with delimiters [ ] { } of type Delim
|
||||
// to mark the start and end of arrays and objects.
|
||||
// Commas and colons are elided.
|
||||
func (dec *Decoder) Token() (Token, error) {
|
||||
for {
|
||||
c, err := dec.peek()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch c {
|
||||
case '[':
|
||||
if !dec.tokenValueAllowed() {
|
||||
return dec.tokenError(c)
|
||||
}
|
||||
dec.scanp++
|
||||
dec.tokenStack = append(dec.tokenStack, dec.tokenState)
|
||||
dec.tokenState = tokenArrayStart
|
||||
return Delim('['), nil
|
||||
|
||||
case ']':
|
||||
if dec.tokenState != tokenArrayStart && dec.tokenState != tokenArrayComma {
|
||||
return dec.tokenError(c)
|
||||
}
|
||||
dec.scanp++
|
||||
dec.tokenState = dec.tokenStack[len(dec.tokenStack)-1]
|
||||
dec.tokenStack = dec.tokenStack[:len(dec.tokenStack)-1]
|
||||
dec.tokenValueEnd()
|
||||
return Delim(']'), nil
|
||||
|
||||
case '{':
|
||||
if !dec.tokenValueAllowed() {
|
||||
return dec.tokenError(c)
|
||||
}
|
||||
dec.scanp++
|
||||
dec.tokenStack = append(dec.tokenStack, dec.tokenState)
|
||||
dec.tokenState = tokenObjectStart
|
||||
return Delim('{'), nil
|
||||
|
||||
case '}':
|
||||
if dec.tokenState != tokenObjectStart && dec.tokenState != tokenObjectComma {
|
||||
return dec.tokenError(c)
|
||||
}
|
||||
dec.scanp++
|
||||
dec.tokenState = dec.tokenStack[len(dec.tokenStack)-1]
|
||||
dec.tokenStack = dec.tokenStack[:len(dec.tokenStack)-1]
|
||||
dec.tokenValueEnd()
|
||||
return Delim('}'), nil
|
||||
|
||||
case ':':
|
||||
if dec.tokenState != tokenObjectColon {
|
||||
return dec.tokenError(c)
|
||||
}
|
||||
dec.scanp++
|
||||
dec.tokenState = tokenObjectValue
|
||||
continue
|
||||
|
||||
case ',':
|
||||
if dec.tokenState == tokenArrayComma {
|
||||
dec.scanp++
|
||||
dec.tokenState = tokenArrayValue
|
||||
continue
|
||||
}
|
||||
if dec.tokenState == tokenObjectComma {
|
||||
dec.scanp++
|
||||
dec.tokenState = tokenObjectKey
|
||||
continue
|
||||
}
|
||||
return dec.tokenError(c)
|
||||
|
||||
case '"':
|
||||
if dec.tokenState == tokenObjectStart || dec.tokenState == tokenObjectKey {
|
||||
var x string
|
||||
old := dec.tokenState
|
||||
dec.tokenState = tokenTopValue
|
||||
err := dec.Decode(&x)
|
||||
dec.tokenState = old
|
||||
if err != nil {
|
||||
clearOffset(err)
|
||||
return nil, err
|
||||
}
|
||||
dec.tokenState = tokenObjectColon
|
||||
return x, nil
|
||||
}
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
if !dec.tokenValueAllowed() {
|
||||
return dec.tokenError(c)
|
||||
}
|
||||
var x interface{}
|
||||
if err := dec.Decode(&x); err != nil {
|
||||
clearOffset(err)
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func clearOffset(err error) {
|
||||
if s, ok := err.(*SyntaxError); ok {
|
||||
s.Offset = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (dec *Decoder) tokenError(c byte) (Token, error) {
|
||||
var context string
|
||||
switch dec.tokenState {
|
||||
case tokenTopValue:
|
||||
context = " looking for beginning of value"
|
||||
case tokenArrayStart, tokenArrayValue, tokenObjectValue:
|
||||
context = " looking for beginning of value"
|
||||
case tokenArrayComma:
|
||||
context = " after array element"
|
||||
case tokenObjectKey:
|
||||
context = " looking for beginning of object key string"
|
||||
case tokenObjectColon:
|
||||
context = " after object key"
|
||||
case tokenObjectComma:
|
||||
context = " after object key:value pair"
|
||||
}
|
||||
return nil, &SyntaxError{"invalid character " + quoteChar(int(c)) + " " + context, 0}
|
||||
}
|
||||
|
||||
// More reports whether there is another element in the
|
||||
// current array or object being parsed.
|
||||
func (dec *Decoder) More() bool {
|
||||
c, err := dec.peek()
|
||||
return err == nil && c != ']' && c != '}'
|
||||
}
|
||||
|
||||
func (dec *Decoder) peek() (byte, error) {
|
||||
var err error
|
||||
for {
|
||||
for i := dec.scanp; i < len(dec.buf); i++ {
|
||||
c := dec.buf[i]
|
||||
if isSpace(rune(c)) {
|
||||
continue
|
||||
}
|
||||
dec.scanp = i
|
||||
return c, nil
|
||||
}
|
||||
// buffer has been scanned, now report any error
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = dec.refill()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
TODO
|
||||
|
||||
// EncodeToken writes the given JSON token to the stream.
|
||||
// It returns an error if the delimiters [ ] { } are not properly used.
|
||||
//
|
||||
// EncodeToken does not call Flush, because usually it is part of
|
||||
// a larger operation such as Encode, and those will call Flush when finished.
|
||||
// Callers that create an Encoder and then invoke EncodeToken directly,
|
||||
// without using Encode, need to call Flush when finished to ensure that
|
||||
// the JSON is written to the underlying writer.
|
||||
func (e *Encoder) EncodeToken(t Token) error {
|
||||
...
|
||||
}
|
||||
|
||||
*/
|
||||
44
vendor/github.com/ajeddeloh/go-json/tags.go
generated
vendored
44
vendor/github.com/ajeddeloh/go-json/tags.go
generated
vendored
@@ -1,44 +0,0 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package json
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// tagOptions is the string following a comma in a struct field's "json"
|
||||
// tag, or the empty string. It does not include the leading comma.
|
||||
type tagOptions string
|
||||
|
||||
// parseTag splits a struct field's json tag into its name and
|
||||
// comma-separated options.
|
||||
func parseTag(tag string) (string, tagOptions) {
|
||||
if idx := strings.Index(tag, ","); idx != -1 {
|
||||
return tag[:idx], tagOptions(tag[idx+1:])
|
||||
}
|
||||
return tag, tagOptions("")
|
||||
}
|
||||
|
||||
// Contains reports whether a comma-separated list of options
|
||||
// contains a particular substr flag. substr must be surrounded by a
|
||||
// string boundary or commas.
|
||||
func (o tagOptions) Contains(optionName string) bool {
|
||||
if len(o) == 0 {
|
||||
return false
|
||||
}
|
||||
s := string(o)
|
||||
for s != "" {
|
||||
var next string
|
||||
i := strings.Index(s, ",")
|
||||
if i >= 0 {
|
||||
s, next = s[:i], s[i+1:]
|
||||
}
|
||||
if s == optionName {
|
||||
return true
|
||||
}
|
||||
s = next
|
||||
}
|
||||
return false
|
||||
}
|
||||
9
vendor/github.com/ajeddeloh/yaml/.travis.yml
generated
vendored
9
vendor/github.com/ajeddeloh/yaml/.travis.yml
generated
vendored
@@ -1,9 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- tip
|
||||
|
||||
go_import_path: gopkg.in/yaml.v2
|
||||
201
vendor/github.com/ajeddeloh/yaml/LICENSE
generated
vendored
201
vendor/github.com/ajeddeloh/yaml/LICENSE
generated
vendored
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
31
vendor/github.com/ajeddeloh/yaml/LICENSE.libyaml
generated
vendored
31
vendor/github.com/ajeddeloh/yaml/LICENSE.libyaml
generated
vendored
@@ -1,31 +0,0 @@
|
||||
The following files were ported to Go from C files of libyaml, and thus
|
||||
are still covered by their original copyright and license:
|
||||
|
||||
apic.go
|
||||
emitterc.go
|
||||
parserc.go
|
||||
readerc.go
|
||||
scannerc.go
|
||||
writerc.go
|
||||
yamlh.go
|
||||
yamlprivateh.go
|
||||
|
||||
Copyright (c) 2006 Kirill Simonov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
133
vendor/github.com/ajeddeloh/yaml/README.md
generated
vendored
133
vendor/github.com/ajeddeloh/yaml/README.md
generated
vendored
@@ -1,133 +0,0 @@
|
||||
# YAML support for the Go language
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The yaml package enables Go programs to comfortably encode and decode YAML
|
||||
values. It was developed within [Canonical](https://www.canonical.com) as
|
||||
part of the [juju](https://juju.ubuntu.com) project, and is based on a
|
||||
pure Go port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML)
|
||||
C library to parse and generate YAML data quickly and reliably.
|
||||
|
||||
Compatibility
|
||||
-------------
|
||||
|
||||
The yaml package supports most of YAML 1.1 and 1.2, including support for
|
||||
anchors, tags, map merging, etc. Multi-document unmarshalling is not yet
|
||||
implemented, and base-60 floats from YAML 1.1 are purposefully not
|
||||
supported since they're a poor design and are gone in YAML 1.2.
|
||||
|
||||
Installation and usage
|
||||
----------------------
|
||||
|
||||
The import path for the package is *gopkg.in/yaml.v2*.
|
||||
|
||||
To install it, run:
|
||||
|
||||
go get gopkg.in/yaml.v2
|
||||
|
||||
API documentation
|
||||
-----------------
|
||||
|
||||
If opened in a browser, the import path itself leads to the API documentation:
|
||||
|
||||
* [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2)
|
||||
|
||||
API stability
|
||||
-------------
|
||||
|
||||
The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in).
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details.
|
||||
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
Some more examples can be found in the "examples" folder.
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var data = `
|
||||
a: Easy!
|
||||
b:
|
||||
c: 2
|
||||
d: [3, 4]
|
||||
`
|
||||
|
||||
type T struct {
|
||||
A string
|
||||
B struct {
|
||||
RenamedC int `yaml:"c"`
|
||||
D []int `yaml:",flow"`
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
t := T{}
|
||||
|
||||
err := yaml.Unmarshal([]byte(data), &t)
|
||||
if err != nil {
|
||||
log.Fatalf("error: %v", err)
|
||||
}
|
||||
fmt.Printf("--- t:\n%v\n\n", t)
|
||||
|
||||
d, err := yaml.Marshal(&t)
|
||||
if err != nil {
|
||||
log.Fatalf("error: %v", err)
|
||||
}
|
||||
fmt.Printf("--- t dump:\n%s\n\n", string(d))
|
||||
|
||||
m := make(map[interface{}]interface{})
|
||||
|
||||
err = yaml.Unmarshal([]byte(data), &m)
|
||||
if err != nil {
|
||||
log.Fatalf("error: %v", err)
|
||||
}
|
||||
fmt.Printf("--- m:\n%v\n\n", m)
|
||||
|
||||
d, err = yaml.Marshal(&m)
|
||||
if err != nil {
|
||||
log.Fatalf("error: %v", err)
|
||||
}
|
||||
fmt.Printf("--- m dump:\n%s\n\n", string(d))
|
||||
}
|
||||
```
|
||||
|
||||
This example will generate the following output:
|
||||
|
||||
```
|
||||
--- t:
|
||||
{Easy! {2 [3 4]}}
|
||||
|
||||
--- t dump:
|
||||
a: Easy!
|
||||
b:
|
||||
c: 2
|
||||
d: [3, 4]
|
||||
|
||||
|
||||
--- m:
|
||||
map[a:Easy! b:map[c:2 d:[3 4]]]
|
||||
|
||||
--- m dump:
|
||||
a: Easy!
|
||||
b:
|
||||
c: 2
|
||||
d:
|
||||
- 3
|
||||
- 4
|
||||
```
|
||||
|
||||
742
vendor/github.com/ajeddeloh/yaml/apic.go
generated
vendored
742
vendor/github.com/ajeddeloh/yaml/apic.go
generated
vendored
@@ -1,742 +0,0 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) {
|
||||
//fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens))
|
||||
|
||||
// Check if we can move the queue at the beginning of the buffer.
|
||||
if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) {
|
||||
if parser.tokens_head != len(parser.tokens) {
|
||||
copy(parser.tokens, parser.tokens[parser.tokens_head:])
|
||||
}
|
||||
parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head]
|
||||
parser.tokens_head = 0
|
||||
}
|
||||
parser.tokens = append(parser.tokens, *token)
|
||||
if pos < 0 {
|
||||
return
|
||||
}
|
||||
copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:])
|
||||
parser.tokens[parser.tokens_head+pos] = *token
|
||||
}
|
||||
|
||||
// Create a new parser object.
|
||||
func yaml_parser_initialize(parser *yaml_parser_t) bool {
|
||||
*parser = yaml_parser_t{
|
||||
raw_buffer: make([]byte, 0, input_raw_buffer_size),
|
||||
buffer: make([]byte, 0, input_buffer_size),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Destroy a parser object.
|
||||
func yaml_parser_delete(parser *yaml_parser_t) {
|
||||
*parser = yaml_parser_t{}
|
||||
}
|
||||
|
||||
// String read handler.
|
||||
func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
|
||||
if parser.input_pos == len(parser.input) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n = copy(buffer, parser.input[parser.input_pos:])
|
||||
parser.input_pos += n
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// File read handler.
|
||||
func yaml_file_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) {
|
||||
return parser.input_file.Read(buffer)
|
||||
}
|
||||
|
||||
// Set a string input.
|
||||
func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) {
|
||||
if parser.read_handler != nil {
|
||||
panic("must set the input source only once")
|
||||
}
|
||||
parser.read_handler = yaml_string_read_handler
|
||||
parser.input = input
|
||||
parser.input_pos = 0
|
||||
}
|
||||
|
||||
// Set a file input.
|
||||
func yaml_parser_set_input_file(parser *yaml_parser_t, file *os.File) {
|
||||
if parser.read_handler != nil {
|
||||
panic("must set the input source only once")
|
||||
}
|
||||
parser.read_handler = yaml_file_read_handler
|
||||
parser.input_file = file
|
||||
}
|
||||
|
||||
// Set the source encoding.
|
||||
func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) {
|
||||
if parser.encoding != yaml_ANY_ENCODING {
|
||||
panic("must set the encoding only once")
|
||||
}
|
||||
parser.encoding = encoding
|
||||
}
|
||||
|
||||
// Create a new emitter object.
|
||||
func yaml_emitter_initialize(emitter *yaml_emitter_t) bool {
|
||||
*emitter = yaml_emitter_t{
|
||||
buffer: make([]byte, output_buffer_size),
|
||||
raw_buffer: make([]byte, 0, output_raw_buffer_size),
|
||||
states: make([]yaml_emitter_state_t, 0, initial_stack_size),
|
||||
events: make([]yaml_event_t, 0, initial_queue_size),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Destroy an emitter object.
|
||||
func yaml_emitter_delete(emitter *yaml_emitter_t) {
|
||||
*emitter = yaml_emitter_t{}
|
||||
}
|
||||
|
||||
// String write handler.
|
||||
func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
|
||||
*emitter.output_buffer = append(*emitter.output_buffer, buffer...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// File write handler.
|
||||
func yaml_file_write_handler(emitter *yaml_emitter_t, buffer []byte) error {
|
||||
_, err := emitter.output_file.Write(buffer)
|
||||
return err
|
||||
}
|
||||
|
||||
// Set a string output.
|
||||
func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) {
|
||||
if emitter.write_handler != nil {
|
||||
panic("must set the output target only once")
|
||||
}
|
||||
emitter.write_handler = yaml_string_write_handler
|
||||
emitter.output_buffer = output_buffer
|
||||
}
|
||||
|
||||
// Set a file output.
|
||||
func yaml_emitter_set_output_file(emitter *yaml_emitter_t, file io.Writer) {
|
||||
if emitter.write_handler != nil {
|
||||
panic("must set the output target only once")
|
||||
}
|
||||
emitter.write_handler = yaml_file_write_handler
|
||||
emitter.output_file = file
|
||||
}
|
||||
|
||||
// Set the output encoding.
|
||||
func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) {
|
||||
if emitter.encoding != yaml_ANY_ENCODING {
|
||||
panic("must set the output encoding only once")
|
||||
}
|
||||
emitter.encoding = encoding
|
||||
}
|
||||
|
||||
// Set the canonical output style.
|
||||
func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) {
|
||||
emitter.canonical = canonical
|
||||
}
|
||||
|
||||
//// Set the indentation increment.
|
||||
func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) {
|
||||
if indent < 2 || indent > 9 {
|
||||
indent = 2
|
||||
}
|
||||
emitter.best_indent = indent
|
||||
}
|
||||
|
||||
// Set the preferred line width.
|
||||
func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) {
|
||||
if width < 0 {
|
||||
width = -1
|
||||
}
|
||||
emitter.best_width = width
|
||||
}
|
||||
|
||||
// Set if unescaped non-ASCII characters are allowed.
|
||||
func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) {
|
||||
emitter.unicode = unicode
|
||||
}
|
||||
|
||||
// Set the preferred line break character.
|
||||
func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) {
|
||||
emitter.line_break = line_break
|
||||
}
|
||||
|
||||
///*
|
||||
// * Destroy a token object.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(void)
|
||||
//yaml_token_delete(yaml_token_t *token)
|
||||
//{
|
||||
// assert(token); // Non-NULL token object expected.
|
||||
//
|
||||
// switch (token.type)
|
||||
// {
|
||||
// case YAML_TAG_DIRECTIVE_TOKEN:
|
||||
// yaml_free(token.data.tag_directive.handle);
|
||||
// yaml_free(token.data.tag_directive.prefix);
|
||||
// break;
|
||||
//
|
||||
// case YAML_ALIAS_TOKEN:
|
||||
// yaml_free(token.data.alias.value);
|
||||
// break;
|
||||
//
|
||||
// case YAML_ANCHOR_TOKEN:
|
||||
// yaml_free(token.data.anchor.value);
|
||||
// break;
|
||||
//
|
||||
// case YAML_TAG_TOKEN:
|
||||
// yaml_free(token.data.tag.handle);
|
||||
// yaml_free(token.data.tag.suffix);
|
||||
// break;
|
||||
//
|
||||
// case YAML_SCALAR_TOKEN:
|
||||
// yaml_free(token.data.scalar.value);
|
||||
// break;
|
||||
//
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// memset(token, 0, sizeof(yaml_token_t));
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Check if a string is a valid UTF-8 sequence.
|
||||
// *
|
||||
// * Check 'reader.c' for more details on UTF-8 encoding.
|
||||
// */
|
||||
//
|
||||
//static int
|
||||
//yaml_check_utf8(yaml_char_t *start, size_t length)
|
||||
//{
|
||||
// yaml_char_t *end = start+length;
|
||||
// yaml_char_t *pointer = start;
|
||||
//
|
||||
// while (pointer < end) {
|
||||
// unsigned char octet;
|
||||
// unsigned int width;
|
||||
// unsigned int value;
|
||||
// size_t k;
|
||||
//
|
||||
// octet = pointer[0];
|
||||
// width = (octet & 0x80) == 0x00 ? 1 :
|
||||
// (octet & 0xE0) == 0xC0 ? 2 :
|
||||
// (octet & 0xF0) == 0xE0 ? 3 :
|
||||
// (octet & 0xF8) == 0xF0 ? 4 : 0;
|
||||
// value = (octet & 0x80) == 0x00 ? octet & 0x7F :
|
||||
// (octet & 0xE0) == 0xC0 ? octet & 0x1F :
|
||||
// (octet & 0xF0) == 0xE0 ? octet & 0x0F :
|
||||
// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
|
||||
// if (!width) return 0;
|
||||
// if (pointer+width > end) return 0;
|
||||
// for (k = 1; k < width; k ++) {
|
||||
// octet = pointer[k];
|
||||
// if ((octet & 0xC0) != 0x80) return 0;
|
||||
// value = (value << 6) + (octet & 0x3F);
|
||||
// }
|
||||
// if (!((width == 1) ||
|
||||
// (width == 2 && value >= 0x80) ||
|
||||
// (width == 3 && value >= 0x800) ||
|
||||
// (width == 4 && value >= 0x10000))) return 0;
|
||||
//
|
||||
// pointer += width;
|
||||
// }
|
||||
//
|
||||
// return 1;
|
||||
//}
|
||||
//
|
||||
|
||||
// Create STREAM-START.
|
||||
func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_STREAM_START_EVENT,
|
||||
encoding: encoding,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create STREAM-END.
|
||||
func yaml_stream_end_event_initialize(event *yaml_event_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_STREAM_END_EVENT,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create DOCUMENT-START.
|
||||
func yaml_document_start_event_initialize(event *yaml_event_t, version_directive *yaml_version_directive_t,
|
||||
tag_directives []yaml_tag_directive_t, implicit bool) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_DOCUMENT_START_EVENT,
|
||||
version_directive: version_directive,
|
||||
tag_directives: tag_directives,
|
||||
implicit: implicit,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create DOCUMENT-END.
|
||||
func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_DOCUMENT_END_EVENT,
|
||||
implicit: implicit,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
///*
|
||||
// * Create ALIAS.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t)
|
||||
//{
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
// anchor_copy *yaml_char_t = NULL
|
||||
//
|
||||
// assert(event) // Non-NULL event object is expected.
|
||||
// assert(anchor) // Non-NULL anchor is expected.
|
||||
//
|
||||
// if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0
|
||||
//
|
||||
// anchor_copy = yaml_strdup(anchor)
|
||||
// if (!anchor_copy)
|
||||
// return 0
|
||||
//
|
||||
// ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark)
|
||||
//
|
||||
// return 1
|
||||
//}
|
||||
|
||||
// Create SCALAR.
|
||||
func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_SCALAR_EVENT,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
value: value,
|
||||
implicit: plain_implicit,
|
||||
quoted_implicit: quoted_implicit,
|
||||
style: yaml_style_t(style),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create SEQUENCE-START.
|
||||
func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_SEQUENCE_START_EVENT,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(style),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create SEQUENCE-END.
|
||||
func yaml_sequence_end_event_initialize(event *yaml_event_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_SEQUENCE_END_EVENT,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create MAPPING-START.
|
||||
func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_MAPPING_START_EVENT,
|
||||
anchor: anchor,
|
||||
tag: tag,
|
||||
implicit: implicit,
|
||||
style: yaml_style_t(style),
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Create MAPPING-END.
|
||||
func yaml_mapping_end_event_initialize(event *yaml_event_t) bool {
|
||||
*event = yaml_event_t{
|
||||
typ: yaml_MAPPING_END_EVENT,
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Destroy an event object.
|
||||
func yaml_event_delete(event *yaml_event_t) {
|
||||
*event = yaml_event_t{}
|
||||
}
|
||||
|
||||
///*
|
||||
// * Create a document object.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_initialize(document *yaml_document_t,
|
||||
// version_directive *yaml_version_directive_t,
|
||||
// tag_directives_start *yaml_tag_directive_t,
|
||||
// tag_directives_end *yaml_tag_directive_t,
|
||||
// start_implicit int, end_implicit int)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// struct {
|
||||
// start *yaml_node_t
|
||||
// end *yaml_node_t
|
||||
// top *yaml_node_t
|
||||
// } nodes = { NULL, NULL, NULL }
|
||||
// version_directive_copy *yaml_version_directive_t = NULL
|
||||
// struct {
|
||||
// start *yaml_tag_directive_t
|
||||
// end *yaml_tag_directive_t
|
||||
// top *yaml_tag_directive_t
|
||||
// } tag_directives_copy = { NULL, NULL, NULL }
|
||||
// value yaml_tag_directive_t = { NULL, NULL }
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
// assert((tag_directives_start && tag_directives_end) ||
|
||||
// (tag_directives_start == tag_directives_end))
|
||||
// // Valid tag directives are expected.
|
||||
//
|
||||
// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error
|
||||
//
|
||||
// if (version_directive) {
|
||||
// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t))
|
||||
// if (!version_directive_copy) goto error
|
||||
// version_directive_copy.major = version_directive.major
|
||||
// version_directive_copy.minor = version_directive.minor
|
||||
// }
|
||||
//
|
||||
// if (tag_directives_start != tag_directives_end) {
|
||||
// tag_directive *yaml_tag_directive_t
|
||||
// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE))
|
||||
// goto error
|
||||
// for (tag_directive = tag_directives_start
|
||||
// tag_directive != tag_directives_end; tag_directive ++) {
|
||||
// assert(tag_directive.handle)
|
||||
// assert(tag_directive.prefix)
|
||||
// if (!yaml_check_utf8(tag_directive.handle,
|
||||
// strlen((char *)tag_directive.handle)))
|
||||
// goto error
|
||||
// if (!yaml_check_utf8(tag_directive.prefix,
|
||||
// strlen((char *)tag_directive.prefix)))
|
||||
// goto error
|
||||
// value.handle = yaml_strdup(tag_directive.handle)
|
||||
// value.prefix = yaml_strdup(tag_directive.prefix)
|
||||
// if (!value.handle || !value.prefix) goto error
|
||||
// if (!PUSH(&context, tag_directives_copy, value))
|
||||
// goto error
|
||||
// value.handle = NULL
|
||||
// value.prefix = NULL
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy,
|
||||
// tag_directives_copy.start, tag_directives_copy.top,
|
||||
// start_implicit, end_implicit, mark, mark)
|
||||
//
|
||||
// return 1
|
||||
//
|
||||
//error:
|
||||
// STACK_DEL(&context, nodes)
|
||||
// yaml_free(version_directive_copy)
|
||||
// while (!STACK_EMPTY(&context, tag_directives_copy)) {
|
||||
// value yaml_tag_directive_t = POP(&context, tag_directives_copy)
|
||||
// yaml_free(value.handle)
|
||||
// yaml_free(value.prefix)
|
||||
// }
|
||||
// STACK_DEL(&context, tag_directives_copy)
|
||||
// yaml_free(value.handle)
|
||||
// yaml_free(value.prefix)
|
||||
//
|
||||
// return 0
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Destroy a document object.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(void)
|
||||
//yaml_document_delete(document *yaml_document_t)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// tag_directive *yaml_tag_directive_t
|
||||
//
|
||||
// context.error = YAML_NO_ERROR // Eliminate a compliler warning.
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// while (!STACK_EMPTY(&context, document.nodes)) {
|
||||
// node yaml_node_t = POP(&context, document.nodes)
|
||||
// yaml_free(node.tag)
|
||||
// switch (node.type) {
|
||||
// case YAML_SCALAR_NODE:
|
||||
// yaml_free(node.data.scalar.value)
|
||||
// break
|
||||
// case YAML_SEQUENCE_NODE:
|
||||
// STACK_DEL(&context, node.data.sequence.items)
|
||||
// break
|
||||
// case YAML_MAPPING_NODE:
|
||||
// STACK_DEL(&context, node.data.mapping.pairs)
|
||||
// break
|
||||
// default:
|
||||
// assert(0) // Should not happen.
|
||||
// }
|
||||
// }
|
||||
// STACK_DEL(&context, document.nodes)
|
||||
//
|
||||
// yaml_free(document.version_directive)
|
||||
// for (tag_directive = document.tag_directives.start
|
||||
// tag_directive != document.tag_directives.end
|
||||
// tag_directive++) {
|
||||
// yaml_free(tag_directive.handle)
|
||||
// yaml_free(tag_directive.prefix)
|
||||
// }
|
||||
// yaml_free(document.tag_directives.start)
|
||||
//
|
||||
// memset(document, 0, sizeof(yaml_document_t))
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * Get a document node.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(yaml_node_t *)
|
||||
//yaml_document_get_node(document *yaml_document_t, index int)
|
||||
//{
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// if (index > 0 && document.nodes.start + index <= document.nodes.top) {
|
||||
// return document.nodes.start + index - 1
|
||||
// }
|
||||
// return NULL
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * Get the root object.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(yaml_node_t *)
|
||||
//yaml_document_get_root_node(document *yaml_document_t)
|
||||
//{
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// if (document.nodes.top != document.nodes.start) {
|
||||
// return document.nodes.start
|
||||
// }
|
||||
// return NULL
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Add a scalar node to a document.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_add_scalar(document *yaml_document_t,
|
||||
// tag *yaml_char_t, value *yaml_char_t, length int,
|
||||
// style yaml_scalar_style_t)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
// tag_copy *yaml_char_t = NULL
|
||||
// value_copy *yaml_char_t = NULL
|
||||
// node yaml_node_t
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
// assert(value) // Non-NULL value is expected.
|
||||
//
|
||||
// if (!tag) {
|
||||
// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
|
||||
// tag_copy = yaml_strdup(tag)
|
||||
// if (!tag_copy) goto error
|
||||
//
|
||||
// if (length < 0) {
|
||||
// length = strlen((char *)value)
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(value, length)) goto error
|
||||
// value_copy = yaml_malloc(length+1)
|
||||
// if (!value_copy) goto error
|
||||
// memcpy(value_copy, value, length)
|
||||
// value_copy[length] = '\0'
|
||||
//
|
||||
// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark)
|
||||
// if (!PUSH(&context, document.nodes, node)) goto error
|
||||
//
|
||||
// return document.nodes.top - document.nodes.start
|
||||
//
|
||||
//error:
|
||||
// yaml_free(tag_copy)
|
||||
// yaml_free(value_copy)
|
||||
//
|
||||
// return 0
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Add a sequence node to a document.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_add_sequence(document *yaml_document_t,
|
||||
// tag *yaml_char_t, style yaml_sequence_style_t)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
// tag_copy *yaml_char_t = NULL
|
||||
// struct {
|
||||
// start *yaml_node_item_t
|
||||
// end *yaml_node_item_t
|
||||
// top *yaml_node_item_t
|
||||
// } items = { NULL, NULL, NULL }
|
||||
// node yaml_node_t
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// if (!tag) {
|
||||
// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
|
||||
// tag_copy = yaml_strdup(tag)
|
||||
// if (!tag_copy) goto error
|
||||
//
|
||||
// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error
|
||||
//
|
||||
// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end,
|
||||
// style, mark, mark)
|
||||
// if (!PUSH(&context, document.nodes, node)) goto error
|
||||
//
|
||||
// return document.nodes.top - document.nodes.start
|
||||
//
|
||||
//error:
|
||||
// STACK_DEL(&context, items)
|
||||
// yaml_free(tag_copy)
|
||||
//
|
||||
// return 0
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Add a mapping node to a document.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_add_mapping(document *yaml_document_t,
|
||||
// tag *yaml_char_t, style yaml_mapping_style_t)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
// mark yaml_mark_t = { 0, 0, 0 }
|
||||
// tag_copy *yaml_char_t = NULL
|
||||
// struct {
|
||||
// start *yaml_node_pair_t
|
||||
// end *yaml_node_pair_t
|
||||
// top *yaml_node_pair_t
|
||||
// } pairs = { NULL, NULL, NULL }
|
||||
// node yaml_node_t
|
||||
//
|
||||
// assert(document) // Non-NULL document object is expected.
|
||||
//
|
||||
// if (!tag) {
|
||||
// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG
|
||||
// }
|
||||
//
|
||||
// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error
|
||||
// tag_copy = yaml_strdup(tag)
|
||||
// if (!tag_copy) goto error
|
||||
//
|
||||
// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error
|
||||
//
|
||||
// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end,
|
||||
// style, mark, mark)
|
||||
// if (!PUSH(&context, document.nodes, node)) goto error
|
||||
//
|
||||
// return document.nodes.top - document.nodes.start
|
||||
//
|
||||
//error:
|
||||
// STACK_DEL(&context, pairs)
|
||||
// yaml_free(tag_copy)
|
||||
//
|
||||
// return 0
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Append an item to a sequence node.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_append_sequence_item(document *yaml_document_t,
|
||||
// sequence int, item int)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
//
|
||||
// assert(document) // Non-NULL document is required.
|
||||
// assert(sequence > 0
|
||||
// && document.nodes.start + sequence <= document.nodes.top)
|
||||
// // Valid sequence id is required.
|
||||
// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE)
|
||||
// // A sequence node is required.
|
||||
// assert(item > 0 && document.nodes.start + item <= document.nodes.top)
|
||||
// // Valid item id is required.
|
||||
//
|
||||
// if (!PUSH(&context,
|
||||
// document.nodes.start[sequence-1].data.sequence.items, item))
|
||||
// return 0
|
||||
//
|
||||
// return 1
|
||||
//}
|
||||
//
|
||||
///*
|
||||
// * Append a pair of a key and a value to a mapping node.
|
||||
// */
|
||||
//
|
||||
//YAML_DECLARE(int)
|
||||
//yaml_document_append_mapping_pair(document *yaml_document_t,
|
||||
// mapping int, key int, value int)
|
||||
//{
|
||||
// struct {
|
||||
// error yaml_error_type_t
|
||||
// } context
|
||||
//
|
||||
// pair yaml_node_pair_t
|
||||
//
|
||||
// assert(document) // Non-NULL document is required.
|
||||
// assert(mapping > 0
|
||||
// && document.nodes.start + mapping <= document.nodes.top)
|
||||
// // Valid mapping id is required.
|
||||
// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE)
|
||||
// // A mapping node is required.
|
||||
// assert(key > 0 && document.nodes.start + key <= document.nodes.top)
|
||||
// // Valid key id is required.
|
||||
// assert(value > 0 && document.nodes.start + value <= document.nodes.top)
|
||||
// // Valid value id is required.
|
||||
//
|
||||
// pair.key = key
|
||||
// pair.value = value
|
||||
//
|
||||
// if (!PUSH(&context,
|
||||
// document.nodes.start[mapping-1].data.mapping.pairs, pair))
|
||||
// return 0
|
||||
//
|
||||
// return 1
|
||||
//}
|
||||
//
|
||||
//
|
||||
685
vendor/github.com/ajeddeloh/yaml/decode.go
generated
vendored
685
vendor/github.com/ajeddeloh/yaml/decode.go
generated
vendored
@@ -1,685 +0,0 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DocumentNode = 1 << iota
|
||||
MappingNode
|
||||
SequenceNode
|
||||
ScalarNode
|
||||
AliasNode
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
Kind int
|
||||
Line, Column int
|
||||
Tag string
|
||||
Value string
|
||||
Implicit bool
|
||||
Children []*Node
|
||||
Anchors map[string]*Node
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Parser, produces a Node tree out of a libyaml event stream.
|
||||
|
||||
type parser struct {
|
||||
parser yaml_parser_t
|
||||
event yaml_event_t
|
||||
doc *Node
|
||||
}
|
||||
|
||||
func newParser(b []byte) *parser {
|
||||
p := parser{}
|
||||
if !yaml_parser_initialize(&p.parser) {
|
||||
panic("failed to initialize YAML emitter")
|
||||
}
|
||||
|
||||
if len(b) == 0 {
|
||||
b = []byte{'\n'}
|
||||
}
|
||||
|
||||
yaml_parser_set_input_string(&p.parser, b)
|
||||
|
||||
p.skip()
|
||||
if p.event.typ != yaml_STREAM_START_EVENT {
|
||||
panic("expected stream start event, got " + strconv.Itoa(int(p.event.typ)))
|
||||
}
|
||||
p.skip()
|
||||
return &p
|
||||
}
|
||||
|
||||
func (p *parser) destroy() {
|
||||
if p.event.typ != yaml_NO_EVENT {
|
||||
yaml_event_delete(&p.event)
|
||||
}
|
||||
yaml_parser_delete(&p.parser)
|
||||
}
|
||||
|
||||
func (p *parser) skip() {
|
||||
if p.event.typ != yaml_NO_EVENT {
|
||||
if p.event.typ == yaml_STREAM_END_EVENT {
|
||||
failf("attempted to go past the end of stream; corrupted value?")
|
||||
}
|
||||
yaml_event_delete(&p.event)
|
||||
}
|
||||
if !yaml_parser_parse(&p.parser, &p.event) {
|
||||
p.fail()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) fail() {
|
||||
var where string
|
||||
var line int
|
||||
if p.parser.problem_mark.line != 0 {
|
||||
line = p.parser.problem_mark.line
|
||||
} else if p.parser.context_mark.line != 0 {
|
||||
line = p.parser.context_mark.line
|
||||
}
|
||||
if line != 0 {
|
||||
where = "line " + strconv.Itoa(line) + ": "
|
||||
}
|
||||
var msg string
|
||||
if len(p.parser.problem) > 0 {
|
||||
msg = p.parser.problem
|
||||
} else {
|
||||
msg = "unknown problem parsing YAML content"
|
||||
}
|
||||
failf("%s%s", where, msg)
|
||||
}
|
||||
|
||||
func (p *parser) anchor(n *Node, anchor []byte) {
|
||||
if anchor != nil {
|
||||
p.doc.Anchors[string(anchor)] = n
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parse() *Node {
|
||||
switch p.event.typ {
|
||||
case yaml_SCALAR_EVENT:
|
||||
return p.scalar()
|
||||
case yaml_ALIAS_EVENT:
|
||||
return p.alias()
|
||||
case yaml_MAPPING_START_EVENT:
|
||||
return p.mapping()
|
||||
case yaml_SEQUENCE_START_EVENT:
|
||||
return p.sequence()
|
||||
case yaml_DOCUMENT_START_EVENT:
|
||||
return p.document()
|
||||
case yaml_STREAM_END_EVENT:
|
||||
// Happens when attempting to decode an empty buffer.
|
||||
return nil
|
||||
default:
|
||||
panic("attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ)))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) Node(kind int) *Node {
|
||||
return &Node{
|
||||
Kind: kind,
|
||||
Line: p.event.start_mark.line,
|
||||
Column: p.event.start_mark.column,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) document() *Node {
|
||||
n := p.Node(DocumentNode)
|
||||
n.Anchors = make(map[string]*Node)
|
||||
p.doc = n
|
||||
p.skip()
|
||||
n.Children = append(n.Children, p.parse())
|
||||
if p.event.typ != yaml_DOCUMENT_END_EVENT {
|
||||
panic("expected end of document event but got " + strconv.Itoa(int(p.event.typ)))
|
||||
}
|
||||
p.skip()
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *parser) alias() *Node {
|
||||
n := p.Node(AliasNode)
|
||||
n.Value = string(p.event.anchor)
|
||||
p.skip()
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *parser) scalar() *Node {
|
||||
n := p.Node(ScalarNode)
|
||||
n.Value = string(p.event.value)
|
||||
n.Tag = string(p.event.tag)
|
||||
n.Implicit = p.event.implicit
|
||||
p.anchor(n, p.event.anchor)
|
||||
p.skip()
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *parser) sequence() *Node {
|
||||
n := p.Node(SequenceNode)
|
||||
p.anchor(n, p.event.anchor)
|
||||
p.skip()
|
||||
for p.event.typ != yaml_SEQUENCE_END_EVENT {
|
||||
n.Children = append(n.Children, p.parse())
|
||||
}
|
||||
p.skip()
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *parser) mapping() *Node {
|
||||
n := p.Node(MappingNode)
|
||||
p.anchor(n, p.event.anchor)
|
||||
p.skip()
|
||||
for p.event.typ != yaml_MAPPING_END_EVENT {
|
||||
n.Children = append(n.Children, p.parse(), p.parse())
|
||||
}
|
||||
p.skip()
|
||||
return n
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Decoder, unmarshals a Node into a provided value.
|
||||
|
||||
type decoder struct {
|
||||
doc *Node
|
||||
aliases map[string]bool
|
||||
mapType reflect.Type
|
||||
terrors []string
|
||||
strict bool
|
||||
}
|
||||
|
||||
var (
|
||||
mapItemType = reflect.TypeOf(MapItem{})
|
||||
durationType = reflect.TypeOf(time.Duration(0))
|
||||
defaultMapType = reflect.TypeOf(map[interface{}]interface{}{})
|
||||
ifaceType = defaultMapType.Elem()
|
||||
)
|
||||
|
||||
func newDecoder(strict bool) *decoder {
|
||||
d := &decoder{mapType: defaultMapType, strict: strict}
|
||||
d.aliases = make(map[string]bool)
|
||||
return d
|
||||
}
|
||||
|
||||
func (d *decoder) terror(n *Node, tag string, out reflect.Value) {
|
||||
if n.Tag != "" {
|
||||
tag = n.Tag
|
||||
}
|
||||
value := n.Value
|
||||
if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG {
|
||||
if len(value) > 10 {
|
||||
value = " `" + value[:7] + "...`"
|
||||
} else {
|
||||
value = " `" + value + "`"
|
||||
}
|
||||
}
|
||||
d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line+1, shortTag(tag), value, out.Type()))
|
||||
}
|
||||
|
||||
func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) {
|
||||
terrlen := len(d.terrors)
|
||||
err := u.UnmarshalYAML(func(v interface{}) (err error) {
|
||||
defer handleErr(&err)
|
||||
d.unmarshal(n, reflect.ValueOf(v))
|
||||
if len(d.terrors) > terrlen {
|
||||
issues := d.terrors[terrlen:]
|
||||
d.terrors = d.terrors[:terrlen]
|
||||
return &TypeError{issues}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if e, ok := err.(*TypeError); ok {
|
||||
d.terrors = append(d.terrors, e.Errors...)
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
fail(err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// d.prepare initializes and dereferences pointers and calls UnmarshalYAML
|
||||
// if a value is found to implement it.
|
||||
// It returns the initialized and dereferenced out value, whether
|
||||
// unmarshalling was already done by UnmarshalYAML, and if so whether
|
||||
// its types unmarshalled appropriately.
|
||||
//
|
||||
// If n holds a null value, prepare returns before doing anything.
|
||||
func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
|
||||
if n.Tag == yaml_NULL_TAG || n.Kind == ScalarNode && n.Tag == "" && (n.Value == "null" || n.Value == "" && n.Implicit) {
|
||||
return out, false, false
|
||||
}
|
||||
again := true
|
||||
for again {
|
||||
again = false
|
||||
if out.Kind() == reflect.Ptr {
|
||||
if out.IsNil() {
|
||||
out.Set(reflect.New(out.Type().Elem()))
|
||||
}
|
||||
out = out.Elem()
|
||||
again = true
|
||||
}
|
||||
if out.CanAddr() {
|
||||
if u, ok := out.Addr().Interface().(Unmarshaler); ok {
|
||||
good = d.callUnmarshaler(n, u)
|
||||
return out, true, good
|
||||
}
|
||||
}
|
||||
}
|
||||
return out, false, false
|
||||
}
|
||||
|
||||
func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) {
|
||||
switch n.Kind {
|
||||
case DocumentNode:
|
||||
return d.document(n, out)
|
||||
case AliasNode:
|
||||
return d.alias(n, out)
|
||||
}
|
||||
out, unmarshaled, good := d.prepare(n, out)
|
||||
if unmarshaled {
|
||||
return good
|
||||
}
|
||||
switch n.Kind {
|
||||
case ScalarNode:
|
||||
good = d.scalar(n, out)
|
||||
case MappingNode:
|
||||
good = d.mapping(n, out)
|
||||
case SequenceNode:
|
||||
good = d.sequence(n, out)
|
||||
default:
|
||||
panic("internal error: unknown Node kind: " + strconv.Itoa(n.Kind))
|
||||
}
|
||||
return good
|
||||
}
|
||||
|
||||
func (d *decoder) document(n *Node, out reflect.Value) (good bool) {
|
||||
if len(n.Children) == 1 {
|
||||
d.doc = n
|
||||
d.unmarshal(n.Children[0], out)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (d *decoder) alias(n *Node, out reflect.Value) (good bool) {
|
||||
an, ok := d.doc.Anchors[n.Value]
|
||||
if !ok {
|
||||
failf("unknown anchor '%s' referenced", n.Value)
|
||||
}
|
||||
if d.aliases[n.Value] {
|
||||
failf("anchor '%s' value contains itself", n.Value)
|
||||
}
|
||||
d.aliases[n.Value] = true
|
||||
good = d.unmarshal(an, out)
|
||||
delete(d.aliases, n.Value)
|
||||
return good
|
||||
}
|
||||
|
||||
var zeroValue reflect.Value
|
||||
|
||||
func resetMap(out reflect.Value) {
|
||||
for _, k := range out.MapKeys() {
|
||||
out.SetMapIndex(k, zeroValue)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *decoder) scalar(n *Node, out reflect.Value) (good bool) {
|
||||
var tag string
|
||||
var resolved interface{}
|
||||
if n.Tag == "" && !n.Implicit {
|
||||
tag = yaml_STR_TAG
|
||||
resolved = n.Value
|
||||
} else {
|
||||
tag, resolved = resolve(n.Tag, n.Value)
|
||||
if tag == yaml_BINARY_TAG {
|
||||
data, err := base64.StdEncoding.DecodeString(resolved.(string))
|
||||
if err != nil {
|
||||
failf("!!binary value contains invalid base64 data")
|
||||
}
|
||||
resolved = string(data)
|
||||
}
|
||||
}
|
||||
if resolved == nil {
|
||||
if out.Kind() == reflect.Map && !out.CanAddr() {
|
||||
resetMap(out)
|
||||
} else {
|
||||
out.Set(reflect.Zero(out.Type()))
|
||||
}
|
||||
return true
|
||||
}
|
||||
if s, ok := resolved.(string); ok && out.CanAddr() {
|
||||
if u, ok := out.Addr().Interface().(encoding.TextUnmarshaler); ok {
|
||||
err := u.UnmarshalText([]byte(s))
|
||||
if err != nil {
|
||||
fail(err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
switch out.Kind() {
|
||||
case reflect.String:
|
||||
if tag == yaml_BINARY_TAG {
|
||||
out.SetString(resolved.(string))
|
||||
good = true
|
||||
} else if resolved != nil {
|
||||
out.SetString(n.Value)
|
||||
good = true
|
||||
}
|
||||
case reflect.Interface:
|
||||
if resolved == nil {
|
||||
out.Set(reflect.Zero(out.Type()))
|
||||
} else {
|
||||
out.Set(reflect.ValueOf(resolved))
|
||||
}
|
||||
good = true
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
switch resolved := resolved.(type) {
|
||||
case int:
|
||||
if !out.OverflowInt(int64(resolved)) {
|
||||
out.SetInt(int64(resolved))
|
||||
good = true
|
||||
}
|
||||
case int64:
|
||||
if !out.OverflowInt(resolved) {
|
||||
out.SetInt(resolved)
|
||||
good = true
|
||||
}
|
||||
case uint64:
|
||||
if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
|
||||
out.SetInt(int64(resolved))
|
||||
good = true
|
||||
}
|
||||
case float64:
|
||||
if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
|
||||
out.SetInt(int64(resolved))
|
||||
good = true
|
||||
}
|
||||
case string:
|
||||
if out.Type() == durationType {
|
||||
d, err := time.ParseDuration(resolved)
|
||||
if err == nil {
|
||||
out.SetInt(int64(d))
|
||||
good = true
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
switch resolved := resolved.(type) {
|
||||
case int:
|
||||
if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
|
||||
out.SetUint(uint64(resolved))
|
||||
good = true
|
||||
}
|
||||
case int64:
|
||||
if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
|
||||
out.SetUint(uint64(resolved))
|
||||
good = true
|
||||
}
|
||||
case uint64:
|
||||
if !out.OverflowUint(uint64(resolved)) {
|
||||
out.SetUint(uint64(resolved))
|
||||
good = true
|
||||
}
|
||||
case float64:
|
||||
if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) {
|
||||
out.SetUint(uint64(resolved))
|
||||
good = true
|
||||
}
|
||||
}
|
||||
case reflect.Bool:
|
||||
switch resolved := resolved.(type) {
|
||||
case bool:
|
||||
out.SetBool(resolved)
|
||||
good = true
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
switch resolved := resolved.(type) {
|
||||
case int:
|
||||
out.SetFloat(float64(resolved))
|
||||
good = true
|
||||
case int64:
|
||||
out.SetFloat(float64(resolved))
|
||||
good = true
|
||||
case uint64:
|
||||
out.SetFloat(float64(resolved))
|
||||
good = true
|
||||
case float64:
|
||||
out.SetFloat(resolved)
|
||||
good = true
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if out.Type().Elem() == reflect.TypeOf(resolved) {
|
||||
// TODO DOes this make sense? When is out a Ptr except when decoding a nil value?
|
||||
elem := reflect.New(out.Type().Elem())
|
||||
elem.Elem().Set(reflect.ValueOf(resolved))
|
||||
out.Set(elem)
|
||||
good = true
|
||||
}
|
||||
}
|
||||
if !good {
|
||||
d.terror(n, tag, out)
|
||||
}
|
||||
return good
|
||||
}
|
||||
|
||||
func settableValueOf(i interface{}) reflect.Value {
|
||||
v := reflect.ValueOf(i)
|
||||
sv := reflect.New(v.Type()).Elem()
|
||||
sv.Set(v)
|
||||
return sv
|
||||
}
|
||||
|
||||
func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) {
|
||||
l := len(n.Children)
|
||||
|
||||
var iface reflect.Value
|
||||
switch out.Kind() {
|
||||
case reflect.Slice:
|
||||
out.Set(reflect.MakeSlice(out.Type(), l, l))
|
||||
case reflect.Interface:
|
||||
// No type hints. Will have to use a generic sequence.
|
||||
iface = out
|
||||
out = settableValueOf(make([]interface{}, l))
|
||||
default:
|
||||
d.terror(n, yaml_SEQ_TAG, out)
|
||||
return false
|
||||
}
|
||||
et := out.Type().Elem()
|
||||
|
||||
j := 0
|
||||
for i := 0; i < l; i++ {
|
||||
e := reflect.New(et).Elem()
|
||||
if ok := d.unmarshal(n.Children[i], e); ok {
|
||||
out.Index(j).Set(e)
|
||||
j++
|
||||
}
|
||||
}
|
||||
out.Set(out.Slice(0, j))
|
||||
if iface.IsValid() {
|
||||
iface.Set(out)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
|
||||
switch out.Kind() {
|
||||
case reflect.Struct:
|
||||
return d.mappingStruct(n, out)
|
||||
case reflect.Slice:
|
||||
return d.mappingSlice(n, out)
|
||||
case reflect.Map:
|
||||
// okay
|
||||
case reflect.Interface:
|
||||
if d.mapType.Kind() == reflect.Map {
|
||||
iface := out
|
||||
out = reflect.MakeMap(d.mapType)
|
||||
iface.Set(out)
|
||||
} else {
|
||||
slicev := reflect.New(d.mapType).Elem()
|
||||
if !d.mappingSlice(n, slicev) {
|
||||
return false
|
||||
}
|
||||
out.Set(slicev)
|
||||
return true
|
||||
}
|
||||
default:
|
||||
d.terror(n, yaml_MAP_TAG, out)
|
||||
return false
|
||||
}
|
||||
outt := out.Type()
|
||||
kt := outt.Key()
|
||||
et := outt.Elem()
|
||||
|
||||
mapType := d.mapType
|
||||
if outt.Key() == ifaceType && outt.Elem() == ifaceType {
|
||||
d.mapType = outt
|
||||
}
|
||||
|
||||
if out.IsNil() {
|
||||
out.Set(reflect.MakeMap(outt))
|
||||
}
|
||||
l := len(n.Children)
|
||||
for i := 0; i < l; i += 2 {
|
||||
if isMerge(n.Children[i]) {
|
||||
d.merge(n.Children[i+1], out)
|
||||
continue
|
||||
}
|
||||
k := reflect.New(kt).Elem()
|
||||
if d.unmarshal(n.Children[i], k) {
|
||||
kkind := k.Kind()
|
||||
if kkind == reflect.Interface {
|
||||
kkind = k.Elem().Kind()
|
||||
}
|
||||
if kkind == reflect.Map || kkind == reflect.Slice {
|
||||
failf("invalid map key: %#v", k.Interface())
|
||||
}
|
||||
e := reflect.New(et).Elem()
|
||||
if d.unmarshal(n.Children[i+1], e) {
|
||||
out.SetMapIndex(k, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
d.mapType = mapType
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *decoder) mappingSlice(n *Node, out reflect.Value) (good bool) {
|
||||
outt := out.Type()
|
||||
if outt.Elem() != mapItemType {
|
||||
d.terror(n, yaml_MAP_TAG, out)
|
||||
return false
|
||||
}
|
||||
|
||||
mapType := d.mapType
|
||||
d.mapType = outt
|
||||
|
||||
var slice []MapItem
|
||||
var l = len(n.Children)
|
||||
for i := 0; i < l; i += 2 {
|
||||
if isMerge(n.Children[i]) {
|
||||
d.merge(n.Children[i+1], out)
|
||||
continue
|
||||
}
|
||||
item := MapItem{}
|
||||
k := reflect.ValueOf(&item.Key).Elem()
|
||||
if d.unmarshal(n.Children[i], k) {
|
||||
v := reflect.ValueOf(&item.Value).Elem()
|
||||
if d.unmarshal(n.Children[i+1], v) {
|
||||
slice = append(slice, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
out.Set(reflect.ValueOf(slice))
|
||||
d.mapType = mapType
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
|
||||
sinfo, err := getStructInfo(out.Type())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
name := settableValueOf("")
|
||||
l := len(n.Children)
|
||||
|
||||
var inlineMap reflect.Value
|
||||
var elemType reflect.Type
|
||||
if sinfo.InlineMap != -1 {
|
||||
inlineMap = out.Field(sinfo.InlineMap)
|
||||
inlineMap.Set(reflect.New(inlineMap.Type()).Elem())
|
||||
elemType = inlineMap.Type().Elem()
|
||||
}
|
||||
|
||||
for i := 0; i < l; i += 2 {
|
||||
ni := n.Children[i]
|
||||
if isMerge(ni) {
|
||||
d.merge(n.Children[i+1], out)
|
||||
continue
|
||||
}
|
||||
if !d.unmarshal(ni, name) {
|
||||
continue
|
||||
}
|
||||
if info, ok := sinfo.FieldsMap[name.String()]; ok {
|
||||
var field reflect.Value
|
||||
if info.Inline == nil {
|
||||
field = out.Field(info.Num)
|
||||
} else {
|
||||
field = out.FieldByIndex(info.Inline)
|
||||
}
|
||||
d.unmarshal(n.Children[i+1], field)
|
||||
} else if sinfo.InlineMap != -1 {
|
||||
if inlineMap.IsNil() {
|
||||
inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
|
||||
}
|
||||
value := reflect.New(elemType).Elem()
|
||||
d.unmarshal(n.Children[i+1], value)
|
||||
inlineMap.SetMapIndex(name, value)
|
||||
} else if d.strict {
|
||||
d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in struct %s", n.Line+1, name.String(), out.Type()))
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func failWantMap() {
|
||||
failf("map merge requires map or sequence of maps as the value")
|
||||
}
|
||||
|
||||
func (d *decoder) merge(n *Node, out reflect.Value) {
|
||||
switch n.Kind {
|
||||
case MappingNode:
|
||||
d.unmarshal(n, out)
|
||||
case AliasNode:
|
||||
an, ok := d.doc.Anchors[n.Value]
|
||||
if ok && an.Kind != MappingNode {
|
||||
failWantMap()
|
||||
}
|
||||
d.unmarshal(n, out)
|
||||
case SequenceNode:
|
||||
// Step backwards as earlier Nodes take precedence.
|
||||
for i := len(n.Children) - 1; i >= 0; i-- {
|
||||
ni := n.Children[i]
|
||||
if ni.Kind == AliasNode {
|
||||
an, ok := d.doc.Anchors[ni.Value]
|
||||
if ok && an.Kind != MappingNode {
|
||||
failWantMap()
|
||||
}
|
||||
} else if ni.Kind != MappingNode {
|
||||
failWantMap()
|
||||
}
|
||||
d.unmarshal(ni, out)
|
||||
}
|
||||
default:
|
||||
failWantMap()
|
||||
}
|
||||
}
|
||||
|
||||
func isMerge(n *Node) bool {
|
||||
return n.Kind == ScalarNode && n.Value == "<<" && (n.Implicit == true || n.Tag == yaml_MERGE_TAG)
|
||||
}
|
||||
1684
vendor/github.com/ajeddeloh/yaml/emitterc.go
generated
vendored
1684
vendor/github.com/ajeddeloh/yaml/emitterc.go
generated
vendored
File diff suppressed because it is too large
Load Diff
306
vendor/github.com/ajeddeloh/yaml/encode.go
generated
vendored
306
vendor/github.com/ajeddeloh/yaml/encode.go
generated
vendored
@@ -1,306 +0,0 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type encoder struct {
|
||||
emitter yaml_emitter_t
|
||||
event yaml_event_t
|
||||
out []byte
|
||||
flow bool
|
||||
}
|
||||
|
||||
func newEncoder() (e *encoder) {
|
||||
e = &encoder{}
|
||||
e.must(yaml_emitter_initialize(&e.emitter))
|
||||
yaml_emitter_set_output_string(&e.emitter, &e.out)
|
||||
yaml_emitter_set_unicode(&e.emitter, true)
|
||||
e.must(yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING))
|
||||
e.emit()
|
||||
e.must(yaml_document_start_event_initialize(&e.event, nil, nil, true))
|
||||
e.emit()
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *encoder) finish() {
|
||||
e.must(yaml_document_end_event_initialize(&e.event, true))
|
||||
e.emit()
|
||||
e.emitter.open_ended = false
|
||||
e.must(yaml_stream_end_event_initialize(&e.event))
|
||||
e.emit()
|
||||
}
|
||||
|
||||
func (e *encoder) destroy() {
|
||||
yaml_emitter_delete(&e.emitter)
|
||||
}
|
||||
|
||||
func (e *encoder) emit() {
|
||||
// This will internally delete the e.event value.
|
||||
if !yaml_emitter_emit(&e.emitter, &e.event) && e.event.typ != yaml_DOCUMENT_END_EVENT && e.event.typ != yaml_STREAM_END_EVENT {
|
||||
e.must(false)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encoder) must(ok bool) {
|
||||
if !ok {
|
||||
msg := e.emitter.problem
|
||||
if msg == "" {
|
||||
msg = "unknown problem generating YAML content"
|
||||
}
|
||||
failf("%s", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encoder) marshal(tag string, in reflect.Value) {
|
||||
if !in.IsValid() {
|
||||
e.nilv()
|
||||
return
|
||||
}
|
||||
iface := in.Interface()
|
||||
if m, ok := iface.(Marshaler); ok {
|
||||
v, err := m.MarshalYAML()
|
||||
if err != nil {
|
||||
fail(err)
|
||||
}
|
||||
if v == nil {
|
||||
e.nilv()
|
||||
return
|
||||
}
|
||||
in = reflect.ValueOf(v)
|
||||
} else if m, ok := iface.(encoding.TextMarshaler); ok {
|
||||
text, err := m.MarshalText()
|
||||
if err != nil {
|
||||
fail(err)
|
||||
}
|
||||
in = reflect.ValueOf(string(text))
|
||||
}
|
||||
switch in.Kind() {
|
||||
case reflect.Interface:
|
||||
if in.IsNil() {
|
||||
e.nilv()
|
||||
} else {
|
||||
e.marshal(tag, in.Elem())
|
||||
}
|
||||
case reflect.Map:
|
||||
e.mapv(tag, in)
|
||||
case reflect.Ptr:
|
||||
if in.IsNil() {
|
||||
e.nilv()
|
||||
} else {
|
||||
e.marshal(tag, in.Elem())
|
||||
}
|
||||
case reflect.Struct:
|
||||
e.structv(tag, in)
|
||||
case reflect.Slice:
|
||||
if in.Type().Elem() == mapItemType {
|
||||
e.itemsv(tag, in)
|
||||
} else {
|
||||
e.slicev(tag, in)
|
||||
}
|
||||
case reflect.String:
|
||||
e.stringv(tag, in)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if in.Type() == durationType {
|
||||
e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String()))
|
||||
} else {
|
||||
e.intv(tag, in)
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
e.uintv(tag, in)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
e.floatv(tag, in)
|
||||
case reflect.Bool:
|
||||
e.boolv(tag, in)
|
||||
default:
|
||||
panic("cannot marshal type: " + in.Type().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (e *encoder) mapv(tag string, in reflect.Value) {
|
||||
e.mappingv(tag, func() {
|
||||
keys := keyList(in.MapKeys())
|
||||
sort.Sort(keys)
|
||||
for _, k := range keys {
|
||||
e.marshal("", k)
|
||||
e.marshal("", in.MapIndex(k))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (e *encoder) itemsv(tag string, in reflect.Value) {
|
||||
e.mappingv(tag, func() {
|
||||
slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem)
|
||||
for _, item := range slice {
|
||||
e.marshal("", reflect.ValueOf(item.Key))
|
||||
e.marshal("", reflect.ValueOf(item.Value))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (e *encoder) structv(tag string, in reflect.Value) {
|
||||
sinfo, err := getStructInfo(in.Type())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
e.mappingv(tag, func() {
|
||||
for _, info := range sinfo.FieldsList {
|
||||
var value reflect.Value
|
||||
if info.Inline == nil {
|
||||
value = in.Field(info.Num)
|
||||
} else {
|
||||
value = in.FieldByIndex(info.Inline)
|
||||
}
|
||||
if info.OmitEmpty && isZero(value) {
|
||||
continue
|
||||
}
|
||||
e.marshal("", reflect.ValueOf(info.Key))
|
||||
e.flow = info.Flow
|
||||
e.marshal("", value)
|
||||
}
|
||||
if sinfo.InlineMap >= 0 {
|
||||
m := in.Field(sinfo.InlineMap)
|
||||
if m.Len() > 0 {
|
||||
e.flow = false
|
||||
keys := keyList(m.MapKeys())
|
||||
sort.Sort(keys)
|
||||
for _, k := range keys {
|
||||
if _, found := sinfo.FieldsMap[k.String()]; found {
|
||||
panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
|
||||
}
|
||||
e.marshal("", k)
|
||||
e.flow = false
|
||||
e.marshal("", m.MapIndex(k))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (e *encoder) mappingv(tag string, f func()) {
|
||||
implicit := tag == ""
|
||||
style := yaml_BLOCK_MAPPING_STYLE
|
||||
if e.flow {
|
||||
e.flow = false
|
||||
style = yaml_FLOW_MAPPING_STYLE
|
||||
}
|
||||
e.must(yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
|
||||
e.emit()
|
||||
f()
|
||||
e.must(yaml_mapping_end_event_initialize(&e.event))
|
||||
e.emit()
|
||||
}
|
||||
|
||||
func (e *encoder) slicev(tag string, in reflect.Value) {
|
||||
implicit := tag == ""
|
||||
style := yaml_BLOCK_SEQUENCE_STYLE
|
||||
if e.flow {
|
||||
e.flow = false
|
||||
style = yaml_FLOW_SEQUENCE_STYLE
|
||||
}
|
||||
e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style))
|
||||
e.emit()
|
||||
n := in.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
e.marshal("", in.Index(i))
|
||||
}
|
||||
e.must(yaml_sequence_end_event_initialize(&e.event))
|
||||
e.emit()
|
||||
}
|
||||
|
||||
// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1.
|
||||
//
|
||||
// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported
|
||||
// in YAML 1.2 and by this package, but these should be marshalled quoted for
|
||||
// the time being for compatibility with other parsers.
|
||||
func isBase60Float(s string) (result bool) {
|
||||
// Fast path.
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
c := s[0]
|
||||
if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 {
|
||||
return false
|
||||
}
|
||||
// Do the full match.
|
||||
return base60float.MatchString(s)
|
||||
}
|
||||
|
||||
// From http://yaml.org/type/float.html, except the regular expression there
|
||||
// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
|
||||
var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
|
||||
|
||||
func (e *encoder) stringv(tag string, in reflect.Value) {
|
||||
var style yaml_scalar_style_t
|
||||
s := in.String()
|
||||
rtag, rs := resolve("", s)
|
||||
if rtag == yaml_BINARY_TAG {
|
||||
if tag == "" || tag == yaml_STR_TAG {
|
||||
tag = rtag
|
||||
s = rs.(string)
|
||||
} else if tag == yaml_BINARY_TAG {
|
||||
failf("explicitly tagged !!binary data must be base64-encoded")
|
||||
} else {
|
||||
failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag))
|
||||
}
|
||||
}
|
||||
if tag == "" && (rtag != yaml_STR_TAG || isBase60Float(s)) {
|
||||
style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
|
||||
} else if strings.Contains(s, "\n") {
|
||||
style = yaml_LITERAL_SCALAR_STYLE
|
||||
} else {
|
||||
style = yaml_PLAIN_SCALAR_STYLE
|
||||
}
|
||||
e.emitScalar(s, "", tag, style)
|
||||
}
|
||||
|
||||
func (e *encoder) boolv(tag string, in reflect.Value) {
|
||||
var s string
|
||||
if in.Bool() {
|
||||
s = "true"
|
||||
} else {
|
||||
s = "false"
|
||||
}
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *encoder) intv(tag string, in reflect.Value) {
|
||||
s := strconv.FormatInt(in.Int(), 10)
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *encoder) uintv(tag string, in reflect.Value) {
|
||||
s := strconv.FormatUint(in.Uint(), 10)
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *encoder) floatv(tag string, in reflect.Value) {
|
||||
// FIXME: Handle 64 bits here.
|
||||
s := strconv.FormatFloat(float64(in.Float()), 'g', -1, 32)
|
||||
switch s {
|
||||
case "+Inf":
|
||||
s = ".inf"
|
||||
case "-Inf":
|
||||
s = "-.inf"
|
||||
case "NaN":
|
||||
s = ".nan"
|
||||
}
|
||||
e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *encoder) nilv() {
|
||||
e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
|
||||
}
|
||||
|
||||
func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
|
||||
implicit := tag == ""
|
||||
e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
|
||||
e.emit()
|
||||
}
|
||||
1095
vendor/github.com/ajeddeloh/yaml/parserc.go
generated
vendored
1095
vendor/github.com/ajeddeloh/yaml/parserc.go
generated
vendored
File diff suppressed because it is too large
Load Diff
394
vendor/github.com/ajeddeloh/yaml/readerc.go
generated
vendored
394
vendor/github.com/ajeddeloh/yaml/readerc.go
generated
vendored
@@ -1,394 +0,0 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Set the reader error and return 0.
|
||||
func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool {
|
||||
parser.error = yaml_READER_ERROR
|
||||
parser.problem = problem
|
||||
parser.problem_offset = offset
|
||||
parser.problem_value = value
|
||||
return false
|
||||
}
|
||||
|
||||
// Byte order marks.
|
||||
const (
|
||||
bom_UTF8 = "\xef\xbb\xbf"
|
||||
bom_UTF16LE = "\xff\xfe"
|
||||
bom_UTF16BE = "\xfe\xff"
|
||||
)
|
||||
|
||||
// Determine the input stream encoding by checking the BOM symbol. If no BOM is
|
||||
// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure.
|
||||
func yaml_parser_determine_encoding(parser *yaml_parser_t) bool {
|
||||
// Ensure that we had enough bytes in the raw buffer.
|
||||
for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 {
|
||||
if !yaml_parser_update_raw_buffer(parser) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Determine the encoding.
|
||||
buf := parser.raw_buffer
|
||||
pos := parser.raw_buffer_pos
|
||||
avail := len(buf) - pos
|
||||
if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] {
|
||||
parser.encoding = yaml_UTF16LE_ENCODING
|
||||
parser.raw_buffer_pos += 2
|
||||
parser.offset += 2
|
||||
} else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] {
|
||||
parser.encoding = yaml_UTF16BE_ENCODING
|
||||
parser.raw_buffer_pos += 2
|
||||
parser.offset += 2
|
||||
} else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] {
|
||||
parser.encoding = yaml_UTF8_ENCODING
|
||||
parser.raw_buffer_pos += 3
|
||||
parser.offset += 3
|
||||
} else {
|
||||
parser.encoding = yaml_UTF8_ENCODING
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Update the raw buffer.
|
||||
func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool {
|
||||
size_read := 0
|
||||
|
||||
// Return if the raw buffer is full.
|
||||
if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Return on EOF.
|
||||
if parser.eof {
|
||||
return true
|
||||
}
|
||||
|
||||
// Move the remaining bytes in the raw buffer to the beginning.
|
||||
if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) {
|
||||
copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:])
|
||||
}
|
||||
parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos]
|
||||
parser.raw_buffer_pos = 0
|
||||
|
||||
// Call the read handler to fill the buffer.
|
||||
size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)])
|
||||
parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read]
|
||||
if err == io.EOF {
|
||||
parser.eof = true
|
||||
} else if err != nil {
|
||||
return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Ensure that the buffer contains at least `length` characters.
|
||||
// Return true on success, false on failure.
|
||||
//
|
||||
// The length is supposed to be significantly less that the buffer size.
|
||||
func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool {
|
||||
if parser.read_handler == nil {
|
||||
panic("read handler must be set")
|
||||
}
|
||||
|
||||
// If the EOF flag is set and the raw buffer is empty, do nothing.
|
||||
if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Return if the buffer contains enough characters.
|
||||
if parser.unread >= length {
|
||||
return true
|
||||
}
|
||||
|
||||
// Determine the input encoding if it is not known yet.
|
||||
if parser.encoding == yaml_ANY_ENCODING {
|
||||
if !yaml_parser_determine_encoding(parser) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Move the unread characters to the beginning of the buffer.
|
||||
buffer_len := len(parser.buffer)
|
||||
if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len {
|
||||
copy(parser.buffer, parser.buffer[parser.buffer_pos:])
|
||||
buffer_len -= parser.buffer_pos
|
||||
parser.buffer_pos = 0
|
||||
} else if parser.buffer_pos == buffer_len {
|
||||
buffer_len = 0
|
||||
parser.buffer_pos = 0
|
||||
}
|
||||
|
||||
// Open the whole buffer for writing, and cut it before returning.
|
||||
parser.buffer = parser.buffer[:cap(parser.buffer)]
|
||||
|
||||
// Fill the buffer until it has enough characters.
|
||||
first := true
|
||||
for parser.unread < length {
|
||||
|
||||
// Fill the raw buffer if necessary.
|
||||
if !first || parser.raw_buffer_pos == len(parser.raw_buffer) {
|
||||
if !yaml_parser_update_raw_buffer(parser) {
|
||||
parser.buffer = parser.buffer[:buffer_len]
|
||||
return false
|
||||
}
|
||||
}
|
||||
first = false
|
||||
|
||||
// Decode the raw buffer.
|
||||
inner:
|
||||
for parser.raw_buffer_pos != len(parser.raw_buffer) {
|
||||
var value rune
|
||||
var width int
|
||||
|
||||
raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos
|
||||
|
||||
// Decode the next character.
|
||||
switch parser.encoding {
|
||||
case yaml_UTF8_ENCODING:
|
||||
// Decode a UTF-8 character. Check RFC 3629
|
||||
// (http://www.ietf.org/rfc/rfc3629.txt) for more details.
|
||||
//
|
||||
// The following table (taken from the RFC) is used for
|
||||
// decoding.
|
||||
//
|
||||
// Char. number range | UTF-8 octet sequence
|
||||
// (hexadecimal) | (binary)
|
||||
// --------------------+------------------------------------
|
||||
// 0000 0000-0000 007F | 0xxxxxxx
|
||||
// 0000 0080-0000 07FF | 110xxxxx 10xxxxxx
|
||||
// 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
|
||||
// 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
//
|
||||
// Additionally, the characters in the range 0xD800-0xDFFF
|
||||
// are prohibited as they are reserved for use with UTF-16
|
||||
// surrogate pairs.
|
||||
|
||||
// Determine the length of the UTF-8 sequence.
|
||||
octet := parser.raw_buffer[parser.raw_buffer_pos]
|
||||
switch {
|
||||
case octet&0x80 == 0x00:
|
||||
width = 1
|
||||
case octet&0xE0 == 0xC0:
|
||||
width = 2
|
||||
case octet&0xF0 == 0xE0:
|
||||
width = 3
|
||||
case octet&0xF8 == 0xF0:
|
||||
width = 4
|
||||
default:
|
||||
// The leading octet is invalid.
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid leading UTF-8 octet",
|
||||
parser.offset, int(octet))
|
||||
}
|
||||
|
||||
// Check if the raw buffer contains an incomplete character.
|
||||
if width > raw_unread {
|
||||
if parser.eof {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"incomplete UTF-8 octet sequence",
|
||||
parser.offset, -1)
|
||||
}
|
||||
break inner
|
||||
}
|
||||
|
||||
// Decode the leading octet.
|
||||
switch {
|
||||
case octet&0x80 == 0x00:
|
||||
value = rune(octet & 0x7F)
|
||||
case octet&0xE0 == 0xC0:
|
||||
value = rune(octet & 0x1F)
|
||||
case octet&0xF0 == 0xE0:
|
||||
value = rune(octet & 0x0F)
|
||||
case octet&0xF8 == 0xF0:
|
||||
value = rune(octet & 0x07)
|
||||
default:
|
||||
value = 0
|
||||
}
|
||||
|
||||
// Check and decode the trailing octets.
|
||||
for k := 1; k < width; k++ {
|
||||
octet = parser.raw_buffer[parser.raw_buffer_pos+k]
|
||||
|
||||
// Check if the octet is valid.
|
||||
if (octet & 0xC0) != 0x80 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid trailing UTF-8 octet",
|
||||
parser.offset+k, int(octet))
|
||||
}
|
||||
|
||||
// Decode the octet.
|
||||
value = (value << 6) + rune(octet&0x3F)
|
||||
}
|
||||
|
||||
// Check the length of the sequence against the value.
|
||||
switch {
|
||||
case width == 1:
|
||||
case width == 2 && value >= 0x80:
|
||||
case width == 3 && value >= 0x800:
|
||||
case width == 4 && value >= 0x10000:
|
||||
default:
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid length of a UTF-8 sequence",
|
||||
parser.offset, -1)
|
||||
}
|
||||
|
||||
// Check the range of the value.
|
||||
if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"invalid Unicode character",
|
||||
parser.offset, int(value))
|
||||
}
|
||||
|
||||
case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING:
|
||||
var low, high int
|
||||
if parser.encoding == yaml_UTF16LE_ENCODING {
|
||||
low, high = 0, 1
|
||||
} else {
|
||||
low, high = 1, 0
|
||||
}
|
||||
|
||||
// The UTF-16 encoding is not as simple as one might
|
||||
// naively think. Check RFC 2781
|
||||
// (http://www.ietf.org/rfc/rfc2781.txt).
|
||||
//
|
||||
// Normally, two subsequent bytes describe a Unicode
|
||||
// character. However a special technique (called a
|
||||
// surrogate pair) is used for specifying character
|
||||
// values larger than 0xFFFF.
|
||||
//
|
||||
// A surrogate pair consists of two pseudo-characters:
|
||||
// high surrogate area (0xD800-0xDBFF)
|
||||
// low surrogate area (0xDC00-0xDFFF)
|
||||
//
|
||||
// The following formulas are used for decoding
|
||||
// and encoding characters using surrogate pairs:
|
||||
//
|
||||
// U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF)
|
||||
// U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF)
|
||||
// W1 = 110110yyyyyyyyyy
|
||||
// W2 = 110111xxxxxxxxxx
|
||||
//
|
||||
// where U is the character value, W1 is the high surrogate
|
||||
// area, W2 is the low surrogate area.
|
||||
|
||||
// Check for incomplete UTF-16 character.
|
||||
if raw_unread < 2 {
|
||||
if parser.eof {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"incomplete UTF-16 character",
|
||||
parser.offset, -1)
|
||||
}
|
||||
break inner
|
||||
}
|
||||
|
||||
// Get the character.
|
||||
value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) +
|
||||
(rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8)
|
||||
|
||||
// Check for unexpected low surrogate area.
|
||||
if value&0xFC00 == 0xDC00 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"unexpected low surrogate area",
|
||||
parser.offset, int(value))
|
||||
}
|
||||
|
||||
// Check for a high surrogate area.
|
||||
if value&0xFC00 == 0xD800 {
|
||||
width = 4
|
||||
|
||||
// Check for incomplete surrogate pair.
|
||||
if raw_unread < 4 {
|
||||
if parser.eof {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"incomplete UTF-16 surrogate pair",
|
||||
parser.offset, -1)
|
||||
}
|
||||
break inner
|
||||
}
|
||||
|
||||
// Get the next character.
|
||||
value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) +
|
||||
(rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8)
|
||||
|
||||
// Check for a low surrogate area.
|
||||
if value2&0xFC00 != 0xDC00 {
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"expected low surrogate area",
|
||||
parser.offset+2, int(value2))
|
||||
}
|
||||
|
||||
// Generate the value of the surrogate pair.
|
||||
value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF)
|
||||
} else {
|
||||
width = 2
|
||||
}
|
||||
|
||||
default:
|
||||
panic("impossible")
|
||||
}
|
||||
|
||||
// Check if the character is in the allowed range:
|
||||
// #x9 | #xA | #xD | [#x20-#x7E] (8 bit)
|
||||
// | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit)
|
||||
// | [#x10000-#x10FFFF] (32 bit)
|
||||
switch {
|
||||
case value == 0x09:
|
||||
case value == 0x0A:
|
||||
case value == 0x0D:
|
||||
case value >= 0x20 && value <= 0x7E:
|
||||
case value == 0x85:
|
||||
case value >= 0xA0 && value <= 0xD7FF:
|
||||
case value >= 0xE000 && value <= 0xFFFD:
|
||||
case value >= 0x10000 && value <= 0x10FFFF:
|
||||
default:
|
||||
return yaml_parser_set_reader_error(parser,
|
||||
"control characters are not allowed",
|
||||
parser.offset, int(value))
|
||||
}
|
||||
|
||||
// Move the raw pointers.
|
||||
parser.raw_buffer_pos += width
|
||||
parser.offset += width
|
||||
|
||||
// Finally put the character into the buffer.
|
||||
if value <= 0x7F {
|
||||
// 0000 0000-0000 007F . 0xxxxxxx
|
||||
parser.buffer[buffer_len+0] = byte(value)
|
||||
buffer_len += 1
|
||||
} else if value <= 0x7FF {
|
||||
// 0000 0080-0000 07FF . 110xxxxx 10xxxxxx
|
||||
parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6))
|
||||
parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F))
|
||||
buffer_len += 2
|
||||
} else if value <= 0xFFFF {
|
||||
// 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx
|
||||
parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12))
|
||||
parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F))
|
||||
parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F))
|
||||
buffer_len += 3
|
||||
} else {
|
||||
// 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
|
||||
parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18))
|
||||
parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F))
|
||||
parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F))
|
||||
parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F))
|
||||
buffer_len += 4
|
||||
}
|
||||
|
||||
parser.unread++
|
||||
}
|
||||
|
||||
// On EOF, put NUL into the buffer and return.
|
||||
if parser.eof {
|
||||
parser.buffer[buffer_len] = 0
|
||||
buffer_len++
|
||||
parser.unread++
|
||||
break
|
||||
}
|
||||
}
|
||||
parser.buffer = parser.buffer[:buffer_len]
|
||||
return true
|
||||
}
|
||||
208
vendor/github.com/ajeddeloh/yaml/resolve.go
generated
vendored
208
vendor/github.com/ajeddeloh/yaml/resolve.go
generated
vendored
@@ -1,208 +0,0 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type resolveMapItem struct {
|
||||
value interface{}
|
||||
tag string
|
||||
}
|
||||
|
||||
var resolveTable = make([]byte, 256)
|
||||
var resolveMap = make(map[string]resolveMapItem)
|
||||
|
||||
func init() {
|
||||
t := resolveTable
|
||||
t[int('+')] = 'S' // Sign
|
||||
t[int('-')] = 'S'
|
||||
for _, c := range "0123456789" {
|
||||
t[int(c)] = 'D' // Digit
|
||||
}
|
||||
for _, c := range "yYnNtTfFoO~" {
|
||||
t[int(c)] = 'M' // In map
|
||||
}
|
||||
t[int('.')] = '.' // Float (potentially in map)
|
||||
|
||||
var resolveMapList = []struct {
|
||||
v interface{}
|
||||
tag string
|
||||
l []string
|
||||
}{
|
||||
{true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}},
|
||||
{true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}},
|
||||
{true, yaml_BOOL_TAG, []string{"on", "On", "ON"}},
|
||||
{false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}},
|
||||
{false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}},
|
||||
{false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}},
|
||||
{nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}},
|
||||
{math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}},
|
||||
{math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}},
|
||||
{math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}},
|
||||
{math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}},
|
||||
{"<<", yaml_MERGE_TAG, []string{"<<"}},
|
||||
}
|
||||
|
||||
m := resolveMap
|
||||
for _, item := range resolveMapList {
|
||||
for _, s := range item.l {
|
||||
m[s] = resolveMapItem{item.v, item.tag}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const longTagPrefix = "tag:yaml.org,2002:"
|
||||
|
||||
func shortTag(tag string) string {
|
||||
// TODO This can easily be made faster and produce less garbage.
|
||||
if strings.HasPrefix(tag, longTagPrefix) {
|
||||
return "!!" + tag[len(longTagPrefix):]
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
func longTag(tag string) string {
|
||||
if strings.HasPrefix(tag, "!!") {
|
||||
return longTagPrefix + tag[2:]
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
func resolvableTag(tag string) bool {
|
||||
switch tag {
|
||||
case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var yamlStyleFloat = regexp.MustCompile(`^[-+]?[0-9]*\.?[0-9]+([eE][-+][0-9]+)?$`)
|
||||
|
||||
func resolve(tag string, in string) (rtag string, out interface{}) {
|
||||
if !resolvableTag(tag) {
|
||||
return tag, in
|
||||
}
|
||||
|
||||
defer func() {
|
||||
switch tag {
|
||||
case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG:
|
||||
return
|
||||
}
|
||||
failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag))
|
||||
}()
|
||||
|
||||
// Any data is accepted as a !!str or !!binary.
|
||||
// Otherwise, the prefix is enough of a hint about what it might be.
|
||||
hint := byte('N')
|
||||
if in != "" {
|
||||
hint = resolveTable[in[0]]
|
||||
}
|
||||
if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG {
|
||||
// Handle things we can lookup in a map.
|
||||
if item, ok := resolveMap[in]; ok {
|
||||
return item.tag, item.value
|
||||
}
|
||||
|
||||
// Base 60 floats are a bad idea, were dropped in YAML 1.2, and
|
||||
// are purposefully unsupported here. They're still quoted on
|
||||
// the way out for compatibility with other parser, though.
|
||||
|
||||
switch hint {
|
||||
case 'M':
|
||||
// We've already checked the map above.
|
||||
|
||||
case '.':
|
||||
// Not in the map, so maybe a normal float.
|
||||
floatv, err := strconv.ParseFloat(in, 64)
|
||||
if err == nil {
|
||||
return yaml_FLOAT_TAG, floatv
|
||||
}
|
||||
|
||||
case 'D', 'S':
|
||||
// Int, float, or timestamp.
|
||||
plain := strings.Replace(in, "_", "", -1)
|
||||
intv, err := strconv.ParseInt(plain, 0, 64)
|
||||
if err == nil {
|
||||
if intv == int64(int(intv)) {
|
||||
return yaml_INT_TAG, int(intv)
|
||||
} else {
|
||||
return yaml_INT_TAG, intv
|
||||
}
|
||||
}
|
||||
uintv, err := strconv.ParseUint(plain, 0, 64)
|
||||
if err == nil {
|
||||
return yaml_INT_TAG, uintv
|
||||
}
|
||||
if yamlStyleFloat.MatchString(plain) {
|
||||
floatv, err := strconv.ParseFloat(plain, 64)
|
||||
if err == nil {
|
||||
return yaml_FLOAT_TAG, floatv
|
||||
}
|
||||
}
|
||||
if strings.HasPrefix(plain, "0b") {
|
||||
intv, err := strconv.ParseInt(plain[2:], 2, 64)
|
||||
if err == nil {
|
||||
if intv == int64(int(intv)) {
|
||||
return yaml_INT_TAG, int(intv)
|
||||
} else {
|
||||
return yaml_INT_TAG, intv
|
||||
}
|
||||
}
|
||||
uintv, err := strconv.ParseUint(plain[2:], 2, 64)
|
||||
if err == nil {
|
||||
return yaml_INT_TAG, uintv
|
||||
}
|
||||
} else if strings.HasPrefix(plain, "-0b") {
|
||||
intv, err := strconv.ParseInt(plain[3:], 2, 64)
|
||||
if err == nil {
|
||||
if intv == int64(int(intv)) {
|
||||
return yaml_INT_TAG, -int(intv)
|
||||
} else {
|
||||
return yaml_INT_TAG, -intv
|
||||
}
|
||||
}
|
||||
}
|
||||
// XXX Handle timestamps here.
|
||||
|
||||
default:
|
||||
panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")")
|
||||
}
|
||||
}
|
||||
if tag == yaml_BINARY_TAG {
|
||||
return yaml_BINARY_TAG, in
|
||||
}
|
||||
if utf8.ValidString(in) {
|
||||
return yaml_STR_TAG, in
|
||||
}
|
||||
return yaml_BINARY_TAG, encodeBase64(in)
|
||||
}
|
||||
|
||||
// encodeBase64 encodes s as base64 that is broken up into multiple lines
|
||||
// as appropriate for the resulting length.
|
||||
func encodeBase64(s string) string {
|
||||
const lineLen = 70
|
||||
encLen := base64.StdEncoding.EncodedLen(len(s))
|
||||
lines := encLen/lineLen + 1
|
||||
buf := make([]byte, encLen*2+lines)
|
||||
in := buf[0:encLen]
|
||||
out := buf[encLen:]
|
||||
base64.StdEncoding.Encode(in, []byte(s))
|
||||
k := 0
|
||||
for i := 0; i < len(in); i += lineLen {
|
||||
j := i + lineLen
|
||||
if j > len(in) {
|
||||
j = len(in)
|
||||
}
|
||||
k += copy(out[k:], in[i:j])
|
||||
if lines > 1 {
|
||||
out[k] = '\n'
|
||||
k++
|
||||
}
|
||||
}
|
||||
return string(out[:k])
|
||||
}
|
||||
2711
vendor/github.com/ajeddeloh/yaml/scannerc.go
generated
vendored
2711
vendor/github.com/ajeddeloh/yaml/scannerc.go
generated
vendored
File diff suppressed because it is too large
Load Diff
104
vendor/github.com/ajeddeloh/yaml/sorter.go
generated
vendored
104
vendor/github.com/ajeddeloh/yaml/sorter.go
generated
vendored
@@ -1,104 +0,0 @@
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type keyList []reflect.Value
|
||||
|
||||
func (l keyList) Len() int { return len(l) }
|
||||
func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
|
||||
func (l keyList) Less(i, j int) bool {
|
||||
a := l[i]
|
||||
b := l[j]
|
||||
ak := a.Kind()
|
||||
bk := b.Kind()
|
||||
for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() {
|
||||
a = a.Elem()
|
||||
ak = a.Kind()
|
||||
}
|
||||
for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() {
|
||||
b = b.Elem()
|
||||
bk = b.Kind()
|
||||
}
|
||||
af, aok := keyFloat(a)
|
||||
bf, bok := keyFloat(b)
|
||||
if aok && bok {
|
||||
if af != bf {
|
||||
return af < bf
|
||||
}
|
||||
if ak != bk {
|
||||
return ak < bk
|
||||
}
|
||||
return numLess(a, b)
|
||||
}
|
||||
if ak != reflect.String || bk != reflect.String {
|
||||
return ak < bk
|
||||
}
|
||||
ar, br := []rune(a.String()), []rune(b.String())
|
||||
for i := 0; i < len(ar) && i < len(br); i++ {
|
||||
if ar[i] == br[i] {
|
||||
continue
|
||||
}
|
||||
al := unicode.IsLetter(ar[i])
|
||||
bl := unicode.IsLetter(br[i])
|
||||
if al && bl {
|
||||
return ar[i] < br[i]
|
||||
}
|
||||
if al || bl {
|
||||
return bl
|
||||
}
|
||||
var ai, bi int
|
||||
var an, bn int64
|
||||
for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ {
|
||||
an = an*10 + int64(ar[ai]-'0')
|
||||
}
|
||||
for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ {
|
||||
bn = bn*10 + int64(br[bi]-'0')
|
||||
}
|
||||
if an != bn {
|
||||
return an < bn
|
||||
}
|
||||
if ai != bi {
|
||||
return ai < bi
|
||||
}
|
||||
return ar[i] < br[i]
|
||||
}
|
||||
return len(ar) < len(br)
|
||||
}
|
||||
|
||||
// keyFloat returns a float value for v if it is a number/bool
|
||||
// and whether it is a number/bool or not.
|
||||
func keyFloat(v reflect.Value) (f float64, ok bool) {
|
||||
switch v.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return float64(v.Int()), true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float(), true
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return float64(v.Uint()), true
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
return 1, true
|
||||
}
|
||||
return 0, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// numLess returns whether a < b.
|
||||
// a and b must necessarily have the same kind.
|
||||
func numLess(a, b reflect.Value) bool {
|
||||
switch a.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return a.Int() < b.Int()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return a.Float() < b.Float()
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return a.Uint() < b.Uint()
|
||||
case reflect.Bool:
|
||||
return !a.Bool() && b.Bool()
|
||||
}
|
||||
panic("not a number")
|
||||
}
|
||||
89
vendor/github.com/ajeddeloh/yaml/writerc.go
generated
vendored
89
vendor/github.com/ajeddeloh/yaml/writerc.go
generated
vendored
@@ -1,89 +0,0 @@
|
||||
package yaml
|
||||
|
||||
// Set the writer error and return false.
|
||||
func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool {
|
||||
emitter.error = yaml_WRITER_ERROR
|
||||
emitter.problem = problem
|
||||
return false
|
||||
}
|
||||
|
||||
// Flush the output buffer.
|
||||
func yaml_emitter_flush(emitter *yaml_emitter_t) bool {
|
||||
if emitter.write_handler == nil {
|
||||
panic("write handler not set")
|
||||
}
|
||||
|
||||
// Check if the buffer is empty.
|
||||
if emitter.buffer_pos == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// If the output encoding is UTF-8, we don't need to recode the buffer.
|
||||
if emitter.encoding == yaml_UTF8_ENCODING {
|
||||
if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil {
|
||||
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
|
||||
}
|
||||
emitter.buffer_pos = 0
|
||||
return true
|
||||
}
|
||||
|
||||
// Recode the buffer into the raw buffer.
|
||||
var low, high int
|
||||
if emitter.encoding == yaml_UTF16LE_ENCODING {
|
||||
low, high = 0, 1
|
||||
} else {
|
||||
high, low = 1, 0
|
||||
}
|
||||
|
||||
pos := 0
|
||||
for pos < emitter.buffer_pos {
|
||||
// See the "reader.c" code for more details on UTF-8 encoding. Note
|
||||
// that we assume that the buffer contains a valid UTF-8 sequence.
|
||||
|
||||
// Read the next UTF-8 character.
|
||||
octet := emitter.buffer[pos]
|
||||
|
||||
var w int
|
||||
var value rune
|
||||
switch {
|
||||
case octet&0x80 == 0x00:
|
||||
w, value = 1, rune(octet&0x7F)
|
||||
case octet&0xE0 == 0xC0:
|
||||
w, value = 2, rune(octet&0x1F)
|
||||
case octet&0xF0 == 0xE0:
|
||||
w, value = 3, rune(octet&0x0F)
|
||||
case octet&0xF8 == 0xF0:
|
||||
w, value = 4, rune(octet&0x07)
|
||||
}
|
||||
for k := 1; k < w; k++ {
|
||||
octet = emitter.buffer[pos+k]
|
||||
value = (value << 6) + (rune(octet) & 0x3F)
|
||||
}
|
||||
pos += w
|
||||
|
||||
// Write the character.
|
||||
if value < 0x10000 {
|
||||
var b [2]byte
|
||||
b[high] = byte(value >> 8)
|
||||
b[low] = byte(value & 0xFF)
|
||||
emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1])
|
||||
} else {
|
||||
// Write the character using a surrogate pair (check "reader.c").
|
||||
var b [4]byte
|
||||
value -= 0x10000
|
||||
b[high] = byte(0xD8 + (value >> 18))
|
||||
b[low] = byte((value >> 10) & 0xFF)
|
||||
b[high+2] = byte(0xDC + ((value >> 8) & 0xFF))
|
||||
b[low+2] = byte(value & 0xFF)
|
||||
emitter.raw_buffer = append(emitter.raw_buffer, b[0], b[1], b[2], b[3])
|
||||
}
|
||||
}
|
||||
|
||||
// Write the raw buffer.
|
||||
if err := emitter.write_handler(emitter, emitter.raw_buffer); err != nil {
|
||||
return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error())
|
||||
}
|
||||
emitter.buffer_pos = 0
|
||||
emitter.raw_buffer = emitter.raw_buffer[:0]
|
||||
return true
|
||||
}
|
||||
368
vendor/github.com/ajeddeloh/yaml/yaml.go
generated
vendored
368
vendor/github.com/ajeddeloh/yaml/yaml.go
generated
vendored
@@ -1,368 +0,0 @@
|
||||
// Package yaml implements YAML support for the Go language.
|
||||
//
|
||||
// Source code and other details for the project are available at GitHub:
|
||||
//
|
||||
// https://github.com/go-yaml/yaml
|
||||
//
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// MapSlice encodes and decodes as a YAML map.
|
||||
// The order of keys is preserved when encoding and decoding.
|
||||
type MapSlice []MapItem
|
||||
|
||||
// MapItem is an item in a MapSlice.
|
||||
type MapItem struct {
|
||||
Key, Value interface{}
|
||||
}
|
||||
|
||||
// The Unmarshaler interface may be implemented by types to customize their
|
||||
// behavior when being unmarshaled from a YAML document. The UnmarshalYAML
|
||||
// method receives a function that may be called to unmarshal the original
|
||||
// YAML value into a field or variable. It is safe to call the unmarshal
|
||||
// function parameter more than once if necessary.
|
||||
type Unmarshaler interface {
|
||||
UnmarshalYAML(unmarshal func(interface{}) error) error
|
||||
}
|
||||
|
||||
// The Marshaler interface may be implemented by types to customize their
|
||||
// behavior when being marshaled into a YAML document. The returned value
|
||||
// is marshaled in place of the original value implementing Marshaler.
|
||||
//
|
||||
// If an error is returned by MarshalYAML, the marshaling procedure stops
|
||||
// and returns with the provided error.
|
||||
type Marshaler interface {
|
||||
MarshalYAML() (interface{}, error)
|
||||
}
|
||||
|
||||
// Unmarshal decodes the first document found within the in byte slice
|
||||
// and assigns decoded values into the out value.
|
||||
//
|
||||
// Maps and pointers (to a struct, string, int, etc) are accepted as out
|
||||
// values. If an internal pointer within a struct is not initialized,
|
||||
// the yaml package will initialize it if necessary for unmarshalling
|
||||
// the provided data. The out parameter must not be nil.
|
||||
//
|
||||
// The type of the decoded values should be compatible with the respective
|
||||
// values in out. If one or more values cannot be decoded due to a type
|
||||
// mismatches, decoding continues partially until the end of the YAML
|
||||
// content, and a *yaml.TypeError is returned with details for all
|
||||
// missed values.
|
||||
//
|
||||
// Struct fields are only unmarshalled if they are exported (have an
|
||||
// upper case first letter), and are unmarshalled using the field name
|
||||
// lowercased as the default key. Custom keys may be defined via the
|
||||
// "yaml" name in the field tag: the content preceding the first comma
|
||||
// is used as the key, and the following comma-separated options are
|
||||
// used to tweak the marshalling process (see Marshal).
|
||||
// Conflicting names result in a runtime error.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// type T struct {
|
||||
// F int `yaml:"a,omitempty"`
|
||||
// B int
|
||||
// }
|
||||
// var t T
|
||||
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
|
||||
//
|
||||
// See the documentation of Marshal for the format of tags and a list of
|
||||
// supported tag options.
|
||||
//
|
||||
func Unmarshal(in []byte, out interface{}) (err error) {
|
||||
return unmarshal(in, out, false)
|
||||
}
|
||||
|
||||
// UnmarshalStrict is like Unmarshal except that any fields that are found
|
||||
// in the data that do not have corresponding struct members will result in
|
||||
// an error.
|
||||
func UnmarshalStrict(in []byte, out interface{}) (err error) {
|
||||
return unmarshal(in, out, true)
|
||||
}
|
||||
|
||||
func unmarshal(in []byte, out interface{}, strict bool) (err error) {
|
||||
defer handleErr(&err)
|
||||
d := newDecoder(strict)
|
||||
p := newParser(in)
|
||||
defer p.destroy()
|
||||
node := p.parse()
|
||||
if node != nil {
|
||||
v := reflect.ValueOf(out)
|
||||
if v.Kind() == reflect.Ptr && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
d.unmarshal(node, v)
|
||||
}
|
||||
if len(d.terrors) > 0 {
|
||||
return &TypeError{d.terrors}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UnmarshalToNode(in []byte) *Node {
|
||||
p := newParser(in)
|
||||
//defer p.destroy()
|
||||
node := p.parse()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
tmp := Node(*node)
|
||||
return &tmp
|
||||
}
|
||||
|
||||
// Marshal serializes the value provided into a YAML document. The structure
|
||||
// of the generated document will reflect the structure of the value itself.
|
||||
// Maps and pointers (to struct, string, int, etc) are accepted as the in value.
|
||||
//
|
||||
// Struct fields are only unmarshalled if they are exported (have an upper case
|
||||
// first letter), and are unmarshalled using the field name lowercased as the
|
||||
// default key. Custom keys may be defined via the "yaml" name in the field
|
||||
// tag: the content preceding the first comma is used as the key, and the
|
||||
// following comma-separated options are used to tweak the marshalling process.
|
||||
// Conflicting names result in a runtime error.
|
||||
//
|
||||
// The field tag format accepted is:
|
||||
//
|
||||
// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
|
||||
//
|
||||
// The following flags are currently supported:
|
||||
//
|
||||
// omitempty Only include the field if it's not set to the zero
|
||||
// value for the type or to empty slices or maps.
|
||||
// Does not apply to zero valued structs.
|
||||
//
|
||||
// flow Marshal using a flow style (useful for structs,
|
||||
// sequences and maps).
|
||||
//
|
||||
// inline Inline the field, which must be a struct or a map,
|
||||
// causing all of its fields or keys to be processed as if
|
||||
// they were part of the outer struct. For maps, keys must
|
||||
// not conflict with the yaml keys of other struct fields.
|
||||
//
|
||||
// In addition, if the key is "-", the field is ignored.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// type T struct {
|
||||
// F int "a,omitempty"
|
||||
// B int
|
||||
// }
|
||||
// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
|
||||
// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n"
|
||||
//
|
||||
func Marshal(in interface{}) (out []byte, err error) {
|
||||
defer handleErr(&err)
|
||||
e := newEncoder()
|
||||
defer e.destroy()
|
||||
e.marshal("", reflect.ValueOf(in))
|
||||
e.finish()
|
||||
out = e.out
|
||||
return
|
||||
}
|
||||
|
||||
func handleErr(err *error) {
|
||||
if v := recover(); v != nil {
|
||||
if e, ok := v.(yamlError); ok {
|
||||
*err = e.err
|
||||
} else {
|
||||
panic(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type yamlError struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func fail(err error) {
|
||||
panic(yamlError{err})
|
||||
}
|
||||
|
||||
func failf(format string, args ...interface{}) {
|
||||
panic(yamlError{fmt.Errorf("yaml: "+format, args...)})
|
||||
}
|
||||
|
||||
// A TypeError is returned by Unmarshal when one or more fields in
|
||||
// the YAML document cannot be properly decoded into the requested
|
||||
// types. When this error is returned, the value is still
|
||||
// unmarshaled partially.
|
||||
type TypeError struct {
|
||||
Errors []string
|
||||
}
|
||||
|
||||
func (e *TypeError) Error() string {
|
||||
return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n "))
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Maintain a mapping of keys to structure field indexes
|
||||
|
||||
// The code in this section was copied from mgo/bson.
|
||||
|
||||
// structInfo holds details for the serialization of fields of
|
||||
// a given struct.
|
||||
type structInfo struct {
|
||||
FieldsMap map[string]fieldInfo
|
||||
FieldsList []fieldInfo
|
||||
|
||||
// InlineMap is the number of the field in the struct that
|
||||
// contains an ,inline map, or -1 if there's none.
|
||||
InlineMap int
|
||||
}
|
||||
|
||||
type fieldInfo struct {
|
||||
Key string
|
||||
Num int
|
||||
OmitEmpty bool
|
||||
Flow bool
|
||||
|
||||
// Inline holds the field index if the field is part of an inlined struct.
|
||||
Inline []int
|
||||
}
|
||||
|
||||
var structMap = make(map[reflect.Type]*structInfo)
|
||||
var fieldMapMutex sync.RWMutex
|
||||
|
||||
func getStructInfo(st reflect.Type) (*structInfo, error) {
|
||||
fieldMapMutex.RLock()
|
||||
sinfo, found := structMap[st]
|
||||
fieldMapMutex.RUnlock()
|
||||
if found {
|
||||
return sinfo, nil
|
||||
}
|
||||
|
||||
n := st.NumField()
|
||||
fieldsMap := make(map[string]fieldInfo)
|
||||
fieldsList := make([]fieldInfo, 0, n)
|
||||
inlineMap := -1
|
||||
for i := 0; i != n; i++ {
|
||||
field := st.Field(i)
|
||||
if field.PkgPath != "" && !field.Anonymous {
|
||||
continue // Private field
|
||||
}
|
||||
|
||||
info := fieldInfo{Num: i}
|
||||
|
||||
tag := field.Tag.Get("yaml")
|
||||
if tag == "" && strings.Index(string(field.Tag), ":") < 0 {
|
||||
tag = string(field.Tag)
|
||||
}
|
||||
if tag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
inline := false
|
||||
fields := strings.Split(tag, ",")
|
||||
if len(fields) > 1 {
|
||||
for _, flag := range fields[1:] {
|
||||
switch flag {
|
||||
case "omitempty":
|
||||
info.OmitEmpty = true
|
||||
case "flow":
|
||||
info.Flow = true
|
||||
case "inline":
|
||||
inline = true
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
|
||||
}
|
||||
}
|
||||
tag = fields[0]
|
||||
}
|
||||
|
||||
if inline {
|
||||
switch field.Type.Kind() {
|
||||
case reflect.Map:
|
||||
if inlineMap >= 0 {
|
||||
return nil, errors.New("Multiple ,inline maps in struct " + st.String())
|
||||
}
|
||||
if field.Type.Key() != reflect.TypeOf("") {
|
||||
return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
|
||||
}
|
||||
inlineMap = info.Num
|
||||
case reflect.Struct:
|
||||
sinfo, err := getStructInfo(field.Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, finfo := range sinfo.FieldsList {
|
||||
if _, found := fieldsMap[finfo.Key]; found {
|
||||
msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
if finfo.Inline == nil {
|
||||
finfo.Inline = []int{i, finfo.Num}
|
||||
} else {
|
||||
finfo.Inline = append([]int{i}, finfo.Inline...)
|
||||
}
|
||||
fieldsMap[finfo.Key] = finfo
|
||||
fieldsList = append(fieldsList, finfo)
|
||||
}
|
||||
default:
|
||||
//return nil, errors.New("Option ,inline needs a struct value or map field")
|
||||
return nil, errors.New("Option ,inline needs a struct value field")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if tag != "" {
|
||||
info.Key = tag
|
||||
} else {
|
||||
info.Key = strings.ToLower(field.Name)
|
||||
}
|
||||
|
||||
if _, found = fieldsMap[info.Key]; found {
|
||||
msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
|
||||
return nil, errors.New(msg)
|
||||
}
|
||||
|
||||
fieldsList = append(fieldsList, info)
|
||||
fieldsMap[info.Key] = info
|
||||
}
|
||||
|
||||
sinfo = &structInfo{fieldsMap, fieldsList, inlineMap}
|
||||
|
||||
fieldMapMutex.Lock()
|
||||
structMap[st] = sinfo
|
||||
fieldMapMutex.Unlock()
|
||||
return sinfo, nil
|
||||
}
|
||||
|
||||
func isZero(v reflect.Value) bool {
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
return len(v.String()) == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
return v.IsNil()
|
||||
case reflect.Slice:
|
||||
return v.Len() == 0
|
||||
case reflect.Map:
|
||||
return v.Len() == 0
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return v.Int() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return v.Uint() == 0
|
||||
case reflect.Bool:
|
||||
return !v.Bool()
|
||||
case reflect.Struct:
|
||||
vt := v.Type()
|
||||
for i := v.NumField() - 1; i >= 0; i-- {
|
||||
if vt.Field(i).PkgPath != "" {
|
||||
continue // Private field
|
||||
}
|
||||
if !isZero(v.Field(i)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user