Compare commits
231 Commits
experiment
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d8d35889d | ||
|
|
ef2b9f9e1b | ||
|
|
d83e436bbb | ||
|
|
00ebf584ff | ||
|
|
38dd4b1898 | ||
|
|
ece40e90d6 | ||
|
|
5e2a080d2a | ||
|
|
07decbb976 | ||
|
|
9a7fa88d60 | ||
|
|
a32520bcba | ||
|
|
39b542952b | ||
|
|
b0aafdcc54 | ||
|
|
565e4316ca | ||
|
|
961192eb9b | ||
|
|
1bd1c094f2 | ||
|
|
380fa0efda | ||
|
|
059bfd27eb | ||
|
|
45954b02ca | ||
|
|
e1b924f614 | ||
|
|
865dceddf9 | ||
|
|
172bd870c6 | ||
|
|
ee3231c879 | ||
|
|
d18ae5e249 | ||
|
|
48166b4990 | ||
|
|
ec006d3466 | ||
|
|
754051fcb4 | ||
|
|
a4f74712d8 | ||
|
|
4f9ee0b2aa | ||
|
|
06a6f04be9 | ||
|
|
094a429688 | ||
|
|
520caaf67f | ||
|
|
cffec51d48 | ||
|
|
8ccd28a009 | ||
|
|
5ecc336d73 | ||
|
|
8fb6765066 | ||
|
|
3e8a362a1b | ||
|
|
7a3d5d053b | ||
|
|
6cd6e89652 | ||
|
|
22beab49f0 | ||
|
|
72a6e2e8e4 | ||
|
|
588ece1608 | ||
|
|
cbe95869b0 | ||
|
|
21c8f5fa9c | ||
|
|
2d6d95b809 | ||
|
|
09413952ec | ||
|
|
3e89131ae2 | ||
|
|
381e90d319 | ||
|
|
b70afe012b | ||
|
|
a437576753 | ||
|
|
8c96f62349 | ||
|
|
f0b114e8d8 | ||
|
|
7b2c71435e | ||
|
|
20d34c6c0a | ||
|
|
b1ebb09184 | ||
|
|
756e93fbbb | ||
|
|
40e28f9677 | ||
|
|
9d10b07e10 | ||
|
|
1ad5a1eb42 | ||
|
|
d60c333193 | ||
|
|
a9d5a3da9d | ||
|
|
c11dd6630f | ||
|
|
78234b674c | ||
|
|
7b9a0b72d6 | ||
|
|
a645d164e0 | ||
|
|
f58a7a9b06 | ||
|
|
cd16576a1f | ||
|
|
b02023370b | ||
|
|
3acdd86c88 | ||
|
|
52a46ce079 | ||
|
|
c30cddfa06 | ||
|
|
3b7bdfc16e | ||
|
|
cc9161d223 | ||
|
|
1de505f615 | ||
|
|
32293934dd | ||
|
|
740891ff39 | ||
|
|
1b91299899 | ||
|
|
372fb3c434 | ||
|
|
d8a1a2644f | ||
|
|
dd3c206955 | ||
|
|
4db6310bf8 | ||
|
|
91e301ab39 | ||
|
|
8a775d55bb | ||
|
|
e7fcbd70ad | ||
|
|
a8beef4f92 | ||
|
|
31028809c4 | ||
|
|
018323851f | ||
|
|
2d83180d3e | ||
|
|
8209dc2b9d | ||
|
|
25b4a44d79 | ||
|
|
de2ae2dd93 | ||
|
|
05deaa3d15 | ||
|
|
29d75cac57 | ||
|
|
526a001ee8 | ||
|
|
b1a0560f70 | ||
|
|
3c7141923c | ||
|
|
ca6eb2f04a | ||
|
|
fae0bc765c | ||
|
|
1331346177 | ||
|
|
c459caf95b | ||
|
|
83c551d915 | ||
|
|
a909fe7184 | ||
|
|
94652cf772 | ||
|
|
125bf4dcc2 | ||
|
|
0c0662615b | ||
|
|
c1eebcb16e | ||
|
|
082667a542 | ||
|
|
6b4855d571 | ||
|
|
3c9a239f96 | ||
|
|
14600988d8 | ||
|
|
ec35c93bb7 | ||
|
|
352457da76 | ||
|
|
1e5c66ddd6 | ||
|
|
a656e813f8 | ||
|
|
7905b608b9 | ||
|
|
6e422fe9c2 | ||
|
|
36df44fdbf | ||
|
|
26e5525cef | ||
|
|
e90655c23b | ||
|
|
f82d5fff7a | ||
|
|
cb06f5f58d | ||
|
|
4c1a7ad08d | ||
|
|
334dbc04a6 | ||
|
|
830d23fae7 | ||
|
|
aaebcdaa19 | ||
|
|
edcd0c2f87 | ||
|
|
4e4ffe8086 | ||
|
|
e66158aba7 | ||
|
|
26fad3526f | ||
|
|
de8498e5c3 | ||
|
|
89b2109e94 | ||
|
|
16f73ee009 | ||
|
|
b36ed5879c | ||
|
|
693f8b1afa | ||
|
|
ef7b6cc5eb | ||
|
|
85296e817c | ||
|
|
7c5a2b3e1b | ||
|
|
a340341bbd | ||
|
|
5f1c30bb66 | ||
|
|
4aa1883478 | ||
|
|
4b7d19cc7b | ||
|
|
9998faaae2 | ||
|
|
eb1d63d3de | ||
|
|
a0a06b9bb6 | ||
|
|
83ea2bcee4 | ||
|
|
922ad393aa | ||
|
|
88366d31ce | ||
|
|
024b4cf6e7 | ||
|
|
728f010c0c | ||
|
|
09d444c6e4 | ||
|
|
e63e110862 | ||
|
|
b38938a417 | ||
|
|
58fda4c048 | ||
|
|
040b0b9d24 | ||
|
|
ceee81afb5 | ||
|
|
cca5aae4bd | ||
|
|
7414ec328c | ||
|
|
9023b046d8 | ||
|
|
2e0122c7c3 | ||
|
|
6d62773ba0 | ||
|
|
75dac5305f | ||
|
|
8c86d15e0c | ||
|
|
230ecb285a | ||
|
|
0ad349de7a | ||
|
|
ea4b91c8cb | ||
|
|
cd9f88d981 | ||
|
|
624da6cad1 | ||
|
|
19faca21db | ||
|
|
b7a133a50a | ||
|
|
fbf342853a | ||
|
|
e1275d1d09 | ||
|
|
5005a38227 | ||
|
|
6a71a1655e | ||
|
|
aff9dad1f4 | ||
|
|
edd06d8d82 | ||
|
|
6458f3bebe | ||
|
|
e756a3c6c6 | ||
|
|
65fe3c3228 | ||
|
|
520d1ba91e | ||
|
|
7c381b582e | ||
|
|
e37c8ee42b | ||
|
|
1ef3d37498 | ||
|
|
8c8f1ea37f | ||
|
|
4575e9dcff | ||
|
|
f753d82829 | ||
|
|
4e777b10db | ||
|
|
b9b8e0249b | ||
|
|
7e65b701c2 | ||
|
|
e40252ef2d | ||
|
|
09bc3cd31d | ||
|
|
a3970b2624 | ||
|
|
7560062fbb | ||
|
|
e07b613ac4 | ||
|
|
e338373231 | ||
|
|
e309f6b7a8 | ||
|
|
0ce281d820 | ||
|
|
70543d3449 | ||
|
|
072d407807 | ||
|
|
867c59b8fb | ||
|
|
5384d88e8c | ||
|
|
574db1a1e0 | ||
|
|
f8e2d89d6c | ||
|
|
e6fb708c0f | ||
|
|
2d72b0d57c | ||
|
|
6e4f400b54 | ||
|
|
10a2b0521a | ||
|
|
d79fa809fb | ||
|
|
dd144f140d | ||
|
|
3bc4dc15a7 | ||
|
|
1d07a29bcd | ||
|
|
bbb45a38ae | ||
|
|
82e309fc0d | ||
|
|
701a8bd4f6 | ||
|
|
ffad4229fd | ||
|
|
6c974f7903 | ||
|
|
07c396dea5 | ||
|
|
4fd46f8bbd | ||
|
|
76880d271d | ||
|
|
fbb5e09ed9 | ||
|
|
33c3553346 | ||
|
|
12b333de4c | ||
|
|
ea15fd6ec2 | ||
|
|
05afa6f5a0 | ||
|
|
28e4c55785 | ||
|
|
34c8608c9f | ||
|
|
a4b4be1fd4 | ||
|
|
efaca797e4 | ||
|
|
e5c0081c44 | ||
|
|
21826f6b12 | ||
|
|
ba33c84540 | ||
|
|
91075600ed | ||
|
|
1d6b5b8bec |
@ -6,5 +6,5 @@ dumpcerts.sh
|
||||
acme.json
|
||||
acme-backup.json
|
||||
traefik-certs-dumper
|
||||
build-docker.sh
|
||||
manifest.json
|
||||
*.Dockerfile
|
||||
|
||||
4
.github/FUNDING.yml
vendored
Normal file
4
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
github: ldez
|
||||
ko_fi: ldez_oss
|
||||
liberapay: ldez
|
||||
thanks_dev: u/gh/ldez
|
||||
40
.github/workflows/go-cross.yml
vendored
Normal file
40
.github/workflows/go-cross.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
name: Go Matrix
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
|
||||
cross:
|
||||
name: Go
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [ stable ]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
|
||||
steps:
|
||||
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# https://github.com/marketplace/actions/setup-go-environment
|
||||
- name: Set up Go ${{ matrix.go-version }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Test
|
||||
run: go test -v -cover ./...
|
||||
|
||||
- name: Build
|
||||
run: go build -v -ldflags "-s -w" -trimpath
|
||||
41
.github/workflows/main.yml
vendored
Normal file
41
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
name: Main
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
|
||||
main:
|
||||
name: Main Process
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GO_VERSION: stable
|
||||
GOLANGCI_LINT_VERSION: v2.0.1
|
||||
CGO_ENABLED: 0
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check and get dependencies
|
||||
run: |
|
||||
go mod download
|
||||
go mod tidy
|
||||
git diff --exit-code go.mod
|
||||
git diff --exit-code go.sum
|
||||
|
||||
# https://golangci-lint.run/usage/install#other-ci
|
||||
- name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
|
||||
run: |
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/HEAD/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
|
||||
golangci-lint --version
|
||||
|
||||
- name: Make
|
||||
run: make
|
||||
70
.github/workflows/release.yml
vendored
Normal file
70
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
name: "Release a tag"
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release Process
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GO_VERSION: stable
|
||||
CGO_ENABLED: 0
|
||||
|
||||
steps:
|
||||
# temporary workaround for an error in free disk space action
|
||||
# https://github.com/jlumbroso/free-disk-space/issues/14
|
||||
- name: Update Package List and Remove Dotnet
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get remove -y '^dotnet-.*'
|
||||
|
||||
# https://github.com/marketplace/actions/free-disk-space-ubuntu
|
||||
- name: Free Disk Space
|
||||
uses: jlumbroso/free-disk-space@main
|
||||
with:
|
||||
# this might remove tools that are actually needed
|
||||
tool-cache: false
|
||||
|
||||
# all of these default to true
|
||||
android: true
|
||||
dotnet: true
|
||||
haskell: true
|
||||
large-packages: true
|
||||
docker-images: true
|
||||
swap-storage: false
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: dockerhub-login
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: ghcr-login
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
version: latest
|
||||
args: release -p 1 --clean --timeout=90m
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -7,4 +7,4 @@ acme.json
|
||||
acme-backup.json
|
||||
traefik-certs-dumper
|
||||
manifest.json
|
||||
/*.Dockerfile
|
||||
/linux-*.Dockerfile
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
[run]
|
||||
deadline = "2m"
|
||||
skip-files = []
|
||||
|
||||
[linters-settings]
|
||||
|
||||
[linters-settings.govet]
|
||||
check-shadowing = true
|
||||
|
||||
[linters-settings.gocyclo]
|
||||
min-complexity = 12.0
|
||||
|
||||
[linters-settings.maligned]
|
||||
suggest-new = true
|
||||
|
||||
[linters-settings.goconst]
|
||||
min-len = 3.0
|
||||
min-occurrences = 3.0
|
||||
|
||||
[linters-settings.misspell]
|
||||
locale = "US"
|
||||
|
||||
[linters]
|
||||
enable-all = true
|
||||
disable = [
|
||||
"maligned",
|
||||
"lll",
|
||||
"gas",
|
||||
"dupl",
|
||||
"prealloc",
|
||||
"scopelint",
|
||||
]
|
||||
|
||||
[issues]
|
||||
exclude-use-default = false
|
||||
max-per-linter = 0
|
||||
max-same-issues = 0
|
||||
exclude = []
|
||||
[[issues.exclude-rules]]
|
||||
path = "version.go"
|
||||
text = "`(version|commit|date)` is a global variable"
|
||||
[[issues.exclude-rules]]
|
||||
path = "cmd/"
|
||||
linters = ["gochecknoglobals", "gochecknoinits"]
|
||||
108
.golangci.yml
Normal file
108
.golangci.yml
Normal file
@ -0,0 +1,108 @@
|
||||
version: "2"
|
||||
|
||||
formatters:
|
||||
enable:
|
||||
- gci
|
||||
- gofumpt
|
||||
settings:
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
|
||||
linters:
|
||||
default: all
|
||||
disable:
|
||||
- cyclop # duplicate of gocyclo
|
||||
- dupl
|
||||
- err113
|
||||
- exhaustive
|
||||
- exhaustruct
|
||||
- lll
|
||||
- mnd
|
||||
- nilnil
|
||||
- nlreturn
|
||||
- paralleltest
|
||||
- prealloc
|
||||
- rowserrcheck # not relevant (SQL)
|
||||
- sqlclosecheck # not relevant (SQL)
|
||||
- testpackage
|
||||
- tparallel
|
||||
- varnamelen
|
||||
- wrapcheck
|
||||
- wsl
|
||||
settings:
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
deny:
|
||||
- pkg: github.com/instana/testify
|
||||
desc: not allowed
|
||||
- pkg: github.com/pkg/errors
|
||||
desc: Should be replaced by standard lib errors package
|
||||
forbidigo:
|
||||
forbid:
|
||||
- pattern: ^print(ln)?$
|
||||
- pattern: ^spew\.Print(f|ln)?$
|
||||
- pattern: ^spew\.Dump$
|
||||
funlen:
|
||||
lines: -1
|
||||
statements: 40
|
||||
goconst:
|
||||
min-len: 3
|
||||
min-occurrences: 3
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- sloppyReassign
|
||||
- rangeValCopy
|
||||
- octalLiteral
|
||||
- paramTypeCombine # already handle by gofumpt.extra-rules
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- style
|
||||
- performance
|
||||
settings:
|
||||
hugeParam:
|
||||
sizeThreshold: 100
|
||||
gocyclo:
|
||||
min-complexity: 12
|
||||
godox:
|
||||
keywords:
|
||||
- FIXME
|
||||
gomoddirectives:
|
||||
replace-allow-list:
|
||||
- github.com/abbot/go-http-auth
|
||||
- github.com/go-check/check
|
||||
- github.com/gorilla/mux
|
||||
- github.com/mailgun/minheap
|
||||
- github.com/mailgun/multibuf
|
||||
- github.com/jaguilar/vt100
|
||||
gosec:
|
||||
excludes:
|
||||
- G204 # Subprocess launched with a potential tainted input or cmd arguments
|
||||
- G301 # Expect directory permissions to be 0750 or less
|
||||
- G306 # Expect WriteFile permissions to be 0600 or less
|
||||
govet:
|
||||
disable:
|
||||
- fieldalignment
|
||||
enable-all: true
|
||||
misspell:
|
||||
locale: US
|
||||
|
||||
exclusions:
|
||||
presets:
|
||||
- comments
|
||||
rules:
|
||||
- linters:
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
path: cmd/
|
||||
- linters:
|
||||
- tagalign
|
||||
path: internal/traefikv[1-3]/
|
||||
- path: (.+)\.go$
|
||||
text: 'ST1000: at least one file in a package should have a package comment'
|
||||
- path: (.+)\.go$
|
||||
text: 'package-comments: should have a package comment'
|
||||
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
195
.goreleaser.yml
195
.goreleaser.yml
@ -1,3 +1,4 @@
|
||||
version: 2
|
||||
project_name: traefik-certs-dumper
|
||||
|
||||
builds:
|
||||
@ -5,7 +6,7 @@ builds:
|
||||
ldflags:
|
||||
- -s -w -X github.com/ldez/traefik-certs-dumper/cmd.version={{.Version}} -X github.com/ldez/traefik-certs-dumper/cmd.commit={{.ShortCommit}} -X github.com/ldez/traefik-certs-dumper/cmd.date={{.Date}}
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
- CGO_ENABLED=0
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
@ -33,17 +34,189 @@ changelog:
|
||||
- '^docs:'
|
||||
- '^doc:'
|
||||
- '^chore:'
|
||||
- '^chore\(deps\):'
|
||||
- '^test:'
|
||||
- '^tests:'
|
||||
|
||||
archive:
|
||||
name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
files:
|
||||
- LICENSE
|
||||
archives:
|
||||
- id: tcd
|
||||
name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
formats: [ 'tar.gz' ]
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
formats: [ 'zip' ]
|
||||
files:
|
||||
- LICENSE
|
||||
|
||||
#release:
|
||||
# disable: true
|
||||
docker_manifests:
|
||||
- name_template: 'ldez/traefik-certs-dumper:{{ .Tag }}'
|
||||
image_templates:
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-amd64'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-arm64'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-armv7'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-armv6'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-386'
|
||||
- name_template: 'ldez/traefik-certs-dumper:latest'
|
||||
image_templates:
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-amd64'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-arm64'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-armv7'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-armv6'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-386'
|
||||
- name_template: 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}'
|
||||
image_templates:
|
||||
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-amd64'
|
||||
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-arm64'
|
||||
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv7'
|
||||
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv6'
|
||||
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-386'
|
||||
- name_template: 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}'
|
||||
image_templates:
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-amd64'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-arm64'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-armv7'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-armv6'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-386'
|
||||
- name_template: 'ghcr.io/ldez/traefik-certs-dumper:latest'
|
||||
image_templates:
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-amd64'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-arm64'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-armv7'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-armv6'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-386'
|
||||
- name_template: 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}'
|
||||
image_templates:
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-amd64'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-arm64'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv7'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv6'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-386'
|
||||
|
||||
dockers:
|
||||
- use: buildx
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
dockerfile: buildx.Dockerfile
|
||||
image_templates:
|
||||
- 'ldez/traefik-certs-dumper:latest-amd64'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-amd64'
|
||||
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-amd64'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:latest-amd64'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-amd64'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-amd64'
|
||||
build_flag_templates:
|
||||
- '--pull'
|
||||
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
|
||||
- '--label=org.opencontainers.image.title={{.ProjectName}}'
|
||||
- '--label=org.opencontainers.image.description=Dump ACME data from Traefik to certificates'
|
||||
- '--label=org.opencontainers.image.source={{.GitURL}}'
|
||||
- '--label=org.opencontainers.image.url={{.GitURL}}'
|
||||
- '--label=org.opencontainers.image.documentation=https://github.com/ldez/traefik-certs-dumper'
|
||||
- '--label=org.opencontainers.image.created={{.Date}}'
|
||||
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
|
||||
- '--label=org.opencontainers.image.version={{.Version}}'
|
||||
- '--platform=linux/amd64'
|
||||
|
||||
- use: buildx
|
||||
goos: linux
|
||||
goarch: arm64
|
||||
dockerfile: buildx.Dockerfile
|
||||
image_templates:
|
||||
- 'ldez/traefik-certs-dumper:latest-arm64'
|
||||
- 'ldez/traefik-certs-dumper:latest-arm.v8' # only for compatibility with Seihon
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-arm64'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-arm.v8' # only for compatibility with Seihon
|
||||
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-arm64'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:latest-arm64'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-arm64'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-arm64'
|
||||
build_flag_templates:
|
||||
- '--pull'
|
||||
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
|
||||
- '--label=org.opencontainers.image.title={{.ProjectName}}'
|
||||
- '--label=org.opencontainers.image.description=Dump ACME data from Traefik to certificates'
|
||||
- '--label=org.opencontainers.image.source={{.GitURL}}'
|
||||
- '--label=org.opencontainers.image.url={{.GitURL}}'
|
||||
- '--label=org.opencontainers.image.documentation=https://github.com/ldez/traefik-certs-dumper'
|
||||
- '--label=org.opencontainers.image.created={{.Date}}'
|
||||
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
|
||||
- '--label=org.opencontainers.image.version={{.Version}}'
|
||||
- '--platform=linux/arm64'
|
||||
|
||||
- use: buildx
|
||||
goos: linux
|
||||
goarch: arm
|
||||
goarm: '7'
|
||||
dockerfile: buildx.Dockerfile
|
||||
image_templates:
|
||||
- 'ldez/traefik-certs-dumper:latest-armv7'
|
||||
- 'ldez/traefik-certs-dumper:latest-arm.v7' # only for compatibility with Seihon
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-armv7'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-arm.v7' # only for compatibility with Seihon
|
||||
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv7'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:latest-armv7'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-armv7'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv7'
|
||||
build_flag_templates:
|
||||
- '--pull'
|
||||
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
|
||||
- '--label=org.opencontainers.image.title={{.ProjectName}}'
|
||||
- '--label=org.opencontainers.image.description=Dump ACME data from Traefik to certificates'
|
||||
- '--label=org.opencontainers.image.source={{.GitURL}}'
|
||||
- '--label=org.opencontainers.image.url={{.GitURL}}'
|
||||
- '--label=org.opencontainers.image.documentation=https://github.com/ldez/traefik-certs-dumper'
|
||||
- '--label=org.opencontainers.image.created={{.Date}}'
|
||||
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
|
||||
- '--label=org.opencontainers.image.version={{.Version}}'
|
||||
- '--platform=linux/arm/v7'
|
||||
|
||||
- use: buildx
|
||||
goos: linux
|
||||
goarch: arm
|
||||
goarm: '6'
|
||||
dockerfile: buildx.Dockerfile
|
||||
image_templates:
|
||||
- 'ldez/traefik-certs-dumper:latest-armv6'
|
||||
- 'ldez/traefik-certs-dumper:latest-arm.v6' # only for compatibility with Seihon
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-armv6'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-arm.v6' # only for compatibility with Seihon
|
||||
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv6'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:latest-armv6'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-armv6'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-armv6'
|
||||
build_flag_templates:
|
||||
- '--pull'
|
||||
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
|
||||
- '--label=org.opencontainers.image.title={{.ProjectName}}'
|
||||
- '--label=org.opencontainers.image.description=Dump ACME data from Traefik to certificates'
|
||||
- '--label=org.opencontainers.image.source={{.GitURL}}'
|
||||
- '--label=org.opencontainers.image.url={{.GitURL}}'
|
||||
- '--label=org.opencontainers.image.documentation=https://github.com/ldez/traefik-certs-dumper'
|
||||
- '--label=org.opencontainers.image.created={{.Date}}'
|
||||
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
|
||||
- '--label=org.opencontainers.image.version={{.Version}}'
|
||||
- '--platform=linux/arm/v6'
|
||||
|
||||
- use: buildx
|
||||
goos: linux
|
||||
goarch: '386'
|
||||
dockerfile: buildx.Dockerfile
|
||||
image_templates:
|
||||
- 'ldez/traefik-certs-dumper:latest-386'
|
||||
- 'ldez/traefik-certs-dumper:{{ .Tag }}-386'
|
||||
- 'ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-386'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:latest-386'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:{{ .Tag }}-386'
|
||||
- 'ghcr.io/ldez/traefik-certs-dumper:v{{ .Major }}.{{ .Minor }}-386'
|
||||
build_flag_templates:
|
||||
- '--pull'
|
||||
# https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys
|
||||
- '--label=org.opencontainers.image.title={{.ProjectName}}'
|
||||
- '--label=org.opencontainers.image.description=Dump ACME data from Traefik to certificates'
|
||||
- '--label=org.opencontainers.image.source={{.GitURL}}'
|
||||
- '--label=org.opencontainers.image.url={{.GitURL}}'
|
||||
- '--label=org.opencontainers.image.documentation=https://github.com/ldez/traefik-certs-dumper'
|
||||
- '--label=org.opencontainers.image.created={{.Date}}'
|
||||
- '--label=org.opencontainers.image.revision={{.FullCommit}}'
|
||||
- '--label=org.opencontainers.image.version={{.Version}}'
|
||||
- '--platform=linux/386'
|
||||
|
||||
48
.travis.yml
48
.travis.yml
@ -1,48 +0,0 @@
|
||||
dist: xenial
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.x
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: change
|
||||
|
||||
before_install:
|
||||
# Install linters and misspell
|
||||
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0
|
||||
- golangci-lint --version
|
||||
|
||||
install:
|
||||
- echo "TRAVIS_GO_VERSION=$TRAVIS_GO_VERSION"
|
||||
- go mod download
|
||||
|
||||
before_deploy:
|
||||
- >
|
||||
if ! [ "$BEFORE_DEPLOY_RUN" ]; then
|
||||
export BEFORE_DEPLOY_RUN=1;
|
||||
echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
|
||||
fi
|
||||
|
||||
deploy:
|
||||
- provider: script
|
||||
skip_cleanup: true
|
||||
script: curl -sL https://git.io/goreleaser | bash
|
||||
on:
|
||||
tags: true
|
||||
condition: $TRAVIS_GO_VERSION =~ ^1\.x$
|
||||
- provider: script
|
||||
skip_cleanup: true
|
||||
script: make publish-images
|
||||
on:
|
||||
tags: true
|
||||
condition: $TRAVIS_GO_VERSION =~ ^1\.x$
|
||||
26
Dockerfile
26
Dockerfile
@ -1,26 +0,0 @@
|
||||
FROM golang:1-alpine as builder
|
||||
|
||||
ARG RUNTIME_HASH
|
||||
ARG GOARCH
|
||||
ARG GOARM
|
||||
ARG GOOS
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git make gcc musl-dev
|
||||
|
||||
WORKDIR /go/src/github.com/ldez/traefik-certs-dumper
|
||||
|
||||
ENV GO111MODULE on
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
RUN GOARCH=${GOARCH} GOARM=${GOARM} GOOS=${GOOS} make build
|
||||
|
||||
FROM alpine:3.9${RUNTIME_HASH}
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add ca-certificates
|
||||
|
||||
COPY --from=builder /go/src/github.com/ldez/traefik-certs-dumper/traefik-certs-dumper /usr/bin/traefik-certs-dumper
|
||||
|
||||
ENTRYPOINT ["/usr/bin/traefik-certs-dumper"]
|
||||
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
Copyright 2019 Fernandez Ludovic
|
||||
Copyright 2019-2024 Fernandez Ludovic
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
||||
9
Makefile
9
Makefile
@ -1,5 +1,7 @@
|
||||
.PHONY: default clean checks test build
|
||||
|
||||
export GO111MODULE=on
|
||||
|
||||
TAG_NAME := $(shell git tag -l --contains HEAD)
|
||||
SHA := $(shell git rev-parse --short HEAD)
|
||||
VERSION := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
|
||||
@ -21,8 +23,5 @@ build: clean
|
||||
checks:
|
||||
golangci-lint run
|
||||
|
||||
publish-images:
|
||||
go run ./internal/multiarch.go --version="$(TAG_NAME)"
|
||||
go run ./internal/multiarch.go --version="latest"
|
||||
# VERSION=$(TAG_NAME) ./build-docker.sh
|
||||
# VERSION="latest" ./build-docker.sh
|
||||
doc:
|
||||
go run . doc
|
||||
|
||||
@ -1,88 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
VERSION=v666
|
||||
|
||||
# safe guard
|
||||
#if [ -n "$TRAVIS_TAG" ] && [ -n "$VERSION" ]; then
|
||||
# echo "Deploying..."
|
||||
#else
|
||||
# echo "Skipping deploy"
|
||||
# exit 0
|
||||
#fi
|
||||
|
||||
# base docker image name
|
||||
IMAGE_NAME="ldez/traefik-certs-dumper"
|
||||
|
||||
# only linux for now
|
||||
OS=linux
|
||||
|
||||
# target platforms in docker manifest notation
|
||||
declare -a PLATFORMS=( "amd64" "arm.v6" "arm.v7" "arm64")
|
||||
|
||||
# images from Dockerfile
|
||||
FROM_IMAGE=$(grep "{RUNTIME_HASH}" < Dockerfile | sed "s/FROM //" | sed 's/\$.*//')
|
||||
|
||||
# manifest cache file
|
||||
MANIFEST_FILE=/tmp/tcd-manifest.${FROM_IMAGE}.json
|
||||
|
||||
# get platform image hash from docker manifest
|
||||
function platformHash () {
|
||||
local ARCHITECTURE VARIANT HASH
|
||||
read -r ARCHITECTURE VARIANT <<< "$@"
|
||||
|
||||
if [ -z "$VARIANT" ]; then
|
||||
HASH=$(jq -r ".manifests[] | select(.platform.architecture == \"$ARCHITECTURE\") | .digest" < "$MANIFEST_FILE")
|
||||
else
|
||||
HASH=$(jq -r ".manifests[] | select(.platform.architecture == \"$ARCHITECTURE\" and .platform.variant == \"$VARIANT\") | .digest" < "$MANIFEST_FILE")
|
||||
fi
|
||||
|
||||
echo "$HASH"
|
||||
}
|
||||
|
||||
# get manifest
|
||||
if [ ! -f "$MANIFEST_FILE" ]; then
|
||||
docker pull "$FROM_IMAGE"
|
||||
echo "docker manifest inspect $FROM_IMAGE"
|
||||
DOCKER_CLI_EXPERIMENTAL=enabled docker manifest inspect "$FROM_IMAGE" > "$MANIFEST_FILE"
|
||||
fi
|
||||
|
||||
# create and push images
|
||||
for platform in "${PLATFORMS[@]}"; do
|
||||
# split architecture.version
|
||||
IFS='.' read -r ARCHITECTURE VARIANT <<< "$platform"
|
||||
|
||||
# add xargs to trim whitespace
|
||||
RUNTIME_HASH=$(platformHash "$ARCHITECTURE" "$VARIANT")
|
||||
|
||||
# arm architectures flavors, strip "v" prefix
|
||||
GOARM=${VARIANT:1}
|
||||
|
||||
# build for target runtime image and architecture
|
||||
# echo "docker build --build-arg=RUNTIME_HASH=@${RUNTIME_HASH} --build-arg=GOARCH=${ARCHITECTURE} --build-arg=GOARM=${GOARM} -t $IMAGE_NAME:${VERSION}-$platform" .
|
||||
docker build --build-arg="RUNTIME_HASH=@${RUNTIME_HASH}" --build-arg="GOARCH=${ARCHITECTURE}" --build-arg="GOARM=${GOARM}" -t "$IMAGE_NAME:${VERSION}-$platform" .
|
||||
|
||||
# push images
|
||||
echo "docker push $IMAGE_NAME:${VERSION}-$platform"
|
||||
# docker push "$IMAGE_NAME:${VERSION}-$platform"
|
||||
done
|
||||
|
||||
# create manifest
|
||||
TAG_LIST=$(printf "$IMAGE_NAME:${VERSION}-%s " "${PLATFORMS[@]}")
|
||||
# shellcheck disable=SC2086
|
||||
echo "docker manifest create --amend $IMAGE_NAME:${VERSION} $TAG_LIST"
|
||||
#DOCKER_CLI_EXPERIMENTAL=enabled docker manifest create --amend "$IMAGE_NAME:${VERSION}" $TAG_LIST
|
||||
|
||||
for platform in "${PLATFORMS[@]}"; do
|
||||
# split architecture.version
|
||||
IFS='.' read -r ARCHITECTURE VARIANT <<< "$platform"
|
||||
|
||||
echo "docker manifest annotate $IMAGE_NAME:${VERSION} $IMAGE_NAME:${VERSION}-$platform --os $OS --arch $ARCHITECTURE --variant $VARIANT"
|
||||
# DOCKER_CLI_EXPERIMENTAL=enabled docker manifest annotate "$IMAGE_NAME:${VERSION}" "$IMAGE_NAME:${VERSION}-$platform" --os "$OS" --arch "$ARCHITECTURE" --variant "$VARIANT"
|
||||
done
|
||||
|
||||
# push manifest
|
||||
echo "docker manifest push $IMAGE_NAME:${VERSION}"
|
||||
#DOCKER_CLI_EXPERIMENTAL=enabled docker manifest push "$IMAGE_NAME:${VERSION}"
|
||||
9
buildx.Dockerfile
Normal file
9
buildx.Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
# syntax=docker/dockerfile:1.4
|
||||
FROM alpine:3
|
||||
|
||||
RUN apk --no-cache --no-progress add git ca-certificates tzdata jq \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
COPY traefik-certs-dumper /usr/bin/traefik-certs-dumper
|
||||
|
||||
ENTRYPOINT ["/usr/bin/traefik-certs-dumper"]
|
||||
@ -1,14 +1,16 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/abronan/valkeyrie/store"
|
||||
"github.com/abronan/valkeyrie/store/boltdb"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/kvtools/boltdb"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper/kv"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// boltdbCmd represents the boltdb command
|
||||
// boltdbCmd represents the boltdb command.
|
||||
var boltdbCmd = &cobra.Command{
|
||||
Use: "boltdb",
|
||||
Short: "Dump the content of BoltDB.",
|
||||
@ -29,11 +31,20 @@ func boltdbRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
config.Options.Bucket = cmd.Flag("bucket").Value.String()
|
||||
config.Options.PersistConnection, _ = cmd.Flags().GetBool("persist-connection")
|
||||
connectionTimeout, err := cmd.Flags().GetInt("connection-timeout")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config.Backend = store.BOLTDB
|
||||
boltdb.Register()
|
||||
persistConnection, _ := cmd.Flags().GetBool("persist-connection")
|
||||
|
||||
return kv.Dump(config, baseConfig)
|
||||
config.Options = &boltdb.Config{
|
||||
Bucket: cmd.Flag("bucket").Value.String(),
|
||||
PersistConnection: persistConnection,
|
||||
ConnectionTimeout: time.Duration(connectionTimeout) * time.Second,
|
||||
}
|
||||
|
||||
config.StoreName = boltdb.StoreName
|
||||
|
||||
return kv.Dump(context.Background(), config, baseConfig)
|
||||
}
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/abronan/valkeyrie/store"
|
||||
"github.com/abronan/valkeyrie/store/consul"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/kvtools/consul"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper/kv"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// consulCmd represents the consul command
|
||||
// consulCmd represents the consul command.
|
||||
var consulCmd = &cobra.Command{
|
||||
Use: "consul",
|
||||
Short: "Dump the content of Consul.",
|
||||
@ -28,10 +30,24 @@ func consulRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
config.Options.Token = cmd.Flag("token").Value.String()
|
||||
tlsConfig, err := createTLSConfig(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config.Backend = store.CONSUL
|
||||
consul.Register()
|
||||
connectionTimeout, err := cmd.Flags().GetInt("connection-timeout")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return kv.Dump(config, baseConfig)
|
||||
config.Options = &consul.Config{
|
||||
TLS: tlsConfig,
|
||||
ConnectionTimeout: time.Duration(connectionTimeout) * time.Second,
|
||||
Token: cmd.Flag("token").Value.String(),
|
||||
Namespace: "",
|
||||
}
|
||||
|
||||
config.StoreName = consul.StoreName
|
||||
|
||||
return kv.Dump(context.Background(), config, baseConfig)
|
||||
}
|
||||
|
||||
@ -5,12 +5,12 @@ import (
|
||||
"github.com/spf13/cobra/doc"
|
||||
)
|
||||
|
||||
// docCmd represents the doc command
|
||||
// docCmd represents the doc command.
|
||||
var docCmd = &cobra.Command{
|
||||
Use: "doc",
|
||||
Short: "Generate documentation",
|
||||
Hidden: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return doc.GenMarkdownTree(rootCmd, "./docs")
|
||||
},
|
||||
}
|
||||
|
||||
49
cmd/etcd.go
49
cmd/etcd.go
@ -1,16 +1,17 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/abronan/valkeyrie/store"
|
||||
"github.com/abronan/valkeyrie/store/etcd/v2"
|
||||
"github.com/kvtools/etcdv2"
|
||||
"github.com/kvtools/etcdv3"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper/kv"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// etcdCmd represents the etcd command
|
||||
// etcdCmd represents the etcd command.
|
||||
var etcdCmd = &cobra.Command{
|
||||
Use: "etcd",
|
||||
Short: "Dump the content of etcd.",
|
||||
@ -22,6 +23,7 @@ func init() {
|
||||
kvCmd.AddCommand(etcdCmd)
|
||||
|
||||
etcdCmd.Flags().Int("sync-period", 0, "Sync period for etcd in seconds.")
|
||||
etcdCmd.Flags().String("etcd-version", "etcd", "The etcd version can be: 'etcd' or 'etcdv3'.")
|
||||
}
|
||||
|
||||
func etcdRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error {
|
||||
@ -30,14 +32,47 @@ func etcdRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
backend, err := cmd.Flags().GetString("etcd-version")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsConfig, err := createTLSConfig(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
synPeriod, err := cmd.Flags().GetInt("sync-period")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.Options.SyncPeriod = time.Duration(synPeriod) * time.Second
|
||||
|
||||
config.Backend = store.ETCD
|
||||
etcd.Register()
|
||||
connectionTimeout, err := cmd.Flags().GetInt("connection-timeout")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return kv.Dump(config, baseConfig)
|
||||
switch backend {
|
||||
case "etcdv3":
|
||||
config.Options = &etcdv3.Config{
|
||||
TLS: tlsConfig,
|
||||
ConnectionTimeout: time.Duration(connectionTimeout) * time.Second,
|
||||
SyncPeriod: time.Duration(synPeriod) * time.Second,
|
||||
Username: cmd.Flag("password").Value.String(),
|
||||
Password: cmd.Flag("username").Value.String(),
|
||||
}
|
||||
config.StoreName = etcdv3.StoreName
|
||||
default:
|
||||
config.Options = &etcdv2.Config{
|
||||
TLS: tlsConfig,
|
||||
ConnectionTimeout: time.Duration(connectionTimeout) * time.Second,
|
||||
SyncPeriod: time.Duration(synPeriod) * time.Second,
|
||||
Username: cmd.Flag("password").Value.String(),
|
||||
Password: cmd.Flag("username").Value.String(),
|
||||
}
|
||||
|
||||
config.StoreName = etcdv2.StoreName
|
||||
}
|
||||
|
||||
return kv.Dump(context.Background(), config, baseConfig)
|
||||
}
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper/file"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// fileCmd represents the file command
|
||||
var fileCmd = &cobra.Command{
|
||||
Use: "file",
|
||||
Short: `Dump the content of the "acme.json" file.`,
|
||||
@ -14,7 +15,9 @@ var fileCmd = &cobra.Command{
|
||||
RunE: runE(func(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error {
|
||||
acmeFile := cmd.Flag("source").Value.String()
|
||||
|
||||
return file.Dump(acmeFile, baseConfig)
|
||||
baseConfig.Version = cmd.Flag("version").Value.String()
|
||||
|
||||
return file.Dump(context.Background(), acmeFile, baseConfig)
|
||||
}),
|
||||
}
|
||||
|
||||
@ -22,4 +25,5 @@ func init() {
|
||||
rootCmd.AddCommand(fileCmd)
|
||||
|
||||
fileCmd.Flags().String("source", "./acme.json", "Path to 'acme.json' file.")
|
||||
fileCmd.Flags().String("version", "", "Traefik version. If empty use v1. Possible values: 'v2', 'v3'.")
|
||||
}
|
||||
|
||||
50
cmd/kv.go
50
cmd/kv.go
@ -3,17 +3,16 @@ package cmd
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/abronan/valkeyrie/store"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper/kv"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// kvCmd represents the kv command
|
||||
// kvCmd represents the kv command.
|
||||
var kvCmd = &cobra.Command{
|
||||
Use: "kv",
|
||||
Short: `Dump the content of a KV store.`,
|
||||
@ -26,6 +25,7 @@ func init() {
|
||||
kvCmd.PersistentFlags().StringSlice("endpoints", []string{"localhost:8500"}, "List of endpoints.")
|
||||
kvCmd.PersistentFlags().Int("connection-timeout", 0, "Connection timeout in seconds.")
|
||||
kvCmd.PersistentFlags().String("prefix", "traefik", "Prefix used for KV store.")
|
||||
kvCmd.PersistentFlags().String("suffix", kv.DefaultStoreKeySuffix, "Suffix/Storage used for KV store.")
|
||||
kvCmd.PersistentFlags().String("password", "", "Password for connection.")
|
||||
kvCmd.PersistentFlags().String("username", "", "Username for connection.")
|
||||
|
||||
@ -43,25 +43,10 @@ func getKvConfig(cmd *cobra.Command) (*kv.Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
connectionTimeout, err := cmd.Flags().GetInt("connection-timeout")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig, err := createTLSConfig(cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &kv.Config{
|
||||
Endpoints: endpoints,
|
||||
Prefix: cmd.Flag("prefix").Value.String(),
|
||||
Options: &store.Config{
|
||||
ConnectionTimeout: time.Duration(connectionTimeout) * time.Second,
|
||||
Username: cmd.Flag("password").Value.String(),
|
||||
Password: cmd.Flag("username").Value.String(),
|
||||
TLS: tlsConfig,
|
||||
},
|
||||
Suffix: cmd.Flag("suffix").Value.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -84,19 +69,19 @@ func createTLSConfig(cmd *cobra.Command) (*tls.Config, error) {
|
||||
privateKey := cmd.Flag("tls.key").Value.String()
|
||||
certContent := cmd.Flag("tls.cert").Value.String()
|
||||
|
||||
if !insecureSkipVerify && (len(certContent) == 0 || len(privateKey) == 0) {
|
||||
return nil, fmt.Errorf("TLS Certificate or Key file must be set when TLS configuration is created")
|
||||
if !insecureSkipVerify && (certContent == "" || privateKey == "") {
|
||||
return nil, errors.New("TLS Certificate or Key file must be set when TLS configuration is created")
|
||||
}
|
||||
|
||||
cert, err := getCertificate(privateKey, certContent)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load TLS keypair: %s", err)
|
||||
return nil, fmt.Errorf("failed to load TLS keypair: %w", err)
|
||||
}
|
||||
|
||||
return &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
RootCAs: caPool,
|
||||
InsecureSkipVerify: insecureSkipVerify,
|
||||
InsecureSkipVerify: insecureSkipVerify, //nolint:gosec // it's a CLI option.
|
||||
ClientAuth: clientAuth,
|
||||
}, nil
|
||||
}
|
||||
@ -107,11 +92,11 @@ func getCertPool(ca string) (*x509.CertPool, error) {
|
||||
if ca != "" {
|
||||
caContent, err := getCAContent(ca)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read CA. %s", err)
|
||||
return nil, fmt.Errorf("failed to read CA. %w", err)
|
||||
}
|
||||
|
||||
if !caPool.AppendCertsFromPEM(caContent) {
|
||||
return nil, fmt.Errorf("failed to parse CA")
|
||||
return nil, errors.New("failed to parse CA")
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,11 +104,14 @@ func getCertPool(ca string) (*x509.CertPool, error) {
|
||||
}
|
||||
|
||||
func getCAContent(ca string) ([]byte, error) {
|
||||
if _, errCA := os.Stat(ca); errCA != nil {
|
||||
return []byte(ca), nil
|
||||
if _, err := os.Stat(ca); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return []byte(ca), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
caContent, err := ioutil.ReadFile(ca)
|
||||
caContent, err := os.ReadFile(filepath.Clean(ca))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -150,11 +138,11 @@ func getCertificate(privateKey, certContent string) (tls.Certificate, error) {
|
||||
_, errCertIsFile := os.Stat(certContent)
|
||||
|
||||
if errCertIsFile == nil && os.IsNotExist(errKeyIsFile) {
|
||||
return tls.Certificate{}, fmt.Errorf("tls cert is a file, but tls key is not")
|
||||
return tls.Certificate{}, errors.New("tls cert is a file, but tls key is not")
|
||||
}
|
||||
|
||||
if os.IsNotExist(errCertIsFile) && errKeyIsFile == nil {
|
||||
return tls.Certificate{}, fmt.Errorf("TLS key is a file, but tls cert is not")
|
||||
return tls.Certificate{}, errors.New("TLS key is a file, but tls cert is not")
|
||||
}
|
||||
|
||||
// string
|
||||
|
||||
55
cmd/root.go
55
cmd/root.go
@ -1,27 +1,30 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var cfgFile string
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
// rootCmd represents the base command when called without any subcommands.
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "traefik-certs-dumper",
|
||||
Short: "Dump Let's Encrypt certificates from Traefik.",
|
||||
Long: `Dump Let's Encrypt certificates from Traefik.`,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
|
||||
if cmd.Name() == "version" {
|
||||
return nil
|
||||
}
|
||||
@ -35,6 +38,7 @@ var rootCmd = &cobra.Command{
|
||||
return fmt.Errorf("--crt-ext (%q) and --key-ext (%q) are identical, in this case --domain-subdir is required", crtExt, keyExt)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@ -46,6 +50,8 @@ func Execute() {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
help()
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -109,7 +115,7 @@ func runE(apply func(*dumper.BaseConfig, *cobra.Command) error) func(*cobra.Comm
|
||||
func tree(root, indent string) error {
|
||||
fi, err := os.Stat(root)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not stat %s: %v", root, err)
|
||||
return fmt.Errorf("could not stat %s: %w", root, err)
|
||||
}
|
||||
|
||||
fmt.Println(fi.Name())
|
||||
@ -117,9 +123,9 @@ func tree(root, indent string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
fis, err := ioutil.ReadDir(root)
|
||||
fis, err := os.ReadDir(root)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read dir %s: %v", root, err)
|
||||
return fmt.Errorf("could not read dir %s: %w", root, err)
|
||||
}
|
||||
|
||||
var names []string
|
||||
@ -132,10 +138,10 @@ func tree(root, indent string) error {
|
||||
for i, name := range names {
|
||||
add := "│ "
|
||||
if i == len(names)-1 {
|
||||
fmt.Printf(indent + "└──")
|
||||
fmt.Print(indent + "└──")
|
||||
add = " "
|
||||
} else {
|
||||
fmt.Printf(indent + "├──")
|
||||
fmt.Print(indent + "├──")
|
||||
}
|
||||
|
||||
if err := tree(filepath.Join(root, name), indent+add); err != nil {
|
||||
@ -178,3 +184,34 @@ func getBaseConfig(cmd *cobra.Command) (*dumper.BaseConfig, error) {
|
||||
Hook: cmd.Flag("post-hook").Value.String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func help() {
|
||||
var maxInt int64 = 2 // -> 50%
|
||||
if time.Now().Month() == time.December {
|
||||
maxInt = 1 // -> 100%
|
||||
}
|
||||
|
||||
n, _ := rand.Int(rand.Reader, big.NewInt(maxInt))
|
||||
if n.Cmp(big.NewInt(0)) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
log.SetFlags(0)
|
||||
|
||||
pStyle := lipgloss.NewStyle().
|
||||
Padding(1).
|
||||
BorderStyle(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("161")).
|
||||
Align(lipgloss.Center)
|
||||
|
||||
hStyle := lipgloss.NewStyle().Bold(true)
|
||||
|
||||
s := fmt.Sprintln(hStyle.Render("Request for Donation."))
|
||||
s += `
|
||||
I need your help!
|
||||
Donations fund the maintenance and development of traefik-certs-dumper.
|
||||
Click on this link to donate: https://donate.ldez.dev`
|
||||
|
||||
log.Println(pStyle.Render(s))
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
}
|
||||
|
||||
@ -13,11 +13,11 @@ var (
|
||||
date = "I don't remember exactly"
|
||||
)
|
||||
|
||||
// versionCmd represents the version command
|
||||
// versionCmd represents the version command.
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Display version",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(_ *cobra.Command, _ []string) {
|
||||
displayVersion(rootCmd.Name())
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/abronan/valkeyrie/store"
|
||||
"github.com/abronan/valkeyrie/store/zookeeper"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/kvtools/zookeeper"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper/kv"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// zookeeperCmd represents the zookeeper command
|
||||
// zookeeperCmd represents the zookeeper command.
|
||||
var zookeeperCmd = &cobra.Command{
|
||||
Use: "zookeeper",
|
||||
Short: "Dump the content of zookeeper.",
|
||||
@ -26,8 +28,19 @@ func zookeeperRun(baseConfig *dumper.BaseConfig, cmd *cobra.Command) error {
|
||||
return err
|
||||
}
|
||||
|
||||
config.Backend = store.ZK
|
||||
zookeeper.Register()
|
||||
connectionTimeout, err := cmd.Flags().GetInt("connection-timeout")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return kv.Dump(config, baseConfig)
|
||||
config.Options = &zookeeper.Config{
|
||||
ConnectionTimeout: time.Duration(connectionTimeout) * time.Second,
|
||||
Username: cmd.Flag("password").Value.String(),
|
||||
Password: cmd.Flag("username").Value.String(),
|
||||
MaxBufferSize: 0,
|
||||
}
|
||||
|
||||
config.StoreName = zookeeper.StoreName
|
||||
|
||||
return kv.Dump(context.Background(), config, baseConfig)
|
||||
}
|
||||
|
||||
1
contrib/readme.md
Normal file
1
contrib/readme.md
Normal file
@ -0,0 +1 @@
|
||||
This directory content external contributions that are not maintain by @ldez.
|
||||
40
contrib/traefik-certs-dumper.service
Normal file
40
contrib/traefik-certs-dumper.service
Normal file
@ -0,0 +1,40 @@
|
||||
[Unit]
|
||||
Description=traefik certs dumper
|
||||
; If you do not start traefik via systemd, choose network.target or docker.target
|
||||
After=traefik.target
|
||||
Wants=network-online.target systemd-networkd-wait-online.service
|
||||
|
||||
[Service]
|
||||
Restart=on-abnormal
|
||||
User=root
|
||||
ExecStart=/usr/local/bin/traefik-certs-dumper file --version v2 --source /etc/traefik/acme/acme.json --dest /etc/ssl --watch
|
||||
RestartSec=30
|
||||
TimeoutSec=30
|
||||
;WatchdogSec=30
|
||||
|
||||
; Limit the number of file descriptors; see `man systemd.exec` for more limit settings.
|
||||
; LimitNOFILE=1048576
|
||||
; Limit number of processes in this unit
|
||||
LimitNPROC=1
|
||||
|
||||
; Use private /tmp and /var/tmp, which are discarded after traefik stops.
|
||||
PrivateTmp=true
|
||||
; Use a minimal /dev (May bring additional security if switched to 'true', but it may not work on Raspberry Pis or other devices)
|
||||
PrivateDevices=true
|
||||
; Hide /home, /root, and /run/user. Nobody will steal your SSH-keys.
|
||||
ProtectHome=true
|
||||
; Make cgroups /sys/fs/cgroup read-only
|
||||
ProtectControlGroups=true
|
||||
; Make kernel settings (procfs and sysfs) read-only
|
||||
ProtectKernelTunables=true
|
||||
; Make /usr, /boot, /etc and possibly some more folders read-only.
|
||||
ProtectSystem=full
|
||||
; This merely retains r/w access rights, it does not add any new. Must still be writable on the host!
|
||||
ReadWriteDirectories=/etc/ssl
|
||||
ReadOnlyPaths=/etc/traefik/acme/acme.json
|
||||
|
||||
; The following additional security directives only work with systemd v229 or later.
|
||||
NoNewPrivileges=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
42
docs/docker-compose-traefik-v1.yml
Normal file
42
docs/docker-compose-traefik-v1.yml
Normal file
@ -0,0 +1,42 @@
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v1.7
|
||||
command:
|
||||
- --logLevel=INFO
|
||||
- --defaultEntryPoints=web,websecure
|
||||
- "--entryPoints=Name:web Address::80 Redirect.EntryPoint:websecure"
|
||||
- "--entryPoints=Name:websecure Address::443 TLS"
|
||||
- --docker
|
||||
- --docker.exposedByDefault=false
|
||||
- --acme
|
||||
- --acme.email=email@example.com
|
||||
- --acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
- --acme.entrypoint=websecure
|
||||
- --acme.storage=/letsencrypt/acme.json
|
||||
- --acme.onHostRule
|
||||
- --acme.tlsChallenge
|
||||
ports:
|
||||
- 80:80
|
||||
- 443:443
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./letsencrypt:/letsencrypt
|
||||
|
||||
traefik-certs-dumper:
|
||||
image: ldez/traefik-certs-dumper:v2.9.3
|
||||
entrypoint: sh -c '
|
||||
while ! [ -e /data/acme.json ]
|
||||
|| ! [ `jq ".Certificates | length" /data/acme.json` != 0 ]; do
|
||||
sleep 1
|
||||
; done
|
||||
&& traefik-certs-dumper file --watch
|
||||
--source /data/acme.json --dest /data/certs'
|
||||
volumes:
|
||||
- ./letsencrypt:/data
|
||||
network_mode: "none"
|
||||
|
||||
whoami:
|
||||
image: traefik/whoami:v1.8.1
|
||||
labels:
|
||||
traefik.enable: true
|
||||
traefik.frontend.rule: Host:example.com
|
||||
44
docs/docker-compose-traefik-v2.yml
Normal file
44
docs/docker-compose-traefik-v2.yml
Normal file
@ -0,0 +1,44 @@
|
||||
services:
|
||||
|
||||
traefik:
|
||||
image: traefik:v2.11.3
|
||||
command:
|
||||
- --log.level=INFO
|
||||
- --entrypoints.web.address=:80
|
||||
- --entrypoints.web.http.redirections.entrypoint.to=websecure
|
||||
- --entrypoints.web.http.redirections.entrypoint.scheme=https
|
||||
- --entrypoints.websecure.address=:443
|
||||
- --entrypoints.websecure.http.tls=true
|
||||
- --entrypoints.websecure.http.tls.certResolver=le
|
||||
- --providers.docker.exposedbydefault=false
|
||||
- --certificatesresolvers.le.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
- --certificatesresolvers.le.acme.email=email@example.com
|
||||
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
|
||||
- --certificatesresolvers.le.acme.tlsChallenge=true
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- ./letsencrypt/:/letsencrypt
|
||||
|
||||
traefik-certs-dumper:
|
||||
image: ldez/traefik-certs-dumper:v2.9.3
|
||||
entrypoint: sh -c '
|
||||
while ! [ -e /data/acme.json ]
|
||||
|| ! [ `jq ".[] | .Certificates | length" /data/acme.json | jq -s "add" ` != 0 ]; do
|
||||
sleep 1
|
||||
; done
|
||||
&& traefik-certs-dumper file --version v2 --watch
|
||||
--source /data/acme.json --dest /data/certs'
|
||||
volumes:
|
||||
- ./letsencrypt:/data
|
||||
network_mode: "none"
|
||||
|
||||
whoami:
|
||||
image: traefik/whoami:v1.8.1
|
||||
labels:
|
||||
traefik.enable: 'true'
|
||||
|
||||
traefik.http.routers.app.rule: Host(`example.com`)
|
||||
traefik.http.routers.app.entrypoints: websecure
|
||||
@ -24,8 +24,9 @@ Dump Let's Encrypt certificates from Traefik.
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [traefik-certs-dumper completion](traefik-certs-dumper_completion.md) - Generate the autocompletion script for the specified shell
|
||||
* [traefik-certs-dumper file](traefik-certs-dumper_file.md) - Dump the content of the "acme.json" file.
|
||||
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
|
||||
* [traefik-certs-dumper version](traefik-certs-dumper_version.md) - Display version
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
|
||||
40
docs/traefik-certs-dumper_completion.md
Normal file
40
docs/traefik-certs-dumper_completion.md
Normal file
@ -0,0 +1,40 @@
|
||||
## traefik-certs-dumper completion
|
||||
|
||||
Generate the autocompletion script for the specified shell
|
||||
|
||||
### Synopsis
|
||||
|
||||
Generate the autocompletion script for traefik-certs-dumper for the specified shell.
|
||||
See each sub-command's help for details on how to use the generated script.
|
||||
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for completion
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
|
||||
--dest string Path to store the dump content. (default "./dump")
|
||||
--domain-subdir Use domain as sub-directory.
|
||||
--key-ext string The file extension of the generated private keys. (default ".key")
|
||||
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
|
||||
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
|
||||
--watch Enable watching changes.
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [traefik-certs-dumper](traefik-certs-dumper.md) - Dump Let's Encrypt certificates from Traefik.
|
||||
* [traefik-certs-dumper completion bash](traefik-certs-dumper_completion_bash.md) - Generate the autocompletion script for bash
|
||||
* [traefik-certs-dumper completion fish](traefik-certs-dumper_completion_fish.md) - Generate the autocompletion script for fish
|
||||
* [traefik-certs-dumper completion powershell](traefik-certs-dumper_completion_powershell.md) - Generate the autocompletion script for powershell
|
||||
* [traefik-certs-dumper completion zsh](traefik-certs-dumper_completion_zsh.md) - Generate the autocompletion script for zsh
|
||||
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
59
docs/traefik-certs-dumper_completion_bash.md
Normal file
59
docs/traefik-certs-dumper_completion_bash.md
Normal file
@ -0,0 +1,59 @@
|
||||
## traefik-certs-dumper completion bash
|
||||
|
||||
Generate the autocompletion script for bash
|
||||
|
||||
### Synopsis
|
||||
|
||||
Generate the autocompletion script for the bash shell.
|
||||
|
||||
This script depends on the 'bash-completion' package.
|
||||
If it is not installed already, you can install it via your OS's package manager.
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
source <(traefik-certs-dumper completion bash)
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
|
||||
#### Linux:
|
||||
|
||||
traefik-certs-dumper completion bash > /etc/bash_completion.d/traefik-certs-dumper
|
||||
|
||||
#### macOS:
|
||||
|
||||
traefik-certs-dumper completion bash > $(brew --prefix)/etc/bash_completion.d/traefik-certs-dumper
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
|
||||
|
||||
```
|
||||
traefik-certs-dumper completion bash
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for bash
|
||||
--no-descriptions disable completion descriptions
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
|
||||
--dest string Path to store the dump content. (default "./dump")
|
||||
--domain-subdir Use domain as sub-directory.
|
||||
--key-ext string The file extension of the generated private keys. (default ".key")
|
||||
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
|
||||
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
|
||||
--watch Enable watching changes.
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [traefik-certs-dumper completion](traefik-certs-dumper_completion.md) - Generate the autocompletion script for the specified shell
|
||||
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
50
docs/traefik-certs-dumper_completion_fish.md
Normal file
50
docs/traefik-certs-dumper_completion_fish.md
Normal file
@ -0,0 +1,50 @@
|
||||
## traefik-certs-dumper completion fish
|
||||
|
||||
Generate the autocompletion script for fish
|
||||
|
||||
### Synopsis
|
||||
|
||||
Generate the autocompletion script for the fish shell.
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
traefik-certs-dumper completion fish | source
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
|
||||
traefik-certs-dumper completion fish > ~/.config/fish/completions/traefik-certs-dumper.fish
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
|
||||
|
||||
```
|
||||
traefik-certs-dumper completion fish [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for fish
|
||||
--no-descriptions disable completion descriptions
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
|
||||
--dest string Path to store the dump content. (default "./dump")
|
||||
--domain-subdir Use domain as sub-directory.
|
||||
--key-ext string The file extension of the generated private keys. (default ".key")
|
||||
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
|
||||
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
|
||||
--watch Enable watching changes.
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [traefik-certs-dumper completion](traefik-certs-dumper_completion.md) - Generate the autocompletion script for the specified shell
|
||||
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
47
docs/traefik-certs-dumper_completion_powershell.md
Normal file
47
docs/traefik-certs-dumper_completion_powershell.md
Normal file
@ -0,0 +1,47 @@
|
||||
## traefik-certs-dumper completion powershell
|
||||
|
||||
Generate the autocompletion script for powershell
|
||||
|
||||
### Synopsis
|
||||
|
||||
Generate the autocompletion script for powershell.
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
traefik-certs-dumper completion powershell | Out-String | Invoke-Expression
|
||||
|
||||
To load completions for every new session, add the output of the above command
|
||||
to your powershell profile.
|
||||
|
||||
|
||||
```
|
||||
traefik-certs-dumper completion powershell [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for powershell
|
||||
--no-descriptions disable completion descriptions
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
|
||||
--dest string Path to store the dump content. (default "./dump")
|
||||
--domain-subdir Use domain as sub-directory.
|
||||
--key-ext string The file extension of the generated private keys. (default ".key")
|
||||
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
|
||||
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
|
||||
--watch Enable watching changes.
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [traefik-certs-dumper completion](traefik-certs-dumper_completion.md) - Generate the autocompletion script for the specified shell
|
||||
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
61
docs/traefik-certs-dumper_completion_zsh.md
Normal file
61
docs/traefik-certs-dumper_completion_zsh.md
Normal file
@ -0,0 +1,61 @@
|
||||
## traefik-certs-dumper completion zsh
|
||||
|
||||
Generate the autocompletion script for zsh
|
||||
|
||||
### Synopsis
|
||||
|
||||
Generate the autocompletion script for the zsh shell.
|
||||
|
||||
If shell completion is not already enabled in your environment you will need
|
||||
to enable it. You can execute the following once:
|
||||
|
||||
echo "autoload -U compinit; compinit" >> ~/.zshrc
|
||||
|
||||
To load completions in your current shell session:
|
||||
|
||||
source <(traefik-certs-dumper completion zsh)
|
||||
|
||||
To load completions for every new session, execute once:
|
||||
|
||||
#### Linux:
|
||||
|
||||
traefik-certs-dumper completion zsh > "${fpath[1]}/_traefik-certs-dumper"
|
||||
|
||||
#### macOS:
|
||||
|
||||
traefik-certs-dumper completion zsh > $(brew --prefix)/share/zsh/site-functions/_traefik-certs-dumper
|
||||
|
||||
You will need to start a new shell for this setup to take effect.
|
||||
|
||||
|
||||
```
|
||||
traefik-certs-dumper completion zsh [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for zsh
|
||||
--no-descriptions disable completion descriptions
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--clean Clean destination folder before dumping content. (default true)
|
||||
--config string config file (default is $HOME/.traefik-certs-dumper.yaml)
|
||||
--crt-ext string The file extension of the generated certificates. (default ".crt")
|
||||
--crt-name string The file name (without extension) of the generated certificates. (default "certificate")
|
||||
--dest string Path to store the dump content. (default "./dump")
|
||||
--domain-subdir Use domain as sub-directory.
|
||||
--key-ext string The file extension of the generated private keys. (default ".key")
|
||||
--key-name string The file name (without extension) of the generated private keys. (default "privatekey")
|
||||
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
|
||||
--watch Enable watching changes.
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [traefik-certs-dumper completion](traefik-certs-dumper_completion.md) - Generate the autocompletion script for the specified shell
|
||||
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
@ -13,8 +13,9 @@ traefik-certs-dumper file [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for file
|
||||
--source string Path to 'acme.json' file. (default "./acme.json")
|
||||
-h, --help help for file
|
||||
--source string Path to 'acme.json' file. (default "./acme.json")
|
||||
--version string Traefik version. If empty use v1. Possible values: 'v2', 'v3'.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@ -36,4 +37,4 @@ traefik-certs-dumper file [flags]
|
||||
|
||||
* [traefik-certs-dumper](traefik-certs-dumper.md) - Dump Let's Encrypt certificates from Traefik.
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
|
||||
@ -14,6 +14,7 @@ Dump the content of a KV store.
|
||||
-h, --help help for kv
|
||||
--password string Password for connection.
|
||||
--prefix string Prefix used for KV store. (default "traefik")
|
||||
--suffix string Suffix/Storage used for KV store. (default "/acme/account/object")
|
||||
--tls Enable TLS encryption.
|
||||
--tls.ca string Root CA for certificate verification if TLS is enabled
|
||||
--tls.ca.optional
|
||||
@ -46,4 +47,4 @@ Dump the content of a KV store.
|
||||
* [traefik-certs-dumper kv etcd](traefik-certs-dumper_kv_etcd.md) - Dump the content of etcd.
|
||||
* [traefik-certs-dumper kv zookeeper](traefik-certs-dumper_kv_zookeeper.md) - Dump the content of zookeeper.
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
|
||||
@ -34,6 +34,7 @@ traefik-certs-dumper kv boltdb [flags]
|
||||
--password string Password for connection.
|
||||
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
|
||||
--prefix string Prefix used for KV store. (default "traefik")
|
||||
--suffix string Suffix/Storage used for KV store. (default "/acme/account/object")
|
||||
--tls Enable TLS encryption.
|
||||
--tls.ca string Root CA for certificate verification if TLS is enabled
|
||||
--tls.ca.optional
|
||||
@ -48,4 +49,4 @@ traefik-certs-dumper kv boltdb [flags]
|
||||
|
||||
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
|
||||
@ -33,6 +33,7 @@ traefik-certs-dumper kv consul [flags]
|
||||
--password string Password for connection.
|
||||
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
|
||||
--prefix string Prefix used for KV store. (default "traefik")
|
||||
--suffix string Suffix/Storage used for KV store. (default "/acme/account/object")
|
||||
--tls Enable TLS encryption.
|
||||
--tls.ca string Root CA for certificate verification if TLS is enabled
|
||||
--tls.ca.optional
|
||||
@ -47,4 +48,4 @@ traefik-certs-dumper kv consul [flags]
|
||||
|
||||
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
|
||||
@ -13,8 +13,9 @@ traefik-certs-dumper kv etcd [flags]
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for etcd
|
||||
--sync-period int Sync period for etcd in seconds.
|
||||
--etcd-version string The etcd version can be: 'etcd' or 'etcdv3'. (default "etcd")
|
||||
-h, --help help for etcd
|
||||
--sync-period int Sync period for etcd in seconds.
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@ -33,6 +34,7 @@ traefik-certs-dumper kv etcd [flags]
|
||||
--password string Password for connection.
|
||||
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
|
||||
--prefix string Prefix used for KV store. (default "traefik")
|
||||
--suffix string Suffix/Storage used for KV store. (default "/acme/account/object")
|
||||
--tls Enable TLS encryption.
|
||||
--tls.ca string Root CA for certificate verification if TLS is enabled
|
||||
--tls.ca.optional
|
||||
@ -47,4 +49,4 @@ traefik-certs-dumper kv etcd [flags]
|
||||
|
||||
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
|
||||
@ -32,6 +32,7 @@ traefik-certs-dumper kv zookeeper [flags]
|
||||
--password string Password for connection.
|
||||
--post-hook string Execute a command only if changes occurs on the data source. (works only with the watch mode)
|
||||
--prefix string Prefix used for KV store. (default "traefik")
|
||||
--suffix string Suffix/Storage used for KV store. (default "/acme/account/object")
|
||||
--tls Enable TLS encryption.
|
||||
--tls.ca string Root CA for certificate verification if TLS is enabled
|
||||
--tls.ca.optional
|
||||
@ -46,4 +47,4 @@ traefik-certs-dumper kv zookeeper [flags]
|
||||
|
||||
* [traefik-certs-dumper kv](traefik-certs-dumper_kv.md) - Dump the content of a KV store.
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
|
||||
@ -2,10 +2,6 @@
|
||||
|
||||
Display version
|
||||
|
||||
### Synopsis
|
||||
|
||||
Display version
|
||||
|
||||
```
|
||||
traefik-certs-dumper version [flags]
|
||||
```
|
||||
@ -35,4 +31,4 @@ traefik-certs-dumper version [flags]
|
||||
|
||||
* [traefik-certs-dumper](traefik-certs-dumper.md) - Dump Let's Encrypt certificates from Traefik.
|
||||
|
||||
###### Auto generated by spf13/cobra on 25-Apr-2019
|
||||
###### Auto generated by spf13/cobra on 21-Feb-2025
|
||||
|
||||
@ -9,4 +9,11 @@ type BaseConfig struct {
|
||||
Clean bool
|
||||
Watch bool
|
||||
Hook string
|
||||
Version string
|
||||
}
|
||||
|
||||
// FileInfo File information.
|
||||
type FileInfo struct {
|
||||
Name string
|
||||
Ext string
|
||||
}
|
||||
|
||||
@ -2,58 +2,133 @@ package file
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
dumperv1 "github.com/ldez/traefik-certs-dumper/v2/dumper/v1"
|
||||
dumperv2 "github.com/ldez/traefik-certs-dumper/v2/dumper/v2"
|
||||
dumperv3 "github.com/ldez/traefik-certs-dumper/v2/dumper/v3"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/hook"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv1"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv2"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv3"
|
||||
)
|
||||
|
||||
// Dump Dumps "acme.json" file to certificates.
|
||||
func Dump(acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
func Dump(ctx context.Context, acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
err := dump(acmeFile, baseConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if baseConfig.Watch {
|
||||
return watch(acmeFile, baseConfig)
|
||||
hook.Exec(ctx, baseConfig.Hook)
|
||||
|
||||
return watch(ctx, acmeFile, baseConfig)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dump(acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
data, err := readFile(acmeFile)
|
||||
switch baseConfig.Version {
|
||||
case "v3":
|
||||
err := dumpV3(acmeFile, baseConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("v3: dump failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case "v2":
|
||||
err := dumpV2(acmeFile, baseConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("v2: dump failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case "v1":
|
||||
err := dumpV1(acmeFile, baseConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("v1: dump failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
default:
|
||||
err := dumpV1(acmeFile, baseConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("v1: dump failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func dumpV1(acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
data := &traefikv1.StoredData{}
|
||||
err := readJSONFile(acmeFile, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dumper.Dump(data, baseConfig)
|
||||
return dumperv1.Dump(data, baseConfig)
|
||||
}
|
||||
|
||||
func readFile(acmeFile string) (*dumper.StoredData, error) {
|
||||
source, err := os.Open(acmeFile)
|
||||
func dumpV2(acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
data := map[string]*traefikv2.StoredData{}
|
||||
err := readJSONFile(acmeFile, &data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
data := &dumper.StoredData{}
|
||||
if err = json.NewDecoder(source).Decode(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
return dumperv2.Dump(data, baseConfig)
|
||||
}
|
||||
|
||||
func watch(acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
func dumpV3(acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
data := map[string]*traefikv3.StoredData{}
|
||||
err := readJSONFile(acmeFile, &data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dumperv3.Dump(data, baseConfig)
|
||||
}
|
||||
|
||||
func readJSONFile(acmeFile string, data interface{}) error {
|
||||
source, err := os.Open(filepath.Clean(acmeFile))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file %q: %w", acmeFile, err)
|
||||
}
|
||||
defer func() { _ = source.Close() }()
|
||||
|
||||
err = json.NewDecoder(source).Decode(data)
|
||||
if errors.Is(err, io.EOF) {
|
||||
log.Printf("warn: file %q may not be ready: %v", acmeFile, err)
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal file %q: %w", acmeFile, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func watch(ctx context.Context, acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to create new watcher: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = watcher.Close() }()
|
||||
@ -73,7 +148,7 @@ func watch(acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
log.Println("event:", event)
|
||||
}
|
||||
|
||||
hash, errW := manageEvent(watcher, event, acmeFile, previousHash, baseConfig)
|
||||
hash, errW := manageEvent(ctx, watcher, event, acmeFile, previousHash, baseConfig)
|
||||
if errW != nil {
|
||||
log.Println("error:", errW)
|
||||
done <- true
|
||||
@ -96,7 +171,7 @@ func watch(acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
|
||||
err = watcher.Add(acmeFile)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to add a new watcher: %w", err)
|
||||
}
|
||||
|
||||
<-done
|
||||
@ -104,15 +179,15 @@ func watch(acmeFile string, baseConfig *dumper.BaseConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func manageEvent(watcher *fsnotify.Watcher, event fsnotify.Event, acmeFile string, previousHash []byte, baseConfig *dumper.BaseConfig) ([]byte, error) {
|
||||
func manageEvent(ctx context.Context, watcher *fsnotify.Watcher, event fsnotify.Event, acmeFile string, previousHash []byte, baseConfig *dumper.BaseConfig) ([]byte, error) {
|
||||
err := manageRename(watcher, event, acmeFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("watcher renewal failed: %w", err)
|
||||
}
|
||||
|
||||
hash, err := calculateHash(acmeFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("file hash calculation failed: %w", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(previousHash, hash) {
|
||||
@ -128,7 +203,7 @@ func manageEvent(watcher *fsnotify.Watcher, event fsnotify.Event, acmeFile strin
|
||||
log.Println("Dumped new certificate data.")
|
||||
}
|
||||
|
||||
hook.Exec(baseConfig.Hook)
|
||||
hook.Exec(ctx, baseConfig.Hook)
|
||||
}
|
||||
|
||||
return hash, nil
|
||||
@ -147,13 +222,13 @@ func manageRename(watcher *fsnotify.Watcher, event fsnotify.Event, acmeFile stri
|
||||
}
|
||||
|
||||
func calculateHash(acmeFile string) ([]byte, error) {
|
||||
file, err := os.Open(acmeFile)
|
||||
file, err := os.Open(filepath.Clean(acmeFile))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
h := md5.New()
|
||||
h := sha256.New()
|
||||
_, err = io.Copy(h, file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
60
dumper/file/file_test.go
Normal file
60
dumper/file/file_test.go
Normal file
@ -0,0 +1,60 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDump(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
acmeFile string
|
||||
version string
|
||||
}{
|
||||
{
|
||||
desc: "should skip EOF error",
|
||||
acmeFile: "./fixtures/acme-empty.json",
|
||||
},
|
||||
{
|
||||
desc: "should dump traefik v1 file content",
|
||||
acmeFile: "./fixtures/acme-v1.json",
|
||||
},
|
||||
{
|
||||
desc: "should dump traefik v2 file content",
|
||||
acmeFile: "./fixtures/acme-v2.json",
|
||||
version: "v2",
|
||||
},
|
||||
{
|
||||
desc: "should dump traefik v3 file content",
|
||||
acmeFile: "./fixtures/acme-v3.json",
|
||||
version: "v3",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
dir := t.TempDir()
|
||||
|
||||
cfg := &dumper.BaseConfig{
|
||||
DumpPath: dir,
|
||||
CrtInfo: dumper.FileInfo{
|
||||
Name: "certificate",
|
||||
Ext: ".crt",
|
||||
},
|
||||
KeyInfo: dumper.FileInfo{
|
||||
Name: "privatekey",
|
||||
Ext: ".key",
|
||||
},
|
||||
Clean: true,
|
||||
Version: test.version,
|
||||
}
|
||||
|
||||
err := Dump(t.Context(), test.acmeFile, cfg)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
0
dumper/file/fixtures/acme-empty.json
Normal file
0
dumper/file/fixtures/acme-empty.json
Normal file
12
dumper/file/fixtures/acme-v1.json
Normal file
12
dumper/file/fixtures/acme-v1.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"Certificates": [
|
||||
{
|
||||
"domain": {
|
||||
"main": "test.example.com"
|
||||
},
|
||||
"certificate": "Q2VydGlmaWNhdGU=",
|
||||
"key": "Q2VydGlmaWNhdGUgS2V5",
|
||||
"Store": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
36
dumper/file/fixtures/acme-v2.json
Normal file
36
dumper/file/fixtures/acme-v2.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"default": {
|
||||
"Account": {
|
||||
"Email": "test@email.com",
|
||||
"Registration": {
|
||||
"body": {
|
||||
"status": "valid",
|
||||
"contact": [
|
||||
"mailto:test@email.com"
|
||||
]
|
||||
},
|
||||
"uri": "https://acme-v02.api.letsencrypt.org/acme/acct/12345678"
|
||||
},
|
||||
"PrivateKey": "Q2VydGlmaWNhdGUgS2V5",
|
||||
"KeyType": "4096"
|
||||
},
|
||||
"Certificates": [
|
||||
{
|
||||
"domain": {
|
||||
"main": "my.domain.com"
|
||||
},
|
||||
"certificate": "Q2VydGlmaWNhdGU=",
|
||||
"key": "Q2VydGlmaWNhdGUgS2V5",
|
||||
"Store": "default"
|
||||
},
|
||||
{
|
||||
"domain": {
|
||||
"main": "my.domain2.com"
|
||||
},
|
||||
"certificate": "Q2VydGlmaWNhdGU=",
|
||||
"key": "Q2VydGlmaWNhdGUgS2V5",
|
||||
"Store": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
36
dumper/file/fixtures/acme-v3.json
Normal file
36
dumper/file/fixtures/acme-v3.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"default": {
|
||||
"Account": {
|
||||
"Email": "test@email.com",
|
||||
"Registration": {
|
||||
"body": {
|
||||
"status": "valid",
|
||||
"contact": [
|
||||
"mailto:test@email.com"
|
||||
]
|
||||
},
|
||||
"uri": "https://acme-v02.api.letsencrypt.org/acme/acct/12345678"
|
||||
},
|
||||
"PrivateKey": "Q2VydGlmaWNhdGUgS2V5",
|
||||
"KeyType": "4096"
|
||||
},
|
||||
"Certificates": [
|
||||
{
|
||||
"domain": {
|
||||
"main": "my.domain.com"
|
||||
},
|
||||
"certificate": "Q2VydGlmaWNhdGU=",
|
||||
"key": "Q2VydGlmaWNhdGUgS2V5",
|
||||
"Store": "default"
|
||||
},
|
||||
{
|
||||
"domain": {
|
||||
"main": "my.domain2.com"
|
||||
},
|
||||
"certificate": "Q2VydGlmaWNhdGU=",
|
||||
"key": "Q2VydGlmaWNhdGUgS2V5",
|
||||
"Store": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,14 @@
|
||||
package kv
|
||||
|
||||
import "github.com/abronan/valkeyrie/store"
|
||||
import (
|
||||
"github.com/kvtools/valkeyrie"
|
||||
)
|
||||
|
||||
// Config KV configuration.
|
||||
type Config struct {
|
||||
Backend store.Backend
|
||||
StoreName string
|
||||
Prefix string
|
||||
Suffix string
|
||||
Endpoints []string
|
||||
Options *store.Config
|
||||
Options valkeyrie.Config
|
||||
}
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
package kv
|
||||
|
||||
import (
|
||||
"github.com/go-acme/lego/certcrypto"
|
||||
"github.com/go-acme/lego/registration"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv1"
|
||||
)
|
||||
|
||||
// CertificateV1 is used to store certificate info
|
||||
type CertificateV1 struct {
|
||||
// CertificateOld is used to store certificate info.
|
||||
type CertificateOld struct {
|
||||
Domain string
|
||||
CertURL string
|
||||
CertStableURL string
|
||||
@ -15,8 +15,8 @@ type CertificateV1 struct {
|
||||
Certificate []byte
|
||||
}
|
||||
|
||||
// AccountV1 is used to store lets encrypt registration info
|
||||
type AccountV1 struct {
|
||||
// AccountOld is used to store lets encrypt registration info.
|
||||
type AccountOld struct {
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
PrivateKey []byte
|
||||
@ -26,40 +26,43 @@ type AccountV1 struct {
|
||||
HTTPChallenge map[string]map[string][]byte
|
||||
}
|
||||
|
||||
// DomainsCertificates stores a certificate for multiple domains
|
||||
// DomainsCertificates stores a certificate for multiple domains.
|
||||
type DomainsCertificates struct {
|
||||
Certs []*DomainsCertificate
|
||||
}
|
||||
|
||||
// ChallengeCert stores a challenge certificate
|
||||
// ChallengeCert stores a challenge certificate.
|
||||
type ChallengeCert struct {
|
||||
Certificate []byte
|
||||
PrivateKey []byte
|
||||
}
|
||||
|
||||
// DomainsCertificate contains a certificate for multiple domains
|
||||
// DomainsCertificate contains a certificate for multiple domains.
|
||||
type DomainsCertificate struct {
|
||||
Domains dumper.Domain
|
||||
Certificate *CertificateV1
|
||||
Domains traefikv1.Domain
|
||||
Certificate *CertificateOld
|
||||
}
|
||||
|
||||
// convertAccountV1ToV2 converts account information from version 1 to 2
|
||||
func convertAccountV1ToV2(account *AccountV1) *dumper.StoredData {
|
||||
storedData := &dumper.StoredData{}
|
||||
storedData.Account = &dumper.Account{
|
||||
PrivateKey: account.PrivateKey,
|
||||
Registration: account.Registration,
|
||||
Email: account.Email,
|
||||
KeyType: account.KeyType,
|
||||
// convertOldAccount converts account information from old account format.
|
||||
func convertOldAccount(account *AccountOld) *traefikv1.StoredData {
|
||||
storedData := &traefikv1.StoredData{
|
||||
Account: &traefikv1.Account{
|
||||
PrivateKey: account.PrivateKey,
|
||||
Registration: account.Registration,
|
||||
Email: account.Email,
|
||||
KeyType: account.KeyType,
|
||||
},
|
||||
}
|
||||
var certs []*dumper.Certificate
|
||||
|
||||
var certs []*traefikv1.Certificate
|
||||
for _, oldCert := range account.DomainsCertificate.Certs {
|
||||
certs = append(certs, &dumper.Certificate{
|
||||
certs = append(certs, &traefikv1.Certificate{
|
||||
Certificate: oldCert.Certificate.Certificate,
|
||||
Domain: oldCert.Domains,
|
||||
Key: oldCert.Certificate.PrivateKey,
|
||||
})
|
||||
}
|
||||
storedData.Certificates = certs
|
||||
|
||||
return storedData
|
||||
}
|
||||
|
||||
@ -3,46 +3,48 @@ package kv
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/abronan/valkeyrie"
|
||||
"github.com/abronan/valkeyrie/store"
|
||||
"github.com/kvtools/valkeyrie"
|
||||
"github.com/kvtools/valkeyrie/store"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
v1 "github.com/ldez/traefik-certs-dumper/v2/dumper/v1"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/hook"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv1"
|
||||
)
|
||||
|
||||
const storeKeySuffix = "/acme/account/object"
|
||||
// DefaultStoreKeySuffix is the default suffix/storage.
|
||||
const DefaultStoreKeySuffix = "/acme/account/object"
|
||||
|
||||
// Dump Dumps KV content to certificates.
|
||||
func Dump(config *Config, baseConfig *dumper.BaseConfig) error {
|
||||
kvStore, err := valkeyrie.NewStore(config.Backend, config.Endpoints, config.Options)
|
||||
func Dump(ctx context.Context, config *Config, baseConfig *dumper.BaseConfig) error {
|
||||
kvStore, err := valkeyrie.NewStore(ctx, config.StoreName, config.Endpoints, config.Options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create client of the store: %v", err)
|
||||
return fmt.Errorf("unable to create client of the store: %w", err)
|
||||
}
|
||||
|
||||
storeKey := config.Prefix + storeKeySuffix
|
||||
storeKey := config.Prefix + config.Suffix
|
||||
|
||||
if baseConfig.Watch {
|
||||
return watch(kvStore, storeKey, baseConfig)
|
||||
return watch(ctx, kvStore, storeKey, baseConfig)
|
||||
}
|
||||
|
||||
pair, err := kvStore.Get(storeKey, nil)
|
||||
pair, err := kvStore.Get(ctx, storeKey, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve %s value: %v", storeKey, err)
|
||||
return fmt.Errorf("unable to retrieve %s value: %w", storeKey, err)
|
||||
}
|
||||
|
||||
return dumpPair(pair, baseConfig)
|
||||
}
|
||||
|
||||
func watch(kvStore store.Store, storeKey string, baseConfig *dumper.BaseConfig) error {
|
||||
stopCh := make(<-chan struct{})
|
||||
|
||||
pairs, err := kvStore.Watch(storeKey, stopCh, nil)
|
||||
func watch(ctx context.Context, kvStore store.Store, storeKey string, baseConfig *dumper.BaseConfig) error {
|
||||
pairs, err := kvStore.Watch(ctx, storeKey, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -62,7 +64,7 @@ func watch(kvStore store.Store, storeKey string, baseConfig *dumper.BaseConfig)
|
||||
log.Println("Dumped new certificate data.")
|
||||
}
|
||||
|
||||
hook.Exec(baseConfig.Hook)
|
||||
hook.Exec(ctx, baseConfig.Hook)
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,26 +74,27 @@ func dumpPair(pair *store.KVPair, baseConfig *dumper.BaseConfig) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return dumper.Dump(data, baseConfig)
|
||||
return v1.Dump(data, baseConfig)
|
||||
}
|
||||
|
||||
func getStoredDataFromGzip(pair *store.KVPair) (*dumper.StoredData, error) {
|
||||
func getStoredDataFromGzip(pair *store.KVPair) (*traefikv1.StoredData, error) {
|
||||
reader, err := gzip.NewReader(bytes.NewBuffer(pair.Value))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to create GZip reader: %v", err)
|
||||
return nil, fmt.Errorf("fail to create GZip reader: %w", err)
|
||||
}
|
||||
|
||||
acmeData, err := ioutil.ReadAll(reader)
|
||||
acmeData, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read the pair content: %v", err)
|
||||
return nil, fmt.Errorf("unable to read the pair content: %w", err)
|
||||
}
|
||||
|
||||
account := &AccountV1{}
|
||||
account := &AccountOld{}
|
||||
//nolint:musttag // old format
|
||||
if err := json.Unmarshal(acmeData, &account); err != nil {
|
||||
return nil, fmt.Errorf("unable marshal AccountV1: %v", err)
|
||||
return nil, fmt.Errorf("unable marshal AccountOld: %w", err)
|
||||
}
|
||||
|
||||
return convertAccountV1ToV2(account), nil
|
||||
return convertOldAccount(account), nil
|
||||
}
|
||||
|
||||
func isDebug() bool {
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
package dumper
|
||||
package v1
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-acme/lego/certcrypto"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv1"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -14,77 +16,70 @@ const (
|
||||
keysSubDir = "private"
|
||||
)
|
||||
|
||||
// FileInfo File information.
|
||||
type FileInfo struct {
|
||||
Name string
|
||||
Ext string
|
||||
}
|
||||
|
||||
// Dump Dumps data to certificates.
|
||||
func Dump(data *StoredData, baseConfig *BaseConfig) error {
|
||||
func Dump(data *traefikv1.StoredData, baseConfig *dumper.BaseConfig) error {
|
||||
if baseConfig.Clean {
|
||||
err := cleanDir(baseConfig.DumpPath)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("folder cleaning failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if !baseConfig.DomainSubDir {
|
||||
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, certsSubDir), 0755); err != nil {
|
||||
return err
|
||||
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, certsSubDir), 0o755); err != nil {
|
||||
return fmt.Errorf("certs folder creation failure: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, keysSubDir), 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privateKeyPem := extractPEMPrivateKey(data.Account)
|
||||
err := ioutil.WriteFile(filepath.Join(baseConfig.DumpPath, keysSubDir, "letsencrypt"+baseConfig.KeyInfo.Ext), privateKeyPem, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, keysSubDir), 0o755); err != nil {
|
||||
return fmt.Errorf("keys folder creation failure: %w", err)
|
||||
}
|
||||
|
||||
for _, cert := range data.Certificates {
|
||||
err := writeCert(baseConfig.DumpPath, cert, baseConfig.CrtInfo, baseConfig.DomainSubDir)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to write certificates: %w", err)
|
||||
}
|
||||
|
||||
err = writeKey(baseConfig.DumpPath, cert, baseConfig.KeyInfo, baseConfig.DomainSubDir)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to write certificate keys: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
if data.Account == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
privateKeyPem := extractPEMPrivateKey(data.Account)
|
||||
return os.WriteFile(filepath.Join(baseConfig.DumpPath, keysSubDir, "letsencrypt"+baseConfig.KeyInfo.Ext), privateKeyPem, 0o600)
|
||||
}
|
||||
|
||||
func writeCert(dumpPath string, cert *Certificate, info FileInfo, domainSubDir bool) error {
|
||||
func writeCert(dumpPath string, cert *traefikv1.Certificate, info dumper.FileInfo, domainSubDir bool) error {
|
||||
certPath := filepath.Join(dumpPath, certsSubDir, safeName(cert.Domain.Main+info.Ext))
|
||||
if domainSubDir {
|
||||
certPath = filepath.Join(dumpPath, safeName(cert.Domain.Main), info.Name+info.Ext)
|
||||
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0755); err != nil {
|
||||
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(certPath, cert.Certificate, 0666)
|
||||
return os.WriteFile(certPath, cert.Certificate, 0o666)
|
||||
}
|
||||
|
||||
func writeKey(dumpPath string, cert *Certificate, info FileInfo, domainSubDir bool) error {
|
||||
func writeKey(dumpPath string, cert *traefikv1.Certificate, info dumper.FileInfo, domainSubDir bool) error {
|
||||
keyPath := filepath.Join(dumpPath, keysSubDir, safeName(cert.Domain.Main+info.Ext))
|
||||
if domainSubDir {
|
||||
keyPath = filepath.Join(dumpPath, safeName(cert.Domain.Main), info.Name+info.Ext)
|
||||
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0755); err != nil {
|
||||
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(keyPath, cert.Key, 0666)
|
||||
return os.WriteFile(keyPath, cert.Key, 0o600)
|
||||
}
|
||||
|
||||
func extractPEMPrivateKey(account *Account) []byte {
|
||||
func extractPEMPrivateKey(account *traefikv1.Account) []byte {
|
||||
var block *pem.Block
|
||||
switch account.KeyType {
|
||||
case certcrypto.RSA2048, certcrypto.RSA4096, certcrypto.RSA8192:
|
||||
@ -98,7 +93,7 @@ func extractPEMPrivateKey(account *Account) []byte {
|
||||
Bytes: account.PrivateKey,
|
||||
}
|
||||
default:
|
||||
panic("unsupported key type")
|
||||
panic(fmt.Sprintf("unsupported key type: '%v'", account.KeyType))
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(block)
|
||||
@ -114,7 +109,7 @@ func cleanDir(dumpPath string) error {
|
||||
return errExists
|
||||
}
|
||||
|
||||
dir, err := ioutil.ReadDir(dumpPath)
|
||||
dir, err := os.ReadDir(dumpPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package dumper
|
||||
package v1
|
||||
|
||||
func safeName(filename string) string {
|
||||
return filename
|
||||
@ -1,6 +1,6 @@
|
||||
// +build windows
|
||||
|
||||
package dumper
|
||||
package v1
|
||||
|
||||
import "strings"
|
||||
|
||||
132
dumper/v2/dumper.go
Normal file
132
dumper/v2/dumper.go
Normal file
@ -0,0 +1,132 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv2"
|
||||
)
|
||||
|
||||
const (
|
||||
certsSubDir = "certs"
|
||||
keysSubDir = "private"
|
||||
)
|
||||
|
||||
// Dump Dumps data to certificates.
|
||||
func Dump(data map[string]*traefikv2.StoredData, baseConfig *dumper.BaseConfig) error {
|
||||
if baseConfig.Clean {
|
||||
err := cleanDir(baseConfig.DumpPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("folder cleaning failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if !baseConfig.DomainSubDir {
|
||||
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, certsSubDir), 0o755); err != nil {
|
||||
return fmt.Errorf("certs folder creation failure: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, keysSubDir), 0o755); err != nil {
|
||||
return fmt.Errorf("keys folder creation failure: %w", err)
|
||||
}
|
||||
|
||||
for _, store := range data {
|
||||
for _, cert := range store.Certificates {
|
||||
err := writeCert(baseConfig.DumpPath, cert.Certificate, baseConfig.CrtInfo, baseConfig.DomainSubDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write certificates: %w", err)
|
||||
}
|
||||
|
||||
err = writeKey(baseConfig.DumpPath, cert.Certificate, baseConfig.KeyInfo, baseConfig.DomainSubDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write certificate keys: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if store.Account == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
privateKeyPem := extractPEMPrivateKey(store.Account)
|
||||
|
||||
err := os.WriteFile(filepath.Join(baseConfig.DumpPath, keysSubDir, "letsencrypt"+baseConfig.KeyInfo.Ext), privateKeyPem, 0o600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write private key: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeCert(dumpPath string, cert traefikv2.Certificate, info dumper.FileInfo, domainSubDir bool) error {
|
||||
certPath := filepath.Join(dumpPath, certsSubDir, safeName(cert.Domain.Main+info.Ext))
|
||||
if domainSubDir {
|
||||
certPath = filepath.Join(dumpPath, safeName(cert.Domain.Main), info.Name+info.Ext)
|
||||
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return os.WriteFile(certPath, cert.Certificate, 0o666)
|
||||
}
|
||||
|
||||
func writeKey(dumpPath string, cert traefikv2.Certificate, info dumper.FileInfo, domainSubDir bool) error {
|
||||
keyPath := filepath.Join(dumpPath, keysSubDir, safeName(cert.Domain.Main+info.Ext))
|
||||
if domainSubDir {
|
||||
keyPath = filepath.Join(dumpPath, safeName(cert.Domain.Main), info.Name+info.Ext)
|
||||
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return os.WriteFile(keyPath, cert.Key, 0o600)
|
||||
}
|
||||
|
||||
func extractPEMPrivateKey(account *traefikv2.Account) []byte {
|
||||
var block *pem.Block
|
||||
switch account.KeyType {
|
||||
case certcrypto.RSA2048, certcrypto.RSA4096, certcrypto.RSA8192:
|
||||
block = &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: account.PrivateKey,
|
||||
}
|
||||
case certcrypto.EC256, certcrypto.EC384:
|
||||
block = &pem.Block{
|
||||
Type: "EC PRIVATE KEY",
|
||||
Bytes: account.PrivateKey,
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported key type: '%v'", account.KeyType))
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(block)
|
||||
}
|
||||
|
||||
func cleanDir(dumpPath string) error {
|
||||
_, errExists := os.Stat(dumpPath)
|
||||
if os.IsNotExist(errExists) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if errExists != nil {
|
||||
return errExists
|
||||
}
|
||||
|
||||
dir, err := os.ReadDir(dumpPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, f := range dir {
|
||||
if err := os.RemoveAll(filepath.Join(dumpPath, f.Name())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
8
dumper/v2/filename.go
Normal file
8
dumper/v2/filename.go
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package v2
|
||||
|
||||
func safeName(filename string) string {
|
||||
return filename
|
||||
}
|
||||
9
dumper/v2/filename_windows.go
Normal file
9
dumper/v2/filename_windows.go
Normal file
@ -0,0 +1,9 @@
|
||||
// +build windows
|
||||
|
||||
package v2
|
||||
|
||||
import "strings"
|
||||
|
||||
func safeName(filename string) string {
|
||||
return strings.ReplaceAll(filename, "*", "_")
|
||||
}
|
||||
132
dumper/v3/dumper.go
Normal file
132
dumper/v3/dumper.go
Normal file
@ -0,0 +1,132 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/dumper"
|
||||
"github.com/ldez/traefik-certs-dumper/v2/internal/traefikv3"
|
||||
)
|
||||
|
||||
const (
|
||||
certsSubDir = "certs"
|
||||
keysSubDir = "private"
|
||||
)
|
||||
|
||||
// Dump Dumps data to certificates.
|
||||
func Dump(data map[string]*traefikv3.StoredData, baseConfig *dumper.BaseConfig) error {
|
||||
if baseConfig.Clean {
|
||||
err := cleanDir(baseConfig.DumpPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("folder cleaning failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if !baseConfig.DomainSubDir {
|
||||
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, certsSubDir), 0o755); err != nil {
|
||||
return fmt.Errorf("certs folder creation failure: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Join(baseConfig.DumpPath, keysSubDir), 0o755); err != nil {
|
||||
return fmt.Errorf("keys folder creation failure: %w", err)
|
||||
}
|
||||
|
||||
for _, store := range data {
|
||||
for _, cert := range store.Certificates {
|
||||
err := writeCert(baseConfig.DumpPath, cert.Certificate, baseConfig.CrtInfo, baseConfig.DomainSubDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write certificates: %w", err)
|
||||
}
|
||||
|
||||
err = writeKey(baseConfig.DumpPath, cert.Certificate, baseConfig.KeyInfo, baseConfig.DomainSubDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write certificate keys: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if store.Account == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
privateKeyPem := extractPEMPrivateKey(store.Account)
|
||||
|
||||
err := os.WriteFile(filepath.Join(baseConfig.DumpPath, keysSubDir, "letsencrypt"+baseConfig.KeyInfo.Ext), privateKeyPem, 0o600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write private key: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeCert(dumpPath string, cert traefikv3.Certificate, info dumper.FileInfo, domainSubDir bool) error {
|
||||
certPath := filepath.Join(dumpPath, certsSubDir, safeName(cert.Domain.Main+info.Ext))
|
||||
if domainSubDir {
|
||||
certPath = filepath.Join(dumpPath, safeName(cert.Domain.Main), info.Name+info.Ext)
|
||||
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return os.WriteFile(certPath, cert.Certificate, 0o666)
|
||||
}
|
||||
|
||||
func writeKey(dumpPath string, cert traefikv3.Certificate, info dumper.FileInfo, domainSubDir bool) error {
|
||||
keyPath := filepath.Join(dumpPath, keysSubDir, safeName(cert.Domain.Main+info.Ext))
|
||||
if domainSubDir {
|
||||
keyPath = filepath.Join(dumpPath, safeName(cert.Domain.Main), info.Name+info.Ext)
|
||||
if err := os.MkdirAll(filepath.Join(dumpPath, safeName(cert.Domain.Main)), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return os.WriteFile(keyPath, cert.Key, 0o600)
|
||||
}
|
||||
|
||||
func extractPEMPrivateKey(account *traefikv3.Account) []byte {
|
||||
var block *pem.Block
|
||||
switch account.KeyType {
|
||||
case certcrypto.RSA2048, certcrypto.RSA4096, certcrypto.RSA8192:
|
||||
block = &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: account.PrivateKey,
|
||||
}
|
||||
case certcrypto.EC256, certcrypto.EC384:
|
||||
block = &pem.Block{
|
||||
Type: "EC PRIVATE KEY",
|
||||
Bytes: account.PrivateKey,
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported key type: '%v'", account.KeyType))
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(block)
|
||||
}
|
||||
|
||||
func cleanDir(dumpPath string) error {
|
||||
_, errExists := os.Stat(dumpPath)
|
||||
if os.IsNotExist(errExists) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if errExists != nil {
|
||||
return errExists
|
||||
}
|
||||
|
||||
dir, err := os.ReadDir(dumpPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, f := range dir {
|
||||
if err := os.RemoveAll(filepath.Join(dumpPath, f.Name())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
8
dumper/v3/filename.go
Normal file
8
dumper/v3/filename.go
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package v3
|
||||
|
||||
func safeName(filename string) string {
|
||||
return filename
|
||||
}
|
||||
10
dumper/v3/filename_windows.go
Normal file
10
dumper/v3/filename_windows.go
Normal file
@ -0,0 +1,10 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package v3
|
||||
|
||||
import "strings"
|
||||
|
||||
func safeName(filename string) string {
|
||||
return strings.ReplaceAll(filename, "*", "_")
|
||||
}
|
||||
125
go.mod
125
go.mod
@ -1,51 +1,90 @@
|
||||
module github.com/ldez/traefik-certs-dumper/v2
|
||||
|
||||
go 1.12
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/abronan/valkeyrie v0.0.0-20190419181538-ccf7df650fe4
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible // indirect
|
||||
github.com/coreos/bbolt v1.3.2 // indirect
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
||||
github.com/cpuguy83/go-md2man v1.0.10 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/go-acme/lego v2.5.0+incompatible
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
github.com/gorilla/websocket v1.4.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5 // indirect
|
||||
github.com/hashicorp/go-msgpack v0.5.4 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.1 // indirect
|
||||
github.com/hashicorp/memberlist v0.1.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.1.0 // indirect
|
||||
github.com/charmbracelet/lipgloss v1.0.0
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/go-acme/lego/v4 v4.25.2
|
||||
github.com/kvtools/boltdb v1.0.2
|
||||
github.com/kvtools/consul v1.0.2
|
||||
github.com/kvtools/etcdv2 v1.0.2
|
||||
github.com/kvtools/etcdv3 v1.0.2
|
||||
github.com/kvtools/valkeyrie v1.0.0
|
||||
github.com/kvtools/zookeeper v1.0.2
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.1 // indirect
|
||||
github.com/pascaldekloe/goe v0.1.0 // indirect
|
||||
github.com/pkg/errors v0.8.1 // indirect
|
||||
github.com/prometheus/client_golang v0.9.2 // indirect
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 // indirect
|
||||
github.com/sirupsen/logrus v1.4.1 // indirect
|
||||
github.com/soheilhy/cmux v0.1.4 // indirect
|
||||
github.com/spf13/cobra v0.0.3
|
||||
github.com/spf13/viper v1.3.2
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
go.uber.org/atomic v1.3.2 // indirect
|
||||
go.uber.org/multierr v1.1.0 // indirect
|
||||
go.uber.org/zap v1.9.1 // indirect
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.3.1 // indirect
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/ugorji/go => github.com/ugorji/go v1.1.2-0.20181022190402-e5e69e061d4f
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8 => github.com/ugorji/go/codec v1.1.2-0.20181022190402-e5e69e061d4f
|
||||
require (
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.4.2 // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.1 // indirect
|
||||
github.com/go-zookeeper/zk v1.0.4 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/hashicorp/consul/api v1.28.2 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-metrics v0.5.4 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
|
||||
github.com/hashicorp/serf v0.10.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.14 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect
|
||||
go.etcd.io/etcd/client/v2 v2.305.12 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.14 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
exclude github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible
|
||||
|
||||
504
go.sum
504
go.sum
@ -1,177 +1,393 @@
|
||||
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/abronan/valkeyrie v0.0.0-20190419181538-ccf7df650fe4 h1:DrTAEU8rVfy2tRZObh8Hdjs819By7XfFhoOKh8xqX7Y=
|
||||
github.com/abronan/valkeyrie v0.0.0-20190419181538-ccf7df650fe4/go.mod h1:NOvlKBjVll/vPwdjPHGLNhKk7VrnLzLGU/VGOVPLiog=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/aws/aws-sdk-go v1.16.23/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
|
||||
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible h1:tKJnvO2kl0zmb/jA5UKAt4VoEVw1qxKWjE/Bpp46npY=
|
||||
github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.11+incompatible h1:0gCnqKsq7XxMi69JsnbmMc1o+RJH3XH64sV9aiTTYko=
|
||||
github.com/coreos/etcd v3.3.11+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.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-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg=
|
||||
github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo=
|
||||
github.com/charmbracelet/x/ansi v0.4.2 h1:0JM6Aj/g/KC154/gOP4vfxun0ff6itogDYk41kof+qk=
|
||||
github.com/charmbracelet/x/ansi v0.4.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
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/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-acme/lego v2.5.0+incompatible h1:5fNN9yRQfv8ymH3DSsxla+4aYeQt2IgfZqHKVnK8f0s=
|
||||
github.com/go-acme/lego v2.5.0+incompatible/go.mod h1:yzMNe9CasVUhkquNvti5nAtPmG94USbYxYrZfTkIn0M=
|
||||
github.com/gogo/protobuf v1.2.0 h1:xU6/SpYbvkNYiptHJYEDRseDLvYE7wSqhYYNy0QSUzI=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-acme/lego/v4 v4.25.2 h1:+D1Q+VnZrD+WJdlkgUEGHFFTcDrwGlE7q24IFtMmHDI=
|
||||
github.com/go-acme/lego/v4 v4.25.2/go.mod h1:OORYyVNZPaNdIdVYCGSBNRNZDIjhQbPuFxwGDgWj/yM=
|
||||
github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI=
|
||||
github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-zookeeper/zk v1.0.4 h1:DPzxraQx7OrPyXq2phlGlNSIyWEsAox0RJmjTseMV6I=
|
||||
github.com/go-zookeeper/zk v1.0.4/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/consul v1.4.0 h1:PQTW4xCuAExEiSbhrsFsikzbW5gVBoi74BjUvYFyKHw=
|
||||
github.com/hashicorp/consul v1.4.0/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/consul/api v1.28.2 h1:mXfkRHrpHN4YY3RqL09nXU1eHKLNiuAN4kHvDQ16k/8=
|
||||
github.com/hashicorp/consul/api v1.28.2/go.mod h1:KyzqzgMEya+IZPcD65YFoOVAgPpbfERu4I/tzG6/ueE=
|
||||
github.com/hashicorp/consul/sdk v0.16.0 h1:SE9m0W6DEfgIVCJX7xU+iv/hUl4m/nxqMTnCdMxDpJ8=
|
||||
github.com/hashicorp/consul/sdk v0.16.0/go.mod h1:7pxqqhqoaPqnBnzXD1StKed62LqJeClzVsUEy85Zr0A=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-msgpack v0.5.4/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY=
|
||||
github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI=
|
||||
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
||||
github.com/hashicorp/go-msgpack/v2 v2.1.2 h1:4Ee8FTp834e+ewB71RDrQ0VKpyFdrKOjvYtnQ/ltVj0=
|
||||
github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhzywQsSRV5033mMu4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-sockaddr v1.0.5 h1:dvk7TIXCZpmfOlM+9mlcrWmWjw/wlKT+VDq2wMvfPJU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.5/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.1 h1:mYs6SMzu72+90OcPa5wr3nfznA4Dw9UyR791ZFNOIf4=
|
||||
github.com/hashicorp/serf v0.8.1/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
||||
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM=
|
||||
github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
|
||||
github.com/hashicorp/memberlist v0.5.2 h1:rJoNPWZ0juJBgqn48gjy59K5H4rNgvUoM1kUD7bXiuI=
|
||||
github.com/hashicorp/memberlist v0.5.2/go.mod h1:Ri9p/tRShbjYnpNf4FFPXG7wxEGY4Nrcn6E7jrVa//4=
|
||||
github.com/hashicorp/serf v0.10.2 h1:m5IORhuNSjaxeljg5DeQVDlQyVkhRIjJDimbkCa8aAc=
|
||||
github.com/hashicorp/serf v0.10.2/go.mod h1:T1CmSGfSeGfnfNy/w0odXQUR1rfECGd2Qdsp84DjOiY=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
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.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kvtools/boltdb v1.0.2 h1:GZaUZ3AOw33mdK8r7p5bRqfhe504/BcwSVj6efQzLNs=
|
||||
github.com/kvtools/boltdb v1.0.2/go.mod h1:ERqvkjiHCFTLy7WcHitvgpDH0y14pT3r+ZwkfqJ6tQM=
|
||||
github.com/kvtools/consul v1.0.2 h1:ltPgs4Ld09Xaa7zrOJ/TewBYKAsr11/LRFpErdkb8AA=
|
||||
github.com/kvtools/consul v1.0.2/go.mod h1:bFnzfGJ5ZIRRXCBGBmwhJlLdEWOlrjOcS1WjyAQzaJA=
|
||||
github.com/kvtools/etcdv2 v1.0.2 h1:Bigfbm2f4uZveD0Av/5Vxs7MdqnWDVnAFpSF4QEL1c8=
|
||||
github.com/kvtools/etcdv2 v1.0.2/go.mod h1:Ye5IwvG5KxUdcP14Yag6Pc3E9bzxy1j7zlvLK8eI/bU=
|
||||
github.com/kvtools/etcdv3 v1.0.2 h1:EB0mAtzqe1folE7m7Q6wnCXcGwaOmrYmsVmF3hNsTKI=
|
||||
github.com/kvtools/etcdv3 v1.0.2/go.mod h1:Xr6DbwqjuCEcXAIWmXxw0DX+N5BhuvablXgN90XeqMM=
|
||||
github.com/kvtools/valkeyrie v1.0.0 h1:LAITop2wPoYCMitR24GZZsW0b57hmI+ePD18VRTtOf0=
|
||||
github.com/kvtools/valkeyrie v1.0.0/go.mod h1:bDi/OdhJCSbGPMsCgUQl881yuEweKCSItAtTBI+ZjpU=
|
||||
github.com/kvtools/zookeeper v1.0.2 h1:uK0CzQa+mtKGxDDH+DeqXo2HC1Kx4hWXZ7pX/zS4aTo=
|
||||
github.com/kvtools/zookeeper v1.0.2/go.mod h1:6TfxUwJ7IuBk5srgnoe528W0ftanNECHgOiShx/t0Aw=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/miekg/dns v1.1.67 h1:kg0EHj0G4bfT5/oOys6HhZw4vmMlnoZ+gDu8tJ/AlI0=
|
||||
github.com/miekg/dns v1.1.67/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec h1:6ncX5ko6B9LntYM0YBRXkiSaZMmLYeZ/NWcmeB43mMY=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.2-0.20181022190402-e5e69e061d4f h1:E6ip3gLExd3v9o1iiZMMxOaC/XiWk3mPbDTOPLL0eWw=
|
||||
github.com/ugorji/go v1.1.2-0.20181022190402-e5e69e061d4f/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
||||
github.com/ugorji/go/codec v1.1.2-0.20181022190402-e5e69e061d4f h1:CZG9W9a8rpiPXPmkGcyXoD9sLF+JfLh/x+BpYHGhK+o=
|
||||
github.com/ugorji/go/codec v1.1.2-0.20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go.etcd.io/bbolt v1.3.1-etcd.8 h1:6J7QAKqfFBGnU80KRnuQxfjjeE5xAGE/qB810I3FQHQ=
|
||||
go.etcd.io/bbolt v1.3.1-etcd.8/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v3.3.11+incompatible h1:AVwRXu9VIzZcvVe1nSirTVkNv7WT3/hwdMRrDVFsf3A=
|
||||
go.etcd.io/etcd v3.3.11+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
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=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI=
|
||||
go.etcd.io/etcd/client/v2 v2.305.12 h1:0m4ovXYo1CHaA/Mp3X/Fak5sRNIWf01wk/X1/G3sGKI=
|
||||
go.etcd.io/etcd/client/v2 v2.305.12/go.mod h1:aQ/yhsxMu+Oht1FOupSr60oBvcS9cKXHrzBpDsPTf9E=
|
||||
go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg=
|
||||
go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
|
||||
golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1YthsFqr/5mxHduZW2A=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/redis.v5 v5.2.9/go.mod h1:6gtv0/+A4iM08kdRfocWYB3bLX2tebpNtfKlFT6H4mY=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
13
hook/hook.go
13
hook/hook.go
@ -4,36 +4,37 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Exec Execute a command on a go routine.
|
||||
func Exec(command string) {
|
||||
func Exec(ctx context.Context, command string) {
|
||||
if command == "" {
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
errH := execute(command)
|
||||
errH := execute(ctx, command)
|
||||
if errH != nil {
|
||||
panic(errH)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func execute(command string) error {
|
||||
ctxCmd, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
func execute(ctx context.Context, command string) error {
|
||||
ctxCmd, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
parts := strings.Fields(command)
|
||||
parts := strings.Fields(os.ExpandEnv(command))
|
||||
output, err := exec.CommandContext(ctxCmd, parts[0], parts[1:]...).CombinedOutput()
|
||||
if len(output) > 0 {
|
||||
fmt.Println(string(output))
|
||||
}
|
||||
|
||||
if ctxCmd.Err() == context.DeadlineExceeded {
|
||||
if errors.Is(ctxCmd.Err(), context.DeadlineExceeded) {
|
||||
return errors.New("hook timed out")
|
||||
}
|
||||
|
||||
|
||||
30
hook/hook_test.go
Normal file
30
hook/hook_test.go
Normal file
@ -0,0 +1,30 @@
|
||||
package hook
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_execute(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
command string
|
||||
}{
|
||||
{
|
||||
desc: "expand env vars",
|
||||
command: `echo "${GOPATH} ${GOARCH}"`,
|
||||
},
|
||||
{
|
||||
desc: "simple",
|
||||
command: `echo 'hello'`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
err := execute(t.Context(), test.command)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -3,16 +3,17 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"io/ioutil"
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/abronan/valkeyrie"
|
||||
"github.com/abronan/valkeyrie/store"
|
||||
"github.com/abronan/valkeyrie/store/boltdb"
|
||||
"github.com/abronan/valkeyrie/store/consul"
|
||||
etcdv3 "github.com/abronan/valkeyrie/store/etcd/v3"
|
||||
"github.com/abronan/valkeyrie/store/zookeeper"
|
||||
"github.com/kvtools/boltdb"
|
||||
"github.com/kvtools/consul"
|
||||
"github.com/kvtools/etcdv3"
|
||||
"github.com/kvtools/valkeyrie"
|
||||
"github.com/kvtools/zookeeper"
|
||||
)
|
||||
|
||||
const storeKey = "traefik/acme/account/object"
|
||||
@ -21,38 +22,43 @@ func main() {
|
||||
log.SetFlags(log.Lshortfile)
|
||||
|
||||
source := "./acme.json"
|
||||
err := loadData(source)
|
||||
err := loadData(context.Background(), source)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func loadData(source string) error {
|
||||
func loadData(ctx context.Context, source string) error {
|
||||
content, err := readFile(source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Consul
|
||||
err = putData(store.CONSUL, []string{"localhost:8500"}, content)
|
||||
|
||||
err = putData(ctx, consul.StoreName, []string{"localhost:8500"},
|
||||
&consul.Config{ConnectionTimeout: 3 * time.Second}, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// ETCD v3
|
||||
err = putData(store.ETCDV3, []string{"localhost:2379"}, content)
|
||||
err = putData(ctx, etcdv3.StoreName, []string{"localhost:2379"},
|
||||
&etcdv3.Config{ConnectionTimeout: 3 * time.Second}, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Zookeeper
|
||||
err = putData(store.ZK, []string{"localhost:2181"}, content)
|
||||
err = putData(ctx, zookeeper.StoreName, []string{"localhost:2181"},
|
||||
&zookeeper.Config{ConnectionTimeout: 3 * time.Second}, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// BoltDB
|
||||
err = putData(store.BOLTDB, []string{"/tmp/test-traefik-certs-dumper.db"}, content)
|
||||
err = putData(ctx, boltdb.StoreName, []string{"/tmp/test-traefik-certs-dumper.db"},
|
||||
&boltdb.Config{ConnectionTimeout: 3 * time.Second, Bucket: "traefik"}, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -60,29 +66,13 @@ func loadData(source string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func putData(backend store.Backend, addrs []string, content []byte) error {
|
||||
storeConfig := &store.Config{
|
||||
ConnectionTimeout: 3 * time.Second,
|
||||
Bucket: "traefik",
|
||||
}
|
||||
|
||||
switch backend {
|
||||
case store.CONSUL:
|
||||
consul.Register()
|
||||
case store.ETCDV3:
|
||||
etcdv3.Register()
|
||||
case store.ZK:
|
||||
zookeeper.Register()
|
||||
case store.BOLTDB:
|
||||
boltdb.Register()
|
||||
}
|
||||
|
||||
kvStore, err := valkeyrie.NewStore(backend, addrs, storeConfig)
|
||||
func putData(ctx context.Context, backend string, addrs []string, options valkeyrie.Config, content []byte) error {
|
||||
kvStore, err := valkeyrie.NewStore(ctx, backend, addrs, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := kvStore.Put(storeKey, content, nil); err != nil {
|
||||
if err := kvStore.Put(ctx, storeKey, content, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -91,7 +81,7 @@ func putData(backend store.Backend, addrs []string, content []byte) error {
|
||||
}
|
||||
|
||||
func readFile(source string) ([]byte, error) {
|
||||
content, err := ioutil.ReadFile(source)
|
||||
content, err := os.ReadFile(filepath.Clean(source))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -1,255 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/distribution/manifest/manifestlist"
|
||||
)
|
||||
|
||||
type buildOption struct {
|
||||
OS string
|
||||
GoARM string
|
||||
GoARCH string
|
||||
Variant string
|
||||
}
|
||||
|
||||
type actions struct {
|
||||
Builds [][]string
|
||||
Push [][]string
|
||||
ManifestAnnotate [][]string
|
||||
ManifestCreate []string
|
||||
ManifestPush []string
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.Lshortfile)
|
||||
|
||||
imageName := flag.String("image-name", "ldez/traefik-certs-dumper", "")
|
||||
version := flag.String("version", "", "")
|
||||
baseImageName := flag.String("base-image-name", "alpine:3.9", "")
|
||||
dryRun := flag.Bool("dry-run", true, "")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
require("image-name", imageName)
|
||||
require("version", version)
|
||||
require("base-image-name", baseImageName)
|
||||
|
||||
// FIXME
|
||||
// _, travisTag := os.LookupEnv("TRAVIS_TAG")
|
||||
// if !travisTag {
|
||||
// log.Println("Skipping deploy")
|
||||
// os.Exit(0)
|
||||
// }
|
||||
|
||||
targets := []string{"arm.v6", "arm.v7", "arm.v8", "amd64", "386"}
|
||||
|
||||
actions, err := buildActions(*imageName, *version, *baseImageName, targets)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = execute(actions, *dryRun)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func require(fieldName string, field *string) {
|
||||
if field == nil || *field == "" {
|
||||
log.Fatalf("%s is required", fieldName)
|
||||
}
|
||||
}
|
||||
|
||||
func buildActions(imageName, version, baseImageName string, targets []string) (actions, error) {
|
||||
manifest, err := getManifest(baseImageName)
|
||||
if err != nil {
|
||||
return actions{}, err
|
||||
}
|
||||
|
||||
buildOptions := map[string]buildOption{
|
||||
"arm.v5": {OS: "linux", GoARM: "5", GoARCH: "arm", Variant: "v5"},
|
||||
"arm.v6": {OS: "linux", GoARM: "6", GoARCH: "arm", Variant: "v6"},
|
||||
"arm.v7": {OS: "linux", GoARM: "7", GoARCH: "arm", Variant: "v7"},
|
||||
"arm.v8": {OS: "linux", GoARCH: "arm64", Variant: "v8"},
|
||||
"amd64": {OS: "linux", GoARCH: "amd64"},
|
||||
"386": {OS: "linux", GoARCH: "386"},
|
||||
}
|
||||
|
||||
actions := actions{}
|
||||
|
||||
for _, target := range targets {
|
||||
buildOption := buildOptions[target]
|
||||
|
||||
descriptor, err := findManifestDescriptor(buildOption, manifest.Manifests)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
dockerfile := fmt.Sprintf("%s-%s-%s.Dockerfile", buildOption.OS, buildOption.GoARCH, buildOption.GoARM)
|
||||
|
||||
actions.Builds = append(actions.Builds, []string{
|
||||
"build",
|
||||
"-t", fmt.Sprintf("%s:%s-%s", imageName, version, target),
|
||||
"-f", dockerfile,
|
||||
".",
|
||||
})
|
||||
|
||||
err = createDockerfile(dockerfile, buildOption, descriptor, baseImageName)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
actions.Push = append(actions.Push, []string{"push", fmt.Sprintf(`%s:%s-%s`, imageName, version, target)})
|
||||
|
||||
ma := []string{
|
||||
"manifest", "annotate",
|
||||
fmt.Sprintf(`"%s:%s"`, imageName, version),
|
||||
fmt.Sprintf(`"%s:%s-%s"`, imageName, version, target),
|
||||
fmt.Sprintf(`--os="%s"`, buildOption.OS),
|
||||
fmt.Sprintf(`--arch="%s"`, buildOption.GoARCH),
|
||||
}
|
||||
if buildOption.Variant != "" {
|
||||
ma = append(ma, fmt.Sprintf(`--variant="%s"`, buildOption.Variant))
|
||||
}
|
||||
actions.ManifestAnnotate = append(actions.ManifestAnnotate, ma)
|
||||
}
|
||||
|
||||
actions.ManifestCreate = []string{
|
||||
"manifest", "create", "--amend",
|
||||
fmt.Sprintf("%s:%s", imageName, version),
|
||||
}
|
||||
|
||||
for _, target := range targets {
|
||||
actions.ManifestCreate = append(actions.ManifestCreate, fmt.Sprintf(`"%s:%s-%s"`, imageName, version, target))
|
||||
}
|
||||
|
||||
actions.ManifestPush = []string{
|
||||
"manifest", "push", fmt.Sprintf("%s:%s", imageName, version),
|
||||
}
|
||||
|
||||
return actions, nil
|
||||
}
|
||||
|
||||
func createDockerfile(dockerfile string, buildOption buildOption, descriptor manifestlist.ManifestDescriptor, baseImageName string) error {
|
||||
base := template.New("tmpl.Dockerfile")
|
||||
parse, err := base.ParseFiles("./internal/tmpl.Dockerfile")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"GoOS": buildOption.OS,
|
||||
"GoARCH": buildOption.GoARCH,
|
||||
"GoARM": buildOption.GoARM,
|
||||
"RuntimeImage": fmt.Sprintf("%s@%s", baseImageName, descriptor.Digest),
|
||||
}
|
||||
|
||||
file, err := os.Create(dockerfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return parse.Execute(file, data)
|
||||
}
|
||||
|
||||
func getManifest(baseImageName string) (*manifestlist.ManifestList, error) {
|
||||
manifestPath := "./manifest.json"
|
||||
|
||||
if _, errExist := os.Stat(manifestPath); os.IsNotExist(errExist) {
|
||||
cmd := exec.Command("docker", "manifest", "inspect", baseImageName)
|
||||
cmd.Env = append(cmd.Env, "DOCKER_CLI_EXPERIMENTAL=enabled")
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(manifestPath, output, 777)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if errExist != nil {
|
||||
return nil, errExist
|
||||
}
|
||||
|
||||
bytes, err := ioutil.ReadFile(manifestPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
manifest := &manifestlist.ManifestList{}
|
||||
|
||||
err = json.Unmarshal(bytes, manifest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return manifest, nil
|
||||
}
|
||||
|
||||
func findManifestDescriptor(criterion buildOption, descriptors []manifestlist.ManifestDescriptor) (manifestlist.ManifestDescriptor, error) {
|
||||
for _, descriptor := range descriptors {
|
||||
if descriptor.Platform.OS == criterion.OS &&
|
||||
descriptor.Platform.Architecture == criterion.GoARCH &&
|
||||
descriptor.Platform.Variant == criterion.Variant {
|
||||
return descriptor, nil
|
||||
}
|
||||
}
|
||||
return manifestlist.ManifestDescriptor{}, fmt.Errorf("not supported: %v", criterion)
|
||||
}
|
||||
|
||||
func execute(actions actions, dryRun bool) error {
|
||||
for _, args := range actions.Builds {
|
||||
if err := execDocker(args, dryRun); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
// for _, args := range actions.Push {
|
||||
// if err := execDocker(args, dryRun); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if err := execDocker(actions.ManifestCreate, dryRun); err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// for _, args := range actions.ManifestAnnotate {
|
||||
// if err := execDocker(args, dryRun); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return execDocker(actions.ManifestPush, dryRun)
|
||||
}
|
||||
|
||||
func execDocker(args []string, dryRun bool) error {
|
||||
if dryRun {
|
||||
fmt.Println("docker", strings.Join(args, " "))
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := exec.Command("docker", args...)
|
||||
cmd.Env = append(cmd.Env, "DOCKER_CLI_EXPERIMENTAL=enabled")
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
log.Println(string(output))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
FROM golang:1-alpine as builder
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git make gcc musl-dev ca-certificates tzdata
|
||||
|
||||
WORKDIR /go/src/github.com/ldez/traefik-certs-dumper
|
||||
|
||||
ENV GO111MODULE on
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
RUN GOARCH={{ .GoARCH }} GOARM={{ .GoARM }} make build
|
||||
|
||||
FROM {{ .RuntimeImage }}
|
||||
|
||||
# Not supported for multi-arch without Buildkit or QEMU
|
||||
#RUN apk --update upgrade \
|
||||
# && apk --no-cache --no-progress add ca-certificates
|
||||
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY --from=builder /go/src/github.com/ldez/traefik-certs-dumper/traefik-certs-dumper /usr/bin/traefik-certs-dumper
|
||||
|
||||
ENTRYPOINT ["/usr/bin/traefik-certs-dumper"]
|
||||
@ -1,11 +1,11 @@
|
||||
package dumper
|
||||
package traefikv1
|
||||
|
||||
import (
|
||||
"github.com/go-acme/lego/certcrypto"
|
||||
"github.com/go-acme/lego/registration"
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
)
|
||||
|
||||
// StoredData represents the data managed by the Store
|
||||
// StoredData represents the data managed by the Store.
|
||||
type StoredData struct {
|
||||
Account *Account
|
||||
Certificates []*Certificate
|
||||
@ -13,20 +13,20 @@ type StoredData struct {
|
||||
TLSChallenges map[string]*Certificate
|
||||
}
|
||||
|
||||
// Certificate is a struct which contains all data needed from an ACME certificate
|
||||
// Certificate is a struct which contains all data needed from an ACME certificate.
|
||||
type Certificate struct {
|
||||
Domain Domain
|
||||
Certificate []byte
|
||||
Key []byte
|
||||
}
|
||||
|
||||
// Domain holds a domain name with SANs
|
||||
// Domain holds a domain name with SANs.
|
||||
type Domain struct {
|
||||
Main string
|
||||
SANs []string
|
||||
}
|
||||
|
||||
// Account is used to store lets encrypt registration info
|
||||
// Account is used to store lets encrypt registration info.
|
||||
type Account struct {
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
101
internal/traefikv2/acme.go
Normal file
101
internal/traefikv2/acme.go
Normal file
@ -0,0 +1,101 @@
|
||||
package traefikv2
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
)
|
||||
|
||||
// StoredData represents the data managed by Store.
|
||||
type StoredData struct {
|
||||
Account *Account
|
||||
Certificates []*CertAndStore
|
||||
}
|
||||
|
||||
// Account is used to store lets encrypt registration info.
|
||||
type Account struct {
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
PrivateKey []byte
|
||||
KeyType certcrypto.KeyType
|
||||
}
|
||||
|
||||
// GetEmail returns email.
|
||||
func (a *Account) GetEmail() string {
|
||||
return a.Email
|
||||
}
|
||||
|
||||
// GetRegistration returns lets encrypt registration resource.
|
||||
func (a *Account) GetRegistration() *registration.Resource {
|
||||
return a.Registration
|
||||
}
|
||||
|
||||
// GetPrivateKey returns private key.
|
||||
func (a *Account) GetPrivateKey() crypto.PrivateKey {
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return privateKey
|
||||
}
|
||||
|
||||
// CertAndStore allows mapping a TLS certificate to a TLS store.
|
||||
type CertAndStore struct {
|
||||
Certificate
|
||||
Store string
|
||||
}
|
||||
|
||||
// Certificate is a struct which contains all data needed from an ACME certificate.
|
||||
type Certificate struct {
|
||||
Domain Domain `json:"domain,omitempty" toml:"domain,omitempty" yaml:"domain,omitempty"`
|
||||
Certificate []byte `json:"certificate,omitempty" toml:"certificate,omitempty" yaml:"certificate,omitempty"`
|
||||
Key []byte `json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty"`
|
||||
}
|
||||
|
||||
// Domain holds a domain name with SANs.
|
||||
type Domain struct {
|
||||
// Main defines the main domain name.
|
||||
Main string `description:"Default subject name." json:"main,omitempty" toml:"main,omitempty" yaml:"main,omitempty"`
|
||||
// SANs defines the subject alternative domain names.
|
||||
SANs []string `description:"Subject alternative names." json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty"`
|
||||
}
|
||||
|
||||
// ToStrArray convert a domain into an array of strings.
|
||||
func (d *Domain) ToStrArray() []string {
|
||||
var domains []string
|
||||
if d.Main != "" {
|
||||
domains = []string{d.Main}
|
||||
}
|
||||
return append(domains, d.SANs...)
|
||||
}
|
||||
|
||||
// Set sets a domains from an array of strings.
|
||||
func (d *Domain) Set(domains []string) {
|
||||
if len(domains) > 0 {
|
||||
d.Main = domains[0]
|
||||
d.SANs = domains[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (d *Domain) DeepCopyInto(out *Domain) {
|
||||
*out = *d
|
||||
if d.SANs != nil {
|
||||
in, out := &d.SANs, &out.SANs
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Domain.
|
||||
func (d *Domain) DeepCopy() *Domain {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Domain)
|
||||
d.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
101
internal/traefikv3/acme.go
Normal file
101
internal/traefikv3/acme.go
Normal file
@ -0,0 +1,101 @@
|
||||
package traefikv3
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
)
|
||||
|
||||
// StoredData represents the data managed by Store.
|
||||
type StoredData struct {
|
||||
Account *Account
|
||||
Certificates []*CertAndStore
|
||||
}
|
||||
|
||||
// Account is used to store lets encrypt registration info.
|
||||
type Account struct {
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
PrivateKey []byte
|
||||
KeyType certcrypto.KeyType
|
||||
}
|
||||
|
||||
// GetEmail returns email.
|
||||
func (a *Account) GetEmail() string {
|
||||
return a.Email
|
||||
}
|
||||
|
||||
// GetRegistration returns lets encrypt registration resource.
|
||||
func (a *Account) GetRegistration() *registration.Resource {
|
||||
return a.Registration
|
||||
}
|
||||
|
||||
// GetPrivateKey returns private key.
|
||||
func (a *Account) GetPrivateKey() crypto.PrivateKey {
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return privateKey
|
||||
}
|
||||
|
||||
// CertAndStore allows mapping a TLS certificate to a TLS store.
|
||||
type CertAndStore struct {
|
||||
Certificate
|
||||
Store string
|
||||
}
|
||||
|
||||
// Certificate is a struct which contains all data needed from an ACME certificate.
|
||||
type Certificate struct {
|
||||
Domain Domain `json:"domain,omitempty" toml:"domain,omitempty" yaml:"domain,omitempty"`
|
||||
Certificate []byte `json:"certificate,omitempty" toml:"certificate,omitempty" yaml:"certificate,omitempty"`
|
||||
Key []byte `json:"key,omitempty" toml:"key,omitempty" yaml:"key,omitempty"`
|
||||
}
|
||||
|
||||
// Domain holds a domain name with SANs.
|
||||
type Domain struct {
|
||||
// Main defines the main domain name.
|
||||
Main string `description:"Default subject name." json:"main,omitempty" toml:"main,omitempty" yaml:"main,omitempty"`
|
||||
// SANs defines the subject alternative domain names.
|
||||
SANs []string `description:"Subject alternative names." json:"sans,omitempty" toml:"sans,omitempty" yaml:"sans,omitempty"`
|
||||
}
|
||||
|
||||
// ToStrArray convert a domain into an array of strings.
|
||||
func (d *Domain) ToStrArray() []string {
|
||||
var domains []string
|
||||
if d.Main != "" {
|
||||
domains = []string{d.Main}
|
||||
}
|
||||
return append(domains, d.SANs...)
|
||||
}
|
||||
|
||||
// Set sets a domains from an array of strings.
|
||||
func (d *Domain) Set(domains []string) {
|
||||
if len(domains) > 0 {
|
||||
d.Main = domains[0]
|
||||
d.SANs = domains[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (d *Domain) DeepCopyInto(out *Domain) {
|
||||
*out = *d
|
||||
if d.SANs != nil {
|
||||
in, out := &d.SANs, &out.SANs
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Domain.
|
||||
func (d *Domain) DeepCopy() *Domain {
|
||||
if d == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Domain)
|
||||
d.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
60
readme.md
60
readme.md
@ -1,13 +1,13 @@
|
||||
# traefik-certs-dumper
|
||||
|
||||
[](https://github.com/ldez/traefik-certs-dumper/releases/latest)
|
||||
[](https://travis-ci.org/ldez/traefik-certs-dumper)
|
||||
[](https://hub.docker.com/r/ldez/traefik-certs-dumper/)
|
||||
[](https://github.com/ldez/traefik-certs-dumper/actions)
|
||||
[](https://hub.docker.com/r/ldez/traefik-certs-dumper/)
|
||||
[](https://goreportcard.com/report/github.com/ldez/traefik-certs-dumper)
|
||||
|
||||
If you appreciate this project:
|
||||
|
||||
[](https://saythanks.io/to/ldez)
|
||||
[](https://github.com/sponsors/ldez)
|
||||
|
||||
## Features
|
||||
|
||||
@ -18,16 +18,17 @@ If you appreciate this project:
|
||||
- from file ("acme.json")
|
||||
- from KV stores (Consul, Etcd, Zookeeper)
|
||||
- Output formats:
|
||||
- use domain as sub-directory (allow custom names and extensions)
|
||||
- use domain as subdirectory (allow custom names and extensions)
|
||||
- flat (domain as filename)
|
||||
- Hook (only with watch mode and if the data source changes)
|
||||
- Support Traefik v1, v2, and v3.
|
||||
|
||||
## Installation
|
||||
|
||||
### Download / CI Integration
|
||||
|
||||
```bash
|
||||
curl -sfL https://raw.githubusercontent.com/ldez/traefik-certs-dumper/master/godownloader.sh | bash -s -- -b $GOPATH/bin v1.5.0
|
||||
curl -sfL https://raw.githubusercontent.com/ldez/traefik-certs-dumper/master/godownloader.sh | bash -s -- -b $(go env GOPATH)/bin v2.9.3
|
||||
```
|
||||
|
||||
<!--
|
||||
@ -56,6 +57,12 @@ You can use pre-compiled binaries:
|
||||
docker run ldez/traefik-certs-dumper:<tag_name>
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
- Traefik v1: [docker-compose](docs/docker-compose-traefik-v1.yml)
|
||||
- Traefik v2: [docker-compose](docs/docker-compose-traefik-v2.yml)
|
||||
- Traefik v3: TODO
|
||||
|
||||
## Usage
|
||||
|
||||
- [traefik-certs-dumper](docs/traefik-certs-dumper.md)
|
||||
@ -67,7 +74,7 @@ docker run ldez/traefik-certs-dumper:<tag_name>
|
||||
### Simple Dump
|
||||
|
||||
```console
|
||||
$ traefik-certs-dumper file
|
||||
$ traefik-certs-dumper file --version v3
|
||||
dump
|
||||
├──certs
|
||||
│ └──my.domain.com.key
|
||||
@ -79,7 +86,7 @@ dump
|
||||
### Change source and destination
|
||||
|
||||
```console
|
||||
$ traefik-certs-dumper file --source ./acme.json --dest ./dump/test
|
||||
$ traefik-certs-dumper file --version v3 --source ./acme.json --dest ./dump/test
|
||||
test
|
||||
├──certs
|
||||
│ └──my.domain.com.key
|
||||
@ -91,7 +98,7 @@ test
|
||||
### Use domain as sub-directory
|
||||
|
||||
```console
|
||||
$ traefik-certs-dumper file --domain-subdir=true
|
||||
$ traefik-certs-dumper file --version v3 --domain-subdir=true
|
||||
dump
|
||||
├──my.domain.com
|
||||
│ ├──certificate.crt
|
||||
@ -103,7 +110,7 @@ dump
|
||||
#### Change file extension
|
||||
|
||||
```console
|
||||
$ traefik-certs-dumper file --domain-subdir --crt-ext=.pem --key-ext=.pem
|
||||
$ traefik-certs-dumper file --version v3 --domain-subdir --crt-ext=.pem --key-ext=.pem
|
||||
dump
|
||||
├──my.domain.com
|
||||
│ ├──certificate.pem
|
||||
@ -115,7 +122,7 @@ dump
|
||||
#### Change file name
|
||||
|
||||
```console
|
||||
$ traefik-certs-dumper file --domain-subdir --crt-name=fullchain --key-name=privkey
|
||||
$ traefik-certs-dumper file --version v3 --domain-subdir --crt-name=fullchain --key-name=privkey
|
||||
dump
|
||||
├──my.domain.com
|
||||
│ ├──fullchain.crt
|
||||
@ -124,6 +131,37 @@ dump
|
||||
└──letsencrypt.key
|
||||
```
|
||||
|
||||
## Hook
|
||||
|
||||
Hook can be a one-liner passed as a string, or a file for more complex post-hook scenarios.
|
||||
For the former, create a file (ex: `hook.sh`) and mount it, then pass `sh hooksh` as a parameter to `--post-hook`.
|
||||
|
||||
Here is a docker-compose example:
|
||||
|
||||
```yml
|
||||
services:
|
||||
# ...
|
||||
|
||||
traefik-certs-dumper:
|
||||
image: ldez/traefik-certs-dumper:v2.9.3
|
||||
container_name: traefik-certs-dumper
|
||||
entrypoint: sh -c '
|
||||
while ! [ -e /data/acme.json ]
|
||||
|| ! [ `jq ".[] | .Certificates | length" /data/acme.json | jq -s "add" ` != 0 ]; do
|
||||
sleep 1
|
||||
; done
|
||||
&& traefik-certs-dumper file --version v2 --watch
|
||||
--source /data/acme.json --dest /data/certs
|
||||
--post-hook "sh /hook.sh"'
|
||||
labels:
|
||||
traefik.enable: false
|
||||
volumes:
|
||||
- ./letsencrypt:/data
|
||||
- ./hook.sh:/hook.sh
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
### KV store
|
||||
|
||||
#### Consul
|
||||
@ -149,5 +187,3 @@ $ traefik-certs-dumper kv boltdb --endpoints /the/path/to/mydb.db
|
||||
```console
|
||||
$ traefik-certs-dumper kv zookeeper --endpoints localhost:2181
|
||||
```
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user