├── TODO.md ├── .gitignore ├── .goreleaser.yml ├── examples └── redis.nomad ├── cmd ├── main.go ├── models.go ├── record_test.go ├── update.go ├── utils.go ├── nomad.go ├── app.go ├── record.go ├── init.go └── prune.go ├── .github └── workflows │ └── build.yml ├── .golangci.yml ├── Makefile ├── docs ├── aws.md └── nomad.md ├── config.sample.toml ├── LICENSE ├── go.mod ├── README.md └── go.sum /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | - [ ] Cloudflare provider 4 | - [ ] Add lint to Github actions 5 | - [ ] Release docker image 6 | - [ ] Add deployment notes for Nomad, AWS, IAM permissions required etc. 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | data/ 18 | config.toml 19 | *.env 20 | bin/ 21 | dist/ 22 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | env: 2 | - GO111MODULE=on 3 | - CGO_ENABLED=0 4 | 5 | builds: 6 | - binary: nomad-external-dns.bin 7 | id: nomad-external-dns 8 | goos: 9 | - linux 10 | goarch: 11 | - amd64 12 | ldflags: 13 | - -s -w -X "main.buildString={{ .Tag }} ({{ .ShortCommit }} {{ .Date }})" 14 | dir: ./cmd/ 15 | 16 | archives: 17 | - format: tar.gz 18 | files: 19 | - README.md 20 | - LICENSE 21 | - config.sample.toml 22 | -------------------------------------------------------------------------------- /examples/redis.nomad: -------------------------------------------------------------------------------- 1 | job "redis" { 2 | datacenters = ["dc1"] 3 | 4 | type = "service" 5 | 6 | group "cache" { 7 | count = 3 8 | 9 | network { 10 | port "db" { 11 | to = 6379 12 | } 13 | } 14 | 15 | service { 16 | provider = "nomad" 17 | name = "redis-cache" 18 | tags = [ 19 | "external-dns/hostname=redis.test.internal", 20 | "external-dns/ttl=30s", 21 | ] 22 | port = "db" 23 | 24 | } 25 | 26 | task "redis" { 27 | driver = "docker" 28 | 29 | config { 30 | image = "redis:7" 31 | 32 | ports = ["db"] 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "log" 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | ) 10 | 11 | var ( 12 | // Version of the build. This is injected at build-time. 13 | buildString = "unknown" 14 | cfgPath = "config.sample.toml" 15 | ) 16 | 17 | func main() { 18 | ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) 19 | defer stop() 20 | 21 | ko := initConfig(cfgPath, "NOMAD_EXTERNAL_DNS_") 22 | 23 | app, err := initApp(ko) 24 | if err != nil { 25 | log.Fatalf("Unable to initialize the app: %v", err) 26 | } 27 | 28 | app.lo.Info("Starting nomad-external-dns", "version", buildString) 29 | app.Start(ctx) 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: goreleaser 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | goreleaser: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v3 17 | with: 18 | fetch-depth: 0 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: "1.20" 23 | - name: Run GoReleaser 24 | uses: goreleaser/goreleaser-action@v4 25 | with: 26 | version: latest 27 | args: release --clean 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | enable: 3 | - revive 4 | - maligned 5 | - bodyclose 6 | - deadcode 7 | - errcheck 8 | - gofmt 9 | - goimports 10 | - gosec 11 | - gosimple 12 | - govet 13 | - ineffassign 14 | - misspell 15 | - prealloc 16 | - staticcheck 17 | - structcheck 18 | - typecheck 19 | - unconvert 20 | - unused 21 | - varcheck 22 | 23 | disable-all: true 24 | 25 | run: 26 | deadline: 5m 27 | issues-exit-code: 1 28 | tests: false 29 | 30 | output: 31 | format: "colored-line-number" 32 | print-issued-lines: true 33 | print-linter-name: true 34 | 35 | issues: 36 | exclude-use-default: false 37 | max-per-linter: 0 38 | max-same-issues: 0 39 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | APP-BIN := ./bin/nomad-external-dns.bin 2 | 3 | LAST_COMMIT := $(shell git rev-parse --short HEAD) 4 | LAST_COMMIT_DATE := $(shell git show -s --format=%ci ${LAST_COMMIT}) 5 | VERSION := $(shell git describe --tags) 6 | BUILDSTR := ${VERSION} (Commit: ${LAST_COMMIT_DATE} (${LAST_COMMIT}), Build: $(shell date +"%Y-%m-%d% %H:%M:%S %z")) 7 | 8 | .PHONY: build 9 | build: ## Build binary. 10 | go build -o ${APP-BIN} -ldflags="-X 'main.buildString=${BUILDSTR}'" ./cmd/ 11 | 12 | .PHONY: run 13 | run: ## Run binary. 14 | mkdir -p ./data/events 15 | ./${APP-BIN} 16 | 17 | .PHONY: fresh 18 | fresh: clean build run 19 | 20 | .PHONY: clean 21 | clean: 22 | rm -rf bin/${APP-BIN} 23 | go clean 24 | 25 | .PHONY: lint 26 | lint: 27 | golangci-lint run -v 28 | -------------------------------------------------------------------------------- /docs/aws.md: -------------------------------------------------------------------------------- 1 | # AWS 2 | 3 | ## IAM Policy 4 | 5 | Use this IAM policy to list zones and update DNS records. 6 | 7 | ```json 8 | { 9 | "Version": "2012-10-17", 10 | "Statement": [ 11 | { 12 | "Effect": "Allow", 13 | "Action": [ 14 | "route53:ChangeResourceRecordSets" 15 | ], 16 | "Resource": [ 17 | "arn:aws:route53:::hostedzone/*" 18 | ] 19 | }, 20 | { 21 | "Effect": "Allow", 22 | "Action": [ 23 | "route53:ListHostedZones", 24 | "route53:ListHostedZonesByName", 25 | "route53:ListResourceRecordSets" 26 | ], 27 | "Resource": [ 28 | "*" 29 | ] 30 | } 31 | ] 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /config.sample.toml: -------------------------------------------------------------------------------- 1 | [app] 2 | log_level = "debug" # `debug` for verbose logs. `info` otherwise. 3 | env = "dev" # dev|prod. 4 | dry_run = true # set to true if you don't want the DNS records to be actually created. 5 | update_interval = "10s" # Interval at which the records are synced from Nomad to DNS providers. 6 | prune_interval = "15s" # Interval at which any extra records that exist in DNS providers but doesn't exist in Nomad cluster are cleaned up. It maybe an expensive operation with some DNS providers like AWS R53 to do this so keep a higher interval (preferably in order of a few minutes) 7 | 8 | [dns] 9 | provider = "route53" 10 | domain_filters = ["test.internal"] 11 | owner_uuid = "0af79bd2-f7e5-4231-bc6a-b492aac6ffbe" # This key is used to identify the records created by this tool. Records without this key will be ignored. 12 | 13 | [provider.route53] 14 | region = "ap-south-1" 15 | max_retries = 5 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Karan Sharma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cmd/models.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/libdns/libdns" 7 | ) 8 | 9 | const ( 10 | // HostnameAnnotationKey is the annotated tag for defining hostname. 11 | HostnameAnnotationKey = "external-dns/hostname" 12 | // TTLAnnotationKey is the annotated tag for defining TTL. 13 | TTLAnnotationKey = "external-dns/ttl" 14 | // DefaultTTL is the TTL to set for records if unspecified or unparseable. 15 | DefaultTTL = time.Second * 30 16 | ) 17 | 18 | // ServiceMeta contains minimal items from a api.ServiceRegistration event. 19 | type ServiceMeta struct { 20 | Name string // Human Name of the service. 21 | Namespace string // Namespace to which the service belongs to. 22 | Job string // Job to which the service belongs to. 23 | Addresses []string // Address of all backend services which is fetched by calling Nomad HTTP API. 24 | Tags []string // Tags in the given service. 25 | DNSName string // DNS name of the service. 26 | } 27 | 28 | // DNSProvider wraps the required libdns interfaces. 29 | type DNSProvider interface { 30 | libdns.RecordAppender 31 | libdns.RecordGetter 32 | libdns.RecordSetter 33 | libdns.RecordDeleter 34 | } 35 | 36 | // RecordMeta wraps around `libdns.Record` 37 | // and adds additional fields. 38 | type RecordMeta struct { 39 | Records []libdns.Record 40 | Zone string 41 | } 42 | -------------------------------------------------------------------------------- /docs/nomad.md: -------------------------------------------------------------------------------- 1 | # Deploying on Nomad 2 | 3 | ## Jobspec 4 | 5 | This is an example jobspec that you can refer to for deploying `nomad-external-dns`. This example uses the `raw_exec` mode and pulls the binary from GitHub Releases. 6 | 7 | ```hcl 8 | job "nomad-external-dns" { 9 | datacenters = ["dc1"] 10 | namespace = "default" 11 | type = "service" 12 | 13 | group "nomad-external-dns" { 14 | count = 1 15 | 16 | network { 17 | mode = "host" 18 | } 19 | 20 | task "app" { 21 | driver = "raw_exec" 22 | 23 | artifact { 24 | source = "https://github.com/mr-karan/nomad-external-dns/releases/download/v0.1.3/nomad-external-dns_0.1.3_linux_amd64.tar.gz" 25 | } 26 | 27 | env { 28 | NOMAD_TOKEN = "xxx" 29 | AWS_ACCESS_KEY_ID = "yyy" 30 | AWS_SECRET_ACCESS_KEY = "zzz" 31 | } 32 | 33 | config { 34 | command = "$${NOMAD_TASK_DIR}/nomad-external-dns.bin" 35 | args = [ 36 | "--config", 37 | "$${NOMAD_TASK_DIR}/config.sample.toml" 38 | ] 39 | } 40 | 41 | resources { 42 | cpu = 500 43 | memory = 500 44 | } 45 | } 46 | } 47 | } 48 | ``` 49 | 50 | ## Notes 51 | 52 | - If ACL is enabled, then you must generate and provide a `NOMAD_TOKEN` variable. 53 | - The service must be able to access the Nomad Cluster API. You can configure other Nomad variables using `env` stanza. 54 | -------------------------------------------------------------------------------- /cmd/record_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/libdns/libdns" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestToRecord(t *testing.T) { 12 | tests := []struct { 13 | name string 14 | service *ServiceMeta 15 | domains []string 16 | owner string 17 | want RecordMeta 18 | wantError bool 19 | }{ 20 | { 21 | name: "valid service", 22 | service: &ServiceMeta{ 23 | Name: "redis", 24 | Namespace: "default", 25 | Job: "redis-job", 26 | Addresses: []string{"192.168.1.1"}, 27 | Tags: []string{"external-dns/hostname=redis.test.internal", "external-dns/ttl=30s"}, 28 | }, 29 | domains: []string{"test.internal"}, 30 | owner: "test-owner", 31 | want: RecordMeta{ 32 | Zone: "test.internal.", 33 | Records: []libdns.Record{ 34 | { 35 | Type: "A", 36 | Name: "redis", 37 | Value: "192.168.1.1", 38 | TTL: 30 * time.Second, 39 | }, 40 | { 41 | Type: "TXT", 42 | Name: "redis", 43 | Value: "service=redis namespace=default owner=test-owner created-by=nomad-external-dns", 44 | TTL: 30 * time.Second, 45 | }, 46 | }, 47 | }, 48 | }, 49 | { 50 | name: "empty tags", 51 | service: &ServiceMeta{ 52 | Name: "redis", 53 | Namespace: "default", 54 | Job: "redis-job", 55 | Addresses: []string{"192.168.1.1"}, 56 | Tags: []string{}, 57 | }, 58 | domains: []string{"test.internal"}, 59 | owner: "test-owner", 60 | wantError: true, 61 | }, 62 | } 63 | 64 | for _, tt := range tests { 65 | t.Run(tt.name, func(t *testing.T) { 66 | got, err := tt.service.ToRecord(tt.domains, tt.owner) 67 | if tt.wantError { 68 | assert.Error(t, err) 69 | } else { 70 | assert.NoError(t, err) 71 | assert.Equal(t, tt.want, got) 72 | } 73 | }) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /cmd/update.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // updateRecords goes through each service in the given map 8 | // and propagates DNS record changes for new or updated services. 9 | // The check to see if a service has to be updated reduces the number of 10 | // API calls to the DNS provider. 11 | func (app *App) updateRecords(services map[string]ServiceMeta, domains []string) { 12 | app.RLock() 13 | defer app.RUnlock() 14 | 15 | for key, service := range services { 16 | if isNewOrUpdatedService(app.services[key], service) { 17 | app.lo.Debug("Service is new or updated", "service", service.DNSName) 18 | if err := app.propogateChange(key, service, domains); err != nil { 19 | app.lo.Error("Error updating DNS records for service", "service", service.DNSName, "error", err) 20 | // Continue processing other services even if this one fails. 21 | continue 22 | } 23 | } 24 | } 25 | } 26 | 27 | // isNewOrUpdatedService checks if the service is new or has been updated. 28 | func isNewOrUpdatedService(existingService, newService ServiceMeta) bool { 29 | // If the service does not exist or its addresses or tags have changed, 30 | // it's considered a new or updated service. 31 | return existingService.Name == "" || 32 | !sameStringSlice(existingService.Addresses, newService.Addresses) || 33 | !sameStringSlice(existingService.Tags, newService.Tags) 34 | } 35 | 36 | // propogateChange updates DNS records for the given service and returns any error encountered. 37 | func (app *App) propogateChange(key string, svc ServiceMeta, domains []string) error { 38 | record, err := svc.ToRecord(domains, app.opts.owner) 39 | if err != nil { 40 | app.lo.Error("error converting service to record", "error", err) 41 | return err 42 | } 43 | 44 | _, err = app.provider.SetRecords(context.Background(), record.Zone, record.Records) 45 | if err != nil { 46 | app.lo.Error("error setting records to zone", "error", err) 47 | return err 48 | } 49 | 50 | app.lo.Info("Updated DNS records", "zone", record.Zone, "records", record.Records) 51 | app.services[key] = svc 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mr-karan/nomad-external-dns 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/hashicorp/nomad/api v0.0.0-20230627233251-f3df01e4220d 7 | github.com/knadh/koanf v1.5.0 8 | github.com/libdns/libdns v0.2.1 9 | github.com/mr-karan/libdns-route53 v1.4.0 10 | github.com/spf13/pflag v1.0.5 11 | github.com/stretchr/testify v1.8.1 12 | golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df 13 | ) 14 | 15 | require ( 16 | github.com/aws/aws-sdk-go-v2 v1.18.1 // indirect 17 | github.com/aws/aws-sdk-go-v2/config v1.18.27 // indirect 18 | github.com/aws/aws-sdk-go-v2/credentials v1.13.26 // indirect 19 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 // indirect 20 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 // indirect 21 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 // indirect 22 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 // indirect 23 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 // indirect 24 | github.com/aws/aws-sdk-go-v2/service/route53 v1.28.3 // indirect 25 | github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 // indirect 26 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 // indirect 27 | github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 // indirect 28 | github.com/aws/smithy-go v1.13.5 // indirect 29 | github.com/davecgh/go-spew v1.1.1 // indirect 30 | github.com/fsnotify/fsnotify v1.6.0 // indirect 31 | github.com/gorilla/websocket v1.5.0 // indirect 32 | github.com/hashicorp/cronexpr v1.1.2 // indirect 33 | github.com/hashicorp/errwrap v1.1.0 // indirect 34 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 35 | github.com/hashicorp/go-multierror v1.1.1 // indirect 36 | github.com/hashicorp/go-rootcerts v1.0.2 // indirect 37 | github.com/jmespath/go-jmespath v0.4.0 // indirect 38 | github.com/mitchellh/copystructure v1.2.0 // indirect 39 | github.com/mitchellh/go-homedir v1.1.0 // indirect 40 | github.com/mitchellh/mapstructure v1.5.0 // indirect 41 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 42 | github.com/pelletier/go-toml v1.9.5 // indirect 43 | github.com/pmezard/go-difflib v1.0.0 // indirect 44 | golang.org/x/sys v0.9.0 // indirect 45 | gopkg.in/yaml.v3 v3.0.1 // indirect 46 | ) 47 | -------------------------------------------------------------------------------- /cmd/utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "sort" 5 | "strings" 6 | 7 | "github.com/hashicorp/nomad/api" 8 | ) 9 | 10 | // Contains checks if a string is present in a slice of strings. 11 | func Contains(s []string, e string) bool { 12 | for _, a := range s { 13 | if a == e { 14 | return true 15 | } 16 | } 17 | return false 18 | } 19 | 20 | // sameStringSlice checks if two string slices have the same elements, regardless of order. 21 | func sameStringSlice(s1, s2 []string) bool { 22 | // Make copies of the slices, because sort.Strings() sorts in place 23 | s1Copy := append([]string{}, s1...) 24 | s2Copy := append([]string{}, s2...) 25 | 26 | sort.Strings(s1Copy) 27 | sort.Strings(s2Copy) 28 | 29 | if len(s1Copy) != len(s2Copy) { 30 | return false 31 | } 32 | for i := range s1Copy { 33 | if s1Copy[i] != s2Copy[i] { 34 | return false 35 | } 36 | } 37 | return true 38 | } 39 | 40 | // hasHostnameAnnotation checks if the provided tags contain a hostname annotation. 41 | // The function iterates over the tags and returns true if a tag with the HostnameAnnotationKey prefix is found. 42 | // If no such tag is found, the function returns false. 43 | func hasHostnameAnnotation(tags []string) bool { 44 | for _, t := range tags { 45 | if strings.HasPrefix(t, HostnameAnnotationKey) { 46 | return true 47 | } 48 | } 49 | return false 50 | } 51 | 52 | // uniqueAddresses generates a slice of unique addresses from a provided slice of ServiceRegistration pointers. 53 | // It uses a map as a set to remove duplicates, adding each address to the map. 54 | // Then, it creates a slice of addresses from the keys of the map. 55 | // The function returns this slice of unique addresses. 56 | func uniqueAddresses(svcRegistrations []*api.ServiceRegistration) []string { 57 | uniqueAddrs := make(map[string]struct{}) 58 | for _, s := range svcRegistrations { 59 | uniqueAddrs[s.Address] = struct{}{} 60 | } 61 | addr := make([]string, 0, len(uniqueAddrs)) 62 | for key := range uniqueAddrs { 63 | addr = append(addr, key) 64 | } 65 | return addr 66 | } 67 | 68 | // getDNSNameFromTags extracts the DNS name from the service's tags. 69 | func getDNSNameFromTags(tags []string) string { 70 | for _, tag := range tags { 71 | if strings.HasPrefix(tag, HostnameAnnotationKey+"=") { 72 | return strings.TrimPrefix(tag, HostnameAnnotationKey+"=") 73 | } 74 | } 75 | return "" 76 | } 77 | 78 | // EnsureFQDN makes sure the domain name is fully qualified (i.e., ends with a dot). 79 | func EnsureFQDN(name string) string { 80 | if !strings.HasSuffix(name, ".") { 81 | return name + "." 82 | } 83 | return name 84 | } 85 | -------------------------------------------------------------------------------- /cmd/nomad.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hashicorp/nomad/api" 7 | ) 8 | 9 | // fetchNomadServices retrieves all services from the Nomad API 10 | // and returns a map where of services where the key is the unique ID of the service. 11 | func (app *App) fetchNomadServices() (map[string]ServiceMeta, error) { 12 | services := make(map[string]ServiceMeta) 13 | 14 | // Fetch the list of services 15 | serviceList, err := app.fetchServiceList() 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | // Iterate over each service to fetch its metadata 21 | for _, l := range serviceList { 22 | for _, s := range l.Services { 23 | svcMeta, err := app.fetchServiceMeta(l.Namespace, s.ServiceName) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | // If metadata exists, store it in the services map. 29 | if svcMeta != nil { 30 | services[EnsureFQDN(svcMeta.DNSName)] = *svcMeta 31 | } 32 | } 33 | } 34 | 35 | return services, nil 36 | } 37 | 38 | // fetchServiceList retrieves the list of services from the Nomad API. 39 | func (app *App) fetchServiceList() ([]*api.ServiceRegistrationListStub, error) { 40 | servicesList, _, err := app.nomadClient.Services().List(&api.QueryOptions{Namespace: "*"}) 41 | if err != nil { 42 | return nil, fmt.Errorf("error listing services: %w", err) 43 | } 44 | app.lo.Debug("Fetched service list with count", "count", len(servicesList)) 45 | return servicesList, nil 46 | } 47 | 48 | // fetchServiceMeta fetches the metadata for a single service. 49 | func (app *App) fetchServiceMeta(namespace, serviceName string) (*ServiceMeta, error) { 50 | // Fetch the service details 51 | svcRegistrations, _, err := app.nomadClient.Services().Get(serviceName, &api.QueryOptions{Namespace: namespace}) 52 | if err != nil { 53 | return nil, fmt.Errorf("error fetching service detail: %w", err) 54 | } 55 | 56 | // If there are no service registrations or no tags, return nil. 57 | if len(svcRegistrations) == 0 || len(svcRegistrations[0].Tags) == 0 { 58 | return nil, nil 59 | } 60 | 61 | // Check if the service has a hostname annotation, if not, ignore the service. 62 | if !hasHostnameAnnotation(svcRegistrations[0].Tags) { 63 | app.lo.Debug("Hostname not found in tags, ignoring service", "service", svcRegistrations[0].ServiceName) 64 | return nil, nil 65 | } 66 | 67 | // Create a ServiceMeta object and return its reference. 68 | svcMeta := ServiceMeta{ 69 | Name: svcRegistrations[0].ServiceName, 70 | Namespace: svcRegistrations[0].Namespace, 71 | Job: svcRegistrations[0].JobID, 72 | Tags: svcRegistrations[0].Tags, 73 | Addresses: uniqueAddresses(svcRegistrations), 74 | DNSName: getDNSNameFromTags(svcRegistrations[0].Tags), 75 | } 76 | return &svcMeta, nil 77 | } 78 | -------------------------------------------------------------------------------- /cmd/app.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "time" 7 | 8 | "github.com/hashicorp/nomad/api" 9 | "golang.org/x/exp/slog" 10 | ) 11 | 12 | // Opts represents certain configurable items. 13 | type Opts struct { 14 | updateInterval time.Duration 15 | pruneInterval time.Duration 16 | owner string 17 | domains []string 18 | dryRun bool 19 | } 20 | 21 | // App is the global container that holds 22 | // objects of various routines that run on boot. 23 | type App struct { 24 | sync.RWMutex 25 | 26 | lo *slog.Logger 27 | opts Opts 28 | provider DNSProvider 29 | nomadClient *api.Client 30 | services map[string]ServiceMeta 31 | } 32 | 33 | // Start initialises background workers and waits for them to exit on cancellation. 34 | func (app *App) Start(ctx context.Context) { 35 | var wg sync.WaitGroup 36 | 37 | app.runWorker(ctx, &wg, app.opts.updateInterval, app.UpdateServices, "updater") 38 | app.runWorker(ctx, &wg, app.opts.pruneInterval, app.PruneRecords, "pruner") 39 | 40 | // Wait for all routines to finish. 41 | wg.Wait() 42 | } 43 | 44 | // runWorker is a helper function to encapsulate the goroutine spawning and error handling logic. 45 | func (app *App) runWorker(ctx context.Context, wg *sync.WaitGroup, interval time.Duration, workerFunc func(context.Context), workerName string) { 46 | wg.Add(1) 47 | 48 | go func() { 49 | defer wg.Done() 50 | 51 | ticker := time.NewTicker(interval) 52 | defer ticker.Stop() 53 | 54 | for { 55 | select { 56 | case <-ticker.C: 57 | workerFunc(ctx) 58 | case <-ctx.Done(): 59 | app.lo.Warn("Context cancellation received, terminating worker", "worker", workerName) 60 | return 61 | } 62 | } 63 | }() 64 | } 65 | 66 | // UpdateServices fetches Nomad services from all the namespaces 67 | // and updates the records in upstream DNS providers. 68 | func (app *App) UpdateServices(ctx context.Context) { 69 | // Fetch the list of services from the cluster. 70 | services, err := app.fetchNomadServices() 71 | if err != nil { 72 | app.lo.Error("Failed to fetch services", "error", err) 73 | return 74 | } 75 | 76 | // Update DNS records for the services fetched. 77 | // This function holds a read lock to determine whether to update records or not. 78 | app.updateRecords(services, app.opts.domains) 79 | 80 | // Add the updated services map to the app once the records are synced. 81 | app.Lock() 82 | app.services = services 83 | app.Unlock() 84 | } 85 | 86 | // PruneRecords fetches the records for all zones from the DNS provider and checks 87 | // whether the service exists in Nomad cluster. If it doesn't exist then it prunes the record in Provider. 88 | func (app *App) PruneRecords(ctx context.Context) { 89 | // cleanupRecords handles DNS deletions for unused records. 90 | if err := app.cleanupRecords(); err != nil { 91 | app.lo.Error("Failed to fetch records", "error", err) 92 | return 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # nomad-external-dns 4 | 5 | _Synchronize Nomad Services with external DNS providers._ 6 | 7 | Inspired by [kubernetes-sigs/external-dns](https://github.com/kubernetes-sigs/external-dns), `nomad-external-dns` makes Nomad Services discoverable via DNS servers. 8 | Nomad 1.3+ [bundles support](https://www.hashicorp.com/blog/nomad-1-3-adds-native-service-discovery-and-edge-workload-support) for native service discovery and `nomad-external-dns` helps to advertise the services inside this registry to external DNS providers. 9 | 10 | ## Supported Providers 11 | 12 | * [AWS Route 53](https://aws.amazon.com/route53/) 13 | * [CloudFlare](https://www.cloudflare.com/dns) - _Coming Soon!_ 14 | 15 | ## How it Works 16 | 17 | `nomad-external-dns` uses the concept of "Annotated Tags" to set properties for the DNS records. Here's an example of a `service` stanza inside a Nomad jobspec: 18 | 19 | ```hcl 20 | service { 21 | provider = "nomad" 22 | name = "redis-cache" 23 | tags = [ 24 | "external-dns/hostname=redis.test.internal", 25 | "external-dns/ttl=30s", 26 | ] 27 | port = "db" 28 | } 29 | ``` 30 | 31 | - At every `app.update_interval` frequency, list of all services across namespaces in the Nomad cluster are fetched. 32 | - For each service, `external-dns` prefix is used to determine properties like TTL, Hostname etc. 33 | - DNS record for this service is created with the registered DNS Provider. `nomad-external-dns` creates or updates an existing record automatically. 34 | 35 | ## Deploy 36 | 37 | NOTE: This is meant to run inside a Nomad cluster and should have proper ACL to query for services across multiple namespaces. 38 | 39 | You can choose one of the various deployment options: 40 | 41 | ### Binary 42 | 43 | Grab the latest release from [Releases](https://github.com/mr-karan/nomad-external-dns/releases). 44 | 45 | To run: 46 | 47 | ``` 48 | $ ./nomad-external-dns.bin --config config.toml 49 | ``` 50 | 51 | ### Nomad 52 | 53 | Refer to the [jobspec](./docs/nomad.md#jobspec) for deploying in a Nomad cluster. 54 | 55 | If you're deploying on AWS, consider referring to the IAM policy mentioned [here](./docs/aws.md#iam-policy) 56 | 57 | ## Configuration 58 | 59 | Refer to [config.sample.toml](./config.sample.toml) for a list of configurable values. 60 | 61 | ### Environment Variables 62 | 63 | All config variables can also be populated as env vairables by prefixing `NOMAD_EXTERNAL_DNS_` and replacing `.` with `__`. 64 | 65 | For eg: `app.update_interval` becomes `NOMAD_EXTERNAL_DNS_app__update_interval`. 66 | 67 | For configuring Nomad API client, [these environment variables](https://www.nomadproject.io/docs/runtime/environment) can be set. 68 | 69 | ## Contribution 70 | 71 | - Support for new providers can be added by registering more providers using [libdns](https://github.com/libdns/libdns). 72 | - Feel free to report any bugs/feature requests. 73 | 74 | ## LICENSE 75 | 76 | [LICENSE](./LICENSE) 77 | -------------------------------------------------------------------------------- /cmd/record.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | 8 | "github.com/libdns/libdns" 9 | ) 10 | 11 | // ToRecord converts a service meta object to a libdns record. 12 | // This is used to send to upstream DNS providers. 13 | func (s *ServiceMeta) ToRecord(domains []string, owner string) (RecordMeta, error) { 14 | var ( 15 | host string 16 | zone string 17 | ttl = DefaultTTL // Set default TTL 18 | err error 19 | ) 20 | 21 | if len(s.Tags) == 0 { 22 | return RecordMeta{}, fmt.Errorf("tags cannot be empty") 23 | } 24 | 25 | // Parse the hostname and TTL from tags. 26 | host, zone, ttl, err = s.parseTags(domains) 27 | if err != nil { 28 | return RecordMeta{}, err 29 | } 30 | 31 | zone = EnsureFQDN(zone) 32 | 33 | record := prepareRecord(s, host, zone, ttl, owner) 34 | return record, nil 35 | } 36 | 37 | // parseTags parses service tags to extract hostname, zone and ttl. 38 | func (s *ServiceMeta) parseTags(domains []string) (host, zone string, ttl time.Duration, err error) { 39 | for _, tag := range s.Tags { 40 | if strings.HasPrefix(tag, HostnameAnnotationKey) { 41 | host, zone, err = parseHost(tag, domains) 42 | if err != nil { 43 | return 44 | } 45 | } else if strings.HasPrefix(tag, TTLAnnotationKey) { 46 | ttl, err = parseTTL(tag) 47 | if err != nil { 48 | ttl = DefaultTTL 49 | } 50 | } 51 | } 52 | return 53 | } 54 | 55 | // parseHost extracts host and zone from a given tag. 56 | func parseHost(tag string, domains []string) (host, zone string, err error) { 57 | split := strings.Split(tag, HostnameAnnotationKey+"=") 58 | if len(split) != 2 { 59 | return "", "", fmt.Errorf("error splitting tag %s: expected 2 elements, got %d", tag, len(split)) 60 | } 61 | 62 | // For a given list of domain filters, determine if the hostname belongs to that domain. 63 | for _, domain := range domains { 64 | if strings.Contains(split[1], domain) { 65 | // If a valid domain is found, return the host and domain separately. 66 | return strings.TrimRight(strings.Split(split[1], domain)[0], "."), domain, nil 67 | } 68 | } 69 | 70 | return "", "", fmt.Errorf("hostname doesn't contain a valid domain TLD") 71 | } 72 | 73 | // parseTTL extracts ttl from a given tag. 74 | func parseTTL(tag string) (ttl time.Duration, err error) { 75 | split := strings.Split(tag, TTLAnnotationKey+"=") 76 | if len(split) != 2 { 77 | return -1, fmt.Errorf("error splitting tag %s: expected 2 elements, got %d", tag, len(split)) 78 | } 79 | ttl, err = time.ParseDuration(split[1]) 80 | if err != nil { 81 | return -1, err 82 | } 83 | return ttl, nil 84 | } 85 | 86 | func prepareRecord(s *ServiceMeta, host, zone string, ttl time.Duration, owner string) RecordMeta { 87 | // Generate comma-separated list of addresses 88 | addresses := strings.Join(s.Addresses, ",") 89 | 90 | // Create an A record with all addresses 91 | aRecord := libdns.Record{ 92 | Type: "A", 93 | Name: host, 94 | Value: addresses, 95 | TTL: ttl, 96 | } 97 | 98 | // Create a TXT record with metadata 99 | txtRecord := libdns.Record{ 100 | Type: "TXT", 101 | Name: host, 102 | Value: fmt.Sprintf( 103 | "service=%s namespace=%s owner=%s created-by=nomad-external-dns", 104 | s.Name, s.Namespace, owner, 105 | ), 106 | TTL: ttl, 107 | } 108 | 109 | // Combine the A and TXT records 110 | records := []libdns.Record{aRecord, txtRecord} 111 | 112 | return RecordMeta{ 113 | Zone: zone, 114 | Records: records, 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /cmd/init.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "strings" 8 | 9 | "github.com/hashicorp/nomad/api" 10 | "github.com/knadh/koanf" 11 | "github.com/knadh/koanf/parsers/toml" 12 | "github.com/knadh/koanf/providers/env" 13 | "github.com/knadh/koanf/providers/file" 14 | route53 "github.com/mr-karan/libdns-route53" 15 | flag "github.com/spf13/pflag" 16 | "golang.org/x/exp/slog" 17 | ) 18 | 19 | // initLogger initializes logger instance. 20 | func initLogger(ko *koanf.Koanf) *slog.Logger { 21 | opts := slog.HandlerOptions{} 22 | if ko.String("app.log_level") == "debug" { 23 | opts.Level = slog.LevelDebug 24 | opts.AddSource = true 25 | } 26 | logger := slog.New(slog.NewTextHandler(os.Stderr, &opts)) 27 | 28 | return logger 29 | } 30 | 31 | // initConfig loads config to `ko` object. 32 | func initConfig(cfgDefault string, envPrefix string) *koanf.Koanf { 33 | var ( 34 | ko = koanf.New(".") 35 | f = flag.NewFlagSet("front", flag.ContinueOnError) 36 | ) 37 | 38 | // Configure Flags. 39 | f.Usage = func() { 40 | fmt.Println(f.FlagUsages()) 41 | os.Exit(0) 42 | } 43 | 44 | // Register `--config` flag. 45 | cfgPath := f.String("config", cfgDefault, "Path to a config file to load.") 46 | 47 | // Parse and Load Flags. 48 | err := f.Parse(os.Args[1:]) 49 | if err != nil { 50 | fmt.Printf("error parsing flags: %v\n", err) 51 | os.Exit(1) 52 | } 53 | 54 | // Load the config files from the path provided. 55 | fmt.Printf("attempting to load config from file: %s\n", *cfgPath) 56 | 57 | err = ko.Load(file.Provider(*cfgPath), toml.Parser()) 58 | if err != nil { 59 | // If the default config is not present, print a warning and continue reading the values from env. 60 | if *cfgPath == cfgDefault { 61 | fmt.Printf("unable to open sample config file: %v\n", err) 62 | } else { 63 | fmt.Printf("error loading config: %v\n", err) 64 | os.Exit(1) 65 | } 66 | } 67 | 68 | fmt.Printf("attempting to read config from env vars\n") 69 | // Load environment variables if the key is given 70 | // and merge into the loaded config. 71 | if envPrefix != "" { 72 | err = ko.Load(env.Provider(envPrefix, ".", func(s string) string { 73 | return strings.Replace(strings.ToLower( 74 | strings.TrimPrefix(s, envPrefix)), "__", ".", -1) 75 | }), nil) 76 | if err != nil { 77 | fmt.Printf("error loading env config: %v\n", err) 78 | os.Exit(1) 79 | } 80 | } 81 | 82 | return ko 83 | } 84 | 85 | // initNomadClient initialises a Nomad API client. 86 | func initNomadClient() (*api.Client, error) { 87 | client, err := api.NewClient(api.DefaultConfig()) 88 | if err != nil { 89 | return nil, err 90 | } 91 | return client, nil 92 | } 93 | 94 | func initOpts(ko *koanf.Koanf) Opts { 95 | return Opts{ 96 | updateInterval: ko.MustDuration("app.update_interval"), 97 | pruneInterval: ko.MustDuration("app.prune_interval"), 98 | domains: ko.MustStrings("dns.domain_filters"), 99 | dryRun: ko.Bool("app.dry_run"), 100 | owner: ko.MustString("dns.owner_uuid"), 101 | } 102 | } 103 | 104 | // initProvider initialises a DNS controller object to interact with 105 | // the upstream DNS provider. 106 | func initProvider(ko *koanf.Koanf) (DNSProvider, error) { 107 | var ( 108 | provider DNSProvider 109 | err error 110 | ) 111 | 112 | switch ko.MustString("dns.provider") { 113 | case "route53": 114 | provider, err = route53.NewProvider(context.Background(), route53.Opt{ 115 | MaxRetries: ko.Int("provider.route53.max_retries"), 116 | Region: ko.MustString("provider.route53.region"), // libdns defaults to us-east-1 so this **must** be provided. 117 | }) 118 | if err != nil { 119 | return nil, err 120 | } 121 | 122 | default: 123 | return nil, fmt.Errorf("unknown provider type") 124 | } 125 | 126 | // Initialise the controller object. 127 | return provider, nil 128 | } 129 | 130 | func initApp(ko *koanf.Koanf) (*App, error) { 131 | logger := initLogger(ko) 132 | opts := initOpts(ko) 133 | 134 | // Validate that prune_interval must always be greater than update_interval. 135 | if opts.pruneInterval < opts.updateInterval { 136 | return nil, fmt.Errorf("prune_interval should be greater than update_interval") 137 | } 138 | 139 | prov, err := initProvider(ko) 140 | if err != nil { 141 | return nil, fmt.Errorf("Failed to initialize DNS provider: %w", err) 142 | } 143 | 144 | client, err := initNomadClient() 145 | if err != nil { 146 | return nil, fmt.Errorf("Failed to initialize Nomad API client: %w", err) 147 | } 148 | 149 | logger.Info("Initialized Nomad client", "addr", client.Address()) 150 | 151 | return &App{ 152 | lo: logger, 153 | opts: opts, 154 | services: make(map[string]ServiceMeta, 0), 155 | provider: prov, 156 | nomadClient: client, 157 | }, nil 158 | } 159 | -------------------------------------------------------------------------------- /cmd/prune.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/libdns/libdns" 9 | ) 10 | 11 | // cleanupRecords identifies outdated DNS records and deletes them from the DNS provider. 12 | // This method is locked to prevent concurrent modification of shared resources. 13 | func (app *App) cleanupRecords() error { 14 | app.Lock() // Lock to prevent concurrent modifications 15 | defer app.Unlock() // Unlock when function execution is finished 16 | 17 | app.lo.Info("Starting cleanup of DNS records") 18 | 19 | // Fetch all DNS records owned by this program 20 | recordsMap, err := app.fetchRecords() 21 | if err != nil { 22 | return fmt.Errorf("error fetching records: %w", err) 23 | } 24 | 25 | // Identify records that are outdated i.e., not present in the current service list 26 | outdatedRecords := identifyOutdatedRecords(app.services, recordsMap) 27 | app.lo.Info("Identified outdated records", "count", len(outdatedRecords), "records", outdatedRecords) 28 | 29 | // Delete the outdated records from the DNS provider. 30 | if len(outdatedRecords) > 0 { 31 | if err := app.deleteOutdatedRecords(outdatedRecords, recordsMap); err != nil { 32 | return fmt.Errorf("error deleting outdated records: %w", err) 33 | } 34 | } 35 | 36 | return nil 37 | } 38 | 39 | // identifyOutdatedRecords compares the current service list with the DNS records and identifies which records are outdated. 40 | func identifyOutdatedRecords(services map[string]ServiceMeta, recordsMap map[string][]RecordMeta) []string { 41 | outdatedRecords := make([]string, 0) 42 | for recordName := range recordsMap { 43 | if _, exists := services[recordName]; !exists { 44 | outdatedRecords = append(outdatedRecords, recordName) 45 | } 46 | } 47 | return outdatedRecords 48 | } 49 | 50 | // fetchRecords retrieves all records from the DNS provider and filters ones that are owned by this program. 51 | // It groups the owned records by domain name. 52 | func (app *App) fetchRecords() (map[string][]RecordMeta, error) { 53 | ownedRecords := make(map[string][]RecordMeta) 54 | 55 | // Iterate over all configured domains 56 | for _, domain := range app.opts.domains { 57 | zone := EnsureFQDN(domain) 58 | 59 | // Get all DNS records for this zone 60 | records, err := app.provider.GetRecords(context.Background(), zone) 61 | if err != nil { 62 | return nil, fmt.Errorf("error fetching records for zone %s: %w", zone, err) 63 | } 64 | 65 | // Filter out records that are not owned by this program 66 | recordNames := filterOwnedRecords(records, app.opts.owner) 67 | 68 | // Build a map of owned records grouped by the record name 69 | // For A records, if multiple records exist for the same name, their values are concatenated 70 | groupOwnedRecords(&ownedRecords, records, recordNames, zone) 71 | } 72 | 73 | return ownedRecords, nil 74 | } 75 | 76 | // filterOwnedRecords iterates over all records and returns a slice of names of records that are owned by this program. 77 | func filterOwnedRecords(records []libdns.Record, owner string) []string { 78 | recordNames := make([]string, 0) 79 | for _, r := range records { 80 | if r.Type == "TXT" && strings.Contains(r.Value, fmt.Sprintf("owner=%s", owner)) { 81 | recordNames = append(recordNames, r.Name) 82 | } 83 | } 84 | return recordNames 85 | } 86 | 87 | // groupOwnedRecords groups the owned records by their name. For A records with the same name, their values are concatenated. 88 | func groupOwnedRecords(ownedRecords *map[string][]RecordMeta, records []libdns.Record, recordNames []string, zone string) { 89 | for _, rec := range records { 90 | if Contains(recordNames, rec.Name) { 91 | dns := rec.Name 92 | rec.Name = strings.TrimSuffix(rec.Name, zone) // Overwrite to set a proper relative name before we delete. 93 | 94 | // If an A record with the same name already exists, append the IP address to the existing record. 95 | if rec.Type == "A" && len((*ownedRecords)[dns]) > 0 { 96 | (*ownedRecords)[dns][0].Records[0].Value += "," + rec.Value 97 | } else { 98 | (*ownedRecords)[dns] = append((*ownedRecords)[dns], RecordMeta{Zone: EnsureFQDN(zone), Records: []libdns.Record{rec}}) 99 | } 100 | } 101 | } 102 | } 103 | 104 | // deleteOutdatedRecords removes the outdated DNS records from the DNS provider. 105 | func (app *App) deleteOutdatedRecords(outdatedRecords []string, recordsMap map[string][]RecordMeta) error { 106 | app.lo.Info("Starting deletion of outdated DNS records", "count", len(outdatedRecords)) 107 | 108 | // Iterate over all outdated records 109 | for _, record := range outdatedRecords { 110 | recordMeta, exists := recordsMap[record] 111 | if !exists { 112 | // This is unlikely to happen but we skip to the next iteration just in case 113 | continue 114 | } 115 | 116 | // Delete each outdated record 117 | for _, meta := range recordMeta { 118 | _, err := app.provider.DeleteRecords(context.Background(), EnsureFQDN(meta.Zone), meta.Records) 119 | if err != nil { 120 | app.lo.Error("Error deleting records", "error", err) 121 | continue 122 | } 123 | 124 | app.lo.Info("Deleted record successfully", "zone", meta.Zone, "records", meta.Records) 125 | } 126 | } 127 | 128 | return nil 129 | } 130 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 5 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 6 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 7 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 8 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 9 | github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 10 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 11 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 12 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 13 | github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 14 | github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= 15 | github.com/aws/aws-sdk-go-v2 v1.18.1 h1:+tefE750oAb7ZQGzla6bLkOwfcQCEtC5y2RqoqCeqKo= 16 | github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= 17 | github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= 18 | github.com/aws/aws-sdk-go-v2/config v1.18.27 h1:Az9uLwmssTE6OGTpsFqOnaGpLnKDqNYOJzWuC6UAYzA= 19 | github.com/aws/aws-sdk-go-v2/config v1.18.27/go.mod h1:0My+YgmkGxeqjXZb5BYme5pc4drjTnM+x1GJ3zv42Nw= 20 | github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= 21 | github.com/aws/aws-sdk-go-v2/credentials v1.13.26 h1:qmU+yhKmOCyujmuPY7tf5MxR/RKyZrOPO3V4DobiTUk= 22 | github.com/aws/aws-sdk-go-v2/credentials v1.13.26/go.mod h1:GoXt2YC8jHUBbA4jr+W3JiemnIbkXOfxSXcisUsZ3os= 23 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= 24 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 h1:LxK/bitrAr4lnh9LnIS6i7zWbCOdMsfzKFBI6LUCS0I= 25 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4/go.mod h1:E1hLXN/BL2e6YizK1zFlYd8vsfi2GTjbjBazinMmeaM= 26 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 h1:A5UqQEmPaCFpedKouS4v+dHCTUo2sKqhoKO9U5kxyWo= 27 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34/go.mod h1:wZpTEecJe0Btj3IYnDx/VlUzor9wm3fJHyvLpQF0VwY= 28 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 h1:srIVS45eQuewqz6fKKu6ZGXaq6FuFg5NzgQBAM6g8Y4= 29 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28/go.mod h1:7VRpKQQedkfIEXb4k52I7swUnZP0wohVajJMRn3vsUw= 30 | github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= 31 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 h1:LWA+3kDM8ly001vJ1X1waCuLJdtTl48gwkPKWy9sosI= 32 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35/go.mod h1:0Eg1YjxE0Bhn56lx+SHJwCzhW+2JGtizsrx+lCqrfm0= 33 | github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= 34 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= 35 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 h1:bkRyG4a929RCnpVSTvLM2j/T4ls015ZhhYApbmYs15s= 36 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28/go.mod h1:jj7znCIg05jXlaGBlFMGP8+7UN3VtCkRBG2spnmRQkU= 37 | github.com/aws/aws-sdk-go-v2/service/route53 v1.28.3 h1:nJbE4+tHd8xpM1RB1ZF0/xTJnFd/ATz42ZC35lwXx0w= 38 | github.com/aws/aws-sdk-go-v2/service/route53 v1.28.3/go.mod h1:Cd4MnFoV+6fELBrgWXJ4Y09FrSkn/VjNPkOr1Yr1muU= 39 | github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= 40 | github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 h1:nneMBM2p79PGWBQovYO/6Xnc2ryRMw3InnDJq1FHkSY= 41 | github.com/aws/aws-sdk-go-v2/service/sso v1.12.12/go.mod h1:HuCOxYsF21eKrerARYO6HapNeh9GBNq7fius2AcwodY= 42 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 h1:2qTR7IFk7/0IN/adSFhYu9Xthr0zVFTgBrmPldILn80= 43 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12/go.mod h1:E4VrHCPzmVB/KFXtqBGKb3c8zpbNBgKe3fisDNLAW5w= 44 | github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= 45 | github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 h1:XFJ2Z6sNUUcAz9poj+245DMkrHE4h2j5I9/xD50RHfE= 46 | github.com/aws/aws-sdk-go-v2/service/sts v1.19.2/go.mod h1:dp0yLPsLBOi++WTxzCjA/oZqi6NPIhoR+uF7GeMU9eg= 47 | github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= 48 | github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= 49 | github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= 50 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 51 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 52 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 53 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 54 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 55 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 56 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 57 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 58 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 59 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 60 | github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 61 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 62 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 63 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 64 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 65 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 66 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 67 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 68 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 69 | github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 70 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 71 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 72 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 73 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 74 | github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= 75 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 76 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 77 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 78 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 79 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 80 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 81 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 82 | github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= 83 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 84 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 85 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 86 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 87 | github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= 88 | github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 89 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 90 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 91 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 92 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 93 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 94 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 95 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 96 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 97 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 98 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 99 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 100 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 101 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 102 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 103 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 104 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 105 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 106 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 107 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 108 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 109 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 110 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 111 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 112 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 113 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 114 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 115 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 116 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 117 | github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 118 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 119 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 120 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 121 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 122 | github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= 123 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 124 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 125 | github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 126 | github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= 127 | github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= 128 | github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= 129 | github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= 130 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 131 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 132 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 133 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 134 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 135 | github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 136 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 137 | github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= 138 | github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= 139 | github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= 140 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 141 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 142 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 143 | github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= 144 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 145 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 146 | github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= 147 | github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= 148 | github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= 149 | github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= 150 | github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= 151 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 152 | github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= 153 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 154 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 155 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 156 | github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 157 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 158 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 159 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 160 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 161 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 162 | github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= 163 | github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= 164 | github.com/hashicorp/nomad/api v0.0.0-20230627233251-f3df01e4220d h1:qVcniwJnlI9Ut1sw7/pOSzgjzA2xL6ExyUYISXqn+n8= 165 | github.com/hashicorp/nomad/api v0.0.0-20230627233251-f3df01e4220d/go.mod h1:Xjd3OXUTfsWbCCBsQd3EdfPTz5evDi+fxqdvpN+WqQg= 166 | github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= 167 | github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= 168 | github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= 169 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 170 | github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 171 | github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= 172 | github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= 173 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 174 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 175 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 176 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 177 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 178 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 179 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 180 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 181 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 182 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 183 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 184 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 185 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 186 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 187 | github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= 188 | github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= 189 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 190 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 191 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 192 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 193 | github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= 194 | github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 195 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 196 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 197 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 198 | github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= 199 | github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= 200 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 201 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 202 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 203 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 204 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 205 | github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= 206 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 207 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 208 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 209 | github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= 210 | github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= 211 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 212 | github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= 213 | github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 214 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 215 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 216 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 217 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 218 | github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 219 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 220 | github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= 221 | github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 222 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 223 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 224 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 225 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 226 | github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 227 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 228 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 229 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 230 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 231 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 232 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 233 | github.com/mr-karan/libdns-route53 v1.4.0 h1:eCD0HeONe3FEG81MgSjcD42QFOYQvc/uE8N4hY9YQbs= 234 | github.com/mr-karan/libdns-route53 v1.4.0/go.mod h1:cOtC6S7LijHQDikOSNr3kGOCWecvU4p7iB0kgF1Febw= 235 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 236 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 237 | github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= 238 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 239 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 240 | github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 241 | github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= 242 | github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= 243 | github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 244 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 245 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 246 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 247 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 248 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 249 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 250 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 251 | github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= 252 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 253 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 254 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 255 | github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 256 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 257 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 258 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 259 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 260 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 261 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 262 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 263 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 264 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 265 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 266 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 267 | github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= 268 | github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 269 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 270 | github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 271 | github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= 272 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 273 | github.com/shoenig/test v0.6.6 h1:Oe8TPH9wAbv++YPNDKJWUnI8Q4PPWCx3UbOfH+FxiMU= 274 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 275 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 276 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 277 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 278 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 279 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 280 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 281 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 282 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 283 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 284 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 285 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 286 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 287 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 288 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 289 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 290 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 291 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 292 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 293 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 294 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 295 | go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= 296 | go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 297 | go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= 298 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 299 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 300 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 301 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 302 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 303 | golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= 304 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 305 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 306 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 307 | golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= 308 | golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= 309 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 310 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 311 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 312 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 313 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 314 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 315 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 316 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 317 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 318 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 319 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 320 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 321 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 322 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 323 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 324 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 325 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 326 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 327 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 328 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 329 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 330 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 331 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 332 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 333 | golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= 334 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 335 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 336 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 337 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 338 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 339 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 340 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 341 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 342 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 343 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 344 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 345 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 346 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 347 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 348 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 349 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 350 | golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 351 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 352 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 353 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 354 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 355 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 356 | golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 357 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 358 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 359 | golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 360 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 361 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 362 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 363 | golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 364 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 365 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 366 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 367 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 368 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 369 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 370 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 371 | golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 372 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 373 | golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 374 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 375 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 376 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 377 | golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= 378 | golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 379 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 380 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 381 | golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 382 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 383 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 384 | golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 385 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 386 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 387 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 388 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 389 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 390 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 391 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 392 | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 393 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 394 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 395 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 396 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 397 | golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 398 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 399 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 400 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 401 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 402 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 403 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 404 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 405 | google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 406 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 407 | google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 408 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 409 | google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 410 | google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 411 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 412 | google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 413 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 414 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 415 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 416 | google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 417 | google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 418 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 419 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 420 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 421 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 422 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 423 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 424 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 425 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 426 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 427 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 428 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 429 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 430 | gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= 431 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 432 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 433 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 434 | gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= 435 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 436 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 437 | gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 438 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 439 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 440 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 441 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 442 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 443 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 444 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 445 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 446 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 447 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 448 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 449 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 450 | sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= 451 | --------------------------------------------------------------------------------