Commit 5df8d801 authored by Murphy Law's avatar Murphy Law Committed by GitHub

Merge pull request #2542 from ethereum-optimism/develop

Develop -> Master
parents e00dba09 44c293d8
---
'@eth-optimism/teleportr': patch
---
Fix confs_remaining calculation to prevent underflow
---
'@eth-optimism/integration-tests': patch
'@eth-optimism/contracts': patch
'@eth-optimism/data-transport-layer': patch
'@eth-optimism/message-relayer': patch
---
Replace calls to getNetwork() with getChainId util
......@@ -199,13 +199,13 @@ jobs:
- run:
name: Lint
command: golangci-lint run -E goimports -E sqlclosecheck -E bodyclose -E asciicheck ./...
working_directory: go/bss-core
working_directory: bss-core
- run:
name: Test
command: |
mkdir -p /test-results
gotestsum --junitfile /test-results/tests.xml
working_directory: go/bss-core
working_directory: bss-core
- store_test_results:
path: /test-results
......@@ -313,26 +313,26 @@ workflows:
- go-lint-test-build:
name: batch-submitter-tests
binary_name: batch-submitter
working_directory: go/batch-submitter
working_directory: batch-submitter
- go-lint-test-build:
name: proxyd-tests
binary_name: proxyd
working_directory: go/proxyd
working_directory: proxyd
- go-lint-test-build:
name: teleportr-tests
binary_name: teleportr
working_directory: go/teleportr
working_directory: teleportr
- go-lint-test-build:
name: gas-oracle-tests
binary_name: gas-oracle
working_directory: go/gas-oracle
working_directory: gas-oracle
- go-lint-test-build:
name: indexer-tests
binary_name: indexer
working_directory: go/indexer
working_directory: indexer
- go-lint-test:
name: bss-core-tests
working_directory: go/bss-core
working_directory: bss-core
- contracts-tests:
requires:
- yarn-monorepo
......@@ -379,14 +379,14 @@ workflows:
- yarn-monorepo
- docker-publish:
name: l2geth-release
docker_file: ops/docker/Dockerfile.geth
docker_file: l2geth/Dockerfile
docker_tags: ethereumoptimism/l2geth:nightly
docker_context: .
context:
- optimism
- docker-publish:
name: gas-oracle-release
docker_file: ops/docker/Dockerfile.gas-oracle
docker_file: gas-oracle/Dockerfile
docker_tags: ethereumoptimism/gas-oracle:nightly
docker_context: .
context:
......@@ -414,21 +414,21 @@ workflows:
- optimism
- docker-publish:
name: proxyd-release
docker_file: go/proxyd/Dockerfile
docker_file: proxyd/Dockerfile
docker_tags: ethereumoptimism/proxyd:nightly
docker_context: .
context:
- optimism
- docker-publish:
name: l2geth-exporter-release
docker_file: ops/docker/Dockerfile.l2geth-exporter
docker_file: l2geth-exporter/Dockerfile
docker_tags: ethereumoptimism/l2geth-exporter:nightly
docker_context: .
context:
- optimism
- docker-publish:
name: op-exporter-release
docker_file: ops/docker/Dockerfile.op-exporter
docker_file: op-exporter/Dockerfile
docker_tags: ethereumoptimism/op-exporter:nightly
docker_context: .
context:
......@@ -467,21 +467,21 @@ workflows:
- optimism
- docker-publish:
name: batch-submitter-service-release
docker_file: ops/docker/Dockerfile.batch-submitter-service
docker_file: ops/docker/Dockerfile
docker_tags: ethereumoptimism/batch-submitter-service:nightly
docker_context: .
context:
- optimism
- docker-publish:
name: indexer-release
docker_file: ops/docker/Dockerfile.indexer
docker_file: indexer/Dockerfile
docker_tags: ethereumoptimism/indexer:nightly
docker_context: .
context:
- optimism
- docker-publish:
name: teleportr-release
docker_file: ops/docker/Dockerfile.teleportr
docker_file: teleportr/Dockerfile
docker_tags: ethereumoptimism/teleportr:nightly
docker_context: .
context:
......
......@@ -113,7 +113,7 @@ jobs:
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.geth
file: ./l2geth/Dockerfile
push: true
tags: ethereumoptimism/l2geth:${{ needs.canary-publish.outputs.canary-docker-tag }}
......@@ -139,7 +139,7 @@ jobs:
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.gas-oracle
file: ./gas-oracle/Dockerfile
push: true
tags: ethereumoptimism/gas-oracle:${{ needs.canary-publish.outputs.canary-docker-tag }}
......@@ -326,14 +326,14 @@ jobs:
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./go/proxyd/package.json)
echo ::set-output name=GITVERSION::$(jq -r .version ./proxyd/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./go/proxyd/Dockerfile
file: ./proxyd/Dockerfile
push: true
tags: ethereumoptimism/proxyd:${{ needs.canary-publish.outputs.proxyd }}
build-args: |
......@@ -363,14 +363,14 @@ jobs:
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./go/op-exporter/package.json)
echo ::set-output name=GITVERSION::$(jq -r .version ./op-exporter/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.op-exporter
file: ./op-exporter/Dockerfile=
push: true
tags: ethereumoptimism/op-exporter:${{ needs.canary-publish.outputs.op-exporter }}
build-args: |
......@@ -400,14 +400,14 @@ jobs:
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./go/l2geth-exporter/package.json)
echo ::set-output name=GITVERSION::$(jq -r .version ./l2geth-exporter/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.l2geth-exporter
file: ./l2geth-exporter/Dockerfile
push: true
tags: ethereumoptimism/l2geth-exporter:${{ needs.canary-publish.outputs.l2geth-exporter }}
build-args: |
......@@ -437,7 +437,7 @@ jobs:
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.batch-submitter-service
file: ./batch-submitter/Dockerfile
push: true
tags: ethereumoptimism/batch-submitter-service:${{ needs.canary-publish.outputs.batch-submitter-service }}
......@@ -463,14 +463,14 @@ jobs:
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./go/indexer/package.json)
echo ::set-output name=GITVERSION::$(jq -r .version ./indexer/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.indexer
file: ./indexer/Dockerfile
push: true
tags: ethereumoptimism/indexer:${{ needs.canary-publish.outputs.indexer }}
build-args: |
......@@ -500,14 +500,14 @@ jobs:
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./go/teleportr/package.json)
echo ::set-output name=GITVERSION::$(jq -r .version ./teleportr/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.teleportr
file: ./teleportr/Dockerfile
push: true
tags: ethereumoptimism/teleportr:${{ needs.canary-publish.outputs.teleportr }}
build-args: |
......
......@@ -93,7 +93,7 @@ jobs:
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.geth
file: ./ops/docker/Dockerfile
push: true
tags: ethereumoptimism/l2geth:${{ needs.release.outputs.l2geth }},ethereumoptimism/l2geth:latest
......@@ -119,7 +119,7 @@ jobs:
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.gas-oracle
file: ./gas-oracle/Dockerfile
push: true
tags: ethereumoptimism/gas-oracle:${{ needs.release.outputs.gas-oracle }},ethereumoptimism/gas-oracle:latest
......@@ -223,14 +223,14 @@ jobs:
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./go/proxyd/package.json)
echo ::set-output name=GITVERSION::$(jq -r .version ./proxyd/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./go/proxyd/Dockerfile
file: ./proxyd/Dockerfile
push: true
tags: ethereumoptimism/proxyd:${{ needs.release.outputs.proxyd }},ethereumoptimism/proxyd:latest
build-args: |
......@@ -260,14 +260,14 @@ jobs:
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./go/l2geth-exporter/package.json)
echo ::set-output name=GITVERSION::$(jq -r .version ./l2geth-exporter/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.l2geth-exporter
file: ./l2geth-exporter/Dockerfile
push: true
tags: ethereumoptimism/l2geth-exporter:${{ needs.release.outputs.l2geth-exporter }},ethereumoptimism/l2geth-exporter:latest
build-args: |
......@@ -297,14 +297,14 @@ jobs:
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./go/op-exporter/package.json)
echo ::set-output name=GITVERSION::$(jq -r .version ./op-exporter/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.op-exporter
file: ./op-exporter/Dockerfile
push: true
tags: ethereumoptimism/op-exporter:${{ needs.release.outputs.op-exporter }},ethereumoptimism/op-exporter:latest
build-args: |
......@@ -469,7 +469,7 @@ jobs:
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.batch-submitter-service
file: ./batch-submitter/Dockerfile
push: true
tags: ethereumoptimism/batch-submitter-service:${{ needs.release.outputs.batch-submitter-service }},ethereumoptimism/batch-submitter-service:latest
......@@ -495,14 +495,14 @@ jobs:
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./go/indexer/package.json)
echo ::set-output name=GITVERSION::$(jq -r .version ./indexer/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Publish Indexer
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.indexer
file: ./indexer/Dockerfile
push: true
tags: ethereumoptimism/indexer:${{ needs.release.outputs.indexer }},ethereumoptimism/indexer:latest
build-args: |
......@@ -532,14 +532,14 @@ jobs:
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./go/teleportr/package.json)
echo ::set-output name=GITVERSION::$(jq -r .version ./teleportr/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Publish Teleportr
uses: docker/build-push-action@v2
with:
context: .
file: ./ops/docker/Dockerfile.teleportr
file: ./teleportr/Dockerfile
push: true
tags: ethereumoptimism/teleportr:${{ needs.release.outputs.teleportr }},ethereumoptimism/teleportr:latest
build-args: |
......
......@@ -38,10 +38,10 @@ root
│ ├── <a href="./packages/message-relayer">message-relayer</a>: Tool for automatically relaying L1<>L2 messages in development
│ └── <a href="./packages/replica-healthcheck">replica-healthcheck</a>: Service for monitoring the health of a replica node
├── <a href="./go">go</a>
│ ├── <a href="./go/batch-submitter">batch-submitter</a>: Service for submitting batches of transactions and results to L1
│ ├── <a href="./go/bss-core">bss-core</a>: Core batch-submitter logic and utilities
│ ├── <a href="./go/gas-oracle">gas-oracle</a>: Service for updating L1 gas prices on L2
│ └── <a href="./go/proxyd">proxyd</a>: Configurable RPC request router and proxy
│ ├── <a href="./batch-submitter">batch-submitter</a>: Service for submitting batches of transactions and results to L1
│ ├── <a href="./bss-core">bss-core</a>: Core batch-submitter logic and utilities
│ ├── <a href="./gas-oracle">gas-oracle</a>: Service for updating L1 gas prices on L2
│ └── <a href="./proxyd">proxyd</a>: Configurable RPC request router and proxy
├── <a href="./l2geth">l2geth</a>: Optimism client software, a fork of <a href="https://github.com/ethereum/go-ethereum/tree/v1.9.10">geth v1.9.10</a>
├── <a href="./integration-tests">integration-tests</a>: Various integration tests for the Optimism network
└── <a href="./ops">ops</a>: Tools for running Optimism nodes and networks
......
......@@ -2,12 +2,12 @@ FROM golang:1.18.0-alpine3.15 as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash
COPY ./l2geth /l2geth
COPY ./go/bss-core /go/bss-core
COPY ./go/batch-submitter/go.mod ./go/batch-submitter/go.sum /go/batch-submitter/
COPY ./batch-submitter /go/batch-submitter
COPY ./bss-core /go/bss-core
COPY ./l2geth /go/l2geth
COPY ./batch-submitter/docker.go.work /go/go.work
WORKDIR /go/batch-submitter
RUN go mod graph | grep -v l2geth | grep -v bss-core | awk '{if ($1 !~ "@") print $2}' | xargs -n 1 go get
COPY ./go/batch-submitter/ ./
RUN make
FROM alpine:3.15
......
......@@ -5,12 +5,12 @@ import (
"os"
"time"
"github.com/ethereum-optimism/optimism/go/batch-submitter/drivers/proposer"
"github.com/ethereum-optimism/optimism/go/batch-submitter/drivers/sequencer"
bsscore "github.com/ethereum-optimism/optimism/go/bss-core"
"github.com/ethereum-optimism/optimism/go/bss-core/dial"
"github.com/ethereum-optimism/optimism/go/bss-core/metrics"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/batch-submitter/drivers/proposer"
"github.com/ethereum-optimism/optimism/batch-submitter/drivers/sequencer"
bsscore "github.com/ethereum-optimism/optimism/bss-core"
"github.com/ethereum-optimism/optimism/bss-core/dial"
"github.com/ethereum-optimism/optimism/bss-core/metrics"
"github.com/ethereum-optimism/optimism/bss-core/txmgr"
"github.com/ethereum/go-ethereum/log"
"github.com/getsentry/sentry-go"
"github.com/urfave/cli"
......
......@@ -8,8 +8,8 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/urfave/cli"
batchsubmitter "github.com/ethereum-optimism/optimism/go/batch-submitter"
"github.com/ethereum-optimism/optimism/go/batch-submitter/flags"
batchsubmitter "github.com/ethereum-optimism/optimism/batch-submitter"
"github.com/ethereum-optimism/optimism/batch-submitter/flags"
)
var (
......
......@@ -7,7 +7,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/go/batch-submitter/flags"
"github.com/ethereum-optimism/optimism/batch-submitter/flags"
)
var (
......
......@@ -4,7 +4,7 @@ import (
"fmt"
"testing"
batchsubmitter "github.com/ethereum-optimism/optimism/go/batch-submitter"
batchsubmitter "github.com/ethereum-optimism/optimism/batch-submitter"
"github.com/stretchr/testify/require"
)
......
......@@ -6,7 +6,7 @@ import (
"net/http"
"strings"
"github.com/ethereum-optimism/optimism/go/bss-core/dial"
"github.com/ethereum-optimism/optimism/bss-core/dial"
"github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum-optimism/optimism/l2geth/log"
"github.com/ethereum-optimism/optimism/l2geth/rpc"
......
go 1.18
use (
./batch-submitter
./bss-core
./l2geth
)
......@@ -7,11 +7,11 @@ import (
"math/big"
"strings"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/scc"
"github.com/ethereum-optimism/optimism/go/bss-core/drivers"
"github.com/ethereum-optimism/optimism/go/bss-core/metrics"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/batch-submitter/bindings/ctc"
"github.com/ethereum-optimism/optimism/batch-submitter/bindings/scc"
"github.com/ethereum-optimism/optimism/bss-core/drivers"
"github.com/ethereum-optimism/optimism/bss-core/metrics"
"github.com/ethereum-optimism/optimism/bss-core/txmgr"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum-optimism/optimism/l2geth/log"
"github.com/ethereum/go-ethereum/accounts/abi"
......
......@@ -4,7 +4,7 @@ import (
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/go/batch-submitter/drivers/sequencer"
"github.com/ethereum-optimism/optimism/batch-submitter/drivers/sequencer"
l2common "github.com/ethereum-optimism/optimism/l2geth/common"
l2types "github.com/ethereum-optimism/optimism/l2geth/core/types"
"github.com/stretchr/testify/require"
......
......@@ -7,10 +7,10 @@ import (
"math/big"
"strings"
"github.com/ethereum-optimism/optimism/go/batch-submitter/bindings/ctc"
"github.com/ethereum-optimism/optimism/go/bss-core/drivers"
"github.com/ethereum-optimism/optimism/go/bss-core/metrics"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/batch-submitter/bindings/ctc"
"github.com/ethereum-optimism/optimism/bss-core/drivers"
"github.com/ethereum-optimism/optimism/bss-core/metrics"
"github.com/ethereum-optimism/optimism/bss-core/txmgr"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
......
......@@ -7,7 +7,7 @@ import (
"os"
"testing"
"github.com/ethereum-optimism/optimism/go/batch-submitter/drivers/sequencer"
"github.com/ethereum-optimism/optimism/batch-submitter/drivers/sequencer"
l2types "github.com/ethereum-optimism/optimism/l2geth/core/types"
l2rlp "github.com/ethereum-optimism/optimism/l2geth/rlp"
"github.com/stretchr/testify/require"
......
package sequencer
import (
"github.com/ethereum-optimism/optimism/go/bss-core/metrics"
"github.com/ethereum-optimism/optimism/bss-core/metrics"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
......
module github.com/ethereum-optimism/optimism/go/batch-submitter
module github.com/ethereum-optimism/optimism/batch-submitter
go 1.16
replace github.com/ethereum-optimism/optimism/bss-core v0.0.0 => ../bss-core
replace github.com/ethereum-optimism/optimism/l2geth v0.0.0 => ../l2geth
require (
github.com/ethereum-optimism/optimism/go/bss-core v0.0.0
github.com/ethereum-optimism/optimism/l2geth v1.0.0
github.com/ethereum-optimism/optimism/bss-core v0.0.0
github.com/ethereum-optimism/optimism/l2geth v0.0.0
github.com/ethereum/go-ethereum v1.10.16
github.com/getsentry/sentry-go v0.11.0
github.com/prometheus/client_golang v1.11.0
github.com/stretchr/testify v1.7.0
github.com/urfave/cli v1.22.5
)
replace github.com/ethereum-optimism/optimism/l2geth => ../../l2geth
replace github.com/ethereum-optimism/optimism/go/bss-core => ../bss-core
......@@ -6,7 +6,7 @@ import (
"strings"
"testing"
bsscore "github.com/ethereum-optimism/optimism/go/bss-core"
bsscore "github.com/ethereum-optimism/optimism/bss-core"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
......
......@@ -7,7 +7,7 @@ import (
"math/big"
"strings"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/bss-core/txmgr"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
......
......@@ -8,9 +8,9 @@ import (
"testing"
"time"
"github.com/ethereum-optimism/optimism/go/bss-core/drivers"
"github.com/ethereum-optimism/optimism/go/bss-core/mock"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/bss-core/drivers"
"github.com/ethereum-optimism/optimism/bss-core/mock"
"github.com/ethereum-optimism/optimism/bss-core/txmgr"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
......
module github.com/ethereum-optimism/optimism/go/bss-core
module github.com/ethereum-optimism/optimism/bss-core
go 1.16
......
......@@ -7,8 +7,8 @@ import (
"sync"
"time"
"github.com/ethereum-optimism/optimism/go/bss-core/metrics"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/bss-core/metrics"
"github.com/ethereum-optimism/optimism/bss-core/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
......
......@@ -4,7 +4,7 @@ import (
"errors"
"testing"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/bss-core/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/stretchr/testify/require"
......
......@@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/bss-core/txmgr"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
......
......@@ -2,7 +2,8 @@ FROM golang:1.18.0-alpine3.15 as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash
COPY ./go/gas-oracle /gas-oracle
COPY ./gas-oracle /gas-oracle
RUN cd /gas-oracle && make gas-oracle
FROM alpine:3.15
......
......@@ -13,6 +13,7 @@ CONTRACTS_PATH := "../../packages/contracts/artifacts/contracts"
gas-oracle:
env GO111MODULE=on go build $(LDFLAGS)
.PHONY: gas-oracle
clean:
rm gas-oracle
......
module github.com/ethereum-optimism/optimism/go/gas-oracle
module github.com/ethereum-optimism/optimism/gas-oracle
go 1.16
......
......@@ -5,9 +5,9 @@ import (
"os"
"time"
"github.com/ethereum-optimism/optimism/go/gas-oracle/flags"
ometrics "github.com/ethereum-optimism/optimism/go/gas-oracle/metrics"
"github.com/ethereum-optimism/optimism/go/gas-oracle/oracle"
"github.com/ethereum-optimism/optimism/gas-oracle/flags"
ometrics "github.com/ethereum-optimism/optimism/gas-oracle/metrics"
"github.com/ethereum-optimism/optimism/gas-oracle/oracle"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics/influxdb"
"github.com/ethereum/go-ethereum/params"
......
......@@ -4,7 +4,7 @@ import (
"context"
"fmt"
"github.com/ethereum-optimism/optimism/go/gas-oracle/bindings"
"github.com/ethereum-optimism/optimism/gas-oracle/bindings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
......
......@@ -4,7 +4,7 @@ import (
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/go/gas-oracle/bindings"
"github.com/ethereum-optimism/optimism/gas-oracle/bindings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
......
......@@ -6,7 +6,7 @@ import (
"math/big"
"strings"
"github.com/ethereum-optimism/optimism/go/gas-oracle/flags"
"github.com/ethereum-optimism/optimism/gas-oracle/flags"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
......
......@@ -7,8 +7,8 @@ import (
"math/big"
"time"
"github.com/ethereum-optimism/optimism/go/gas-oracle/bindings"
"github.com/ethereum-optimism/optimism/go/gas-oracle/gasprices"
"github.com/ethereum-optimism/optimism/gas-oracle/bindings"
"github.com/ethereum-optimism/optimism/gas-oracle/gasprices"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/crypto"
......
......@@ -6,8 +6,8 @@ import (
"math/big"
"time"
"github.com/ethereum-optimism/optimism/go/gas-oracle/bindings"
ometrics "github.com/ethereum-optimism/optimism/go/gas-oracle/metrics"
"github.com/ethereum-optimism/optimism/gas-oracle/bindings"
ometrics "github.com/ethereum-optimism/optimism/gas-oracle/metrics"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common/hexutil"
......
......@@ -6,7 +6,7 @@ import (
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/go/gas-oracle/bindings"
"github.com/ethereum-optimism/optimism/gas-oracle/bindings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/core"
......
go 1.18
use (
./batch-submitter
./bss-core
./gas-oracle
./indexer
./l2geth
./l2geth-exporter
./op-exporter
./proxyd
./teleportr
)
replace github.com/ethereum-optimism/optimism/bss-core v0.0.0 => ./bss-core
replace github.com/ethereum-optimism/optimism/l2geth v0.0.0 => ./l2geth
# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
# Ignore build and test binaries.
bin/
testbin/
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
bin
testbin/*
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Kubernetes Generated files - skip generated files, except for vendored files
!vendor/**/zz_generated.*
# editor and IDE paraphernalia
.idea
*.swp
*.swo
*~
# Build the manager binary
FROM golang:1.16 as builder
WORKDIR /workspace
# Copy the Go Modules manifests
COPY go.mod go.mod
COPY go.sum go.sum
# cache deps before building and copying source so that we don't need to re-download as much
# and so that source changes don't invalidate our downloaded layer
RUN go mod download
# Copy the go source
COPY main.go main.go
COPY api/ api/
COPY controllers/ controllers/
# Build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go
# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM gcr.io/distroless/static:nonroot
WORKDIR /
COPY --from=builder /workspace/manager .
USER 65532:65532
ENTRYPOINT ["/manager"]
# VERSION defines the project version for the bundle.
# Update this value when you upgrade the version of your project.
# To re-generate a bundle for another specific version without changing the standard setup, you can:
# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)
# - use environment variables to overwrite this value (e.g export VERSION=0.0.2)
VERSION ?= 0.0.1
# CHANNELS define the bundle channels used in the bundle.
# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable")
# To re-generate a bundle for other specific channels without changing the standard setup, you can:
# - use the CHANNELS as arg of the bundle target (e.g make bundle CHANNELS=candidate,fast,stable)
# - use environment variables to overwrite this value (e.g export CHANNELS="candidate,fast,stable")
ifneq ($(origin CHANNELS), undefined)
BUNDLE_CHANNELS := --channels=$(CHANNELS)
endif
# DEFAULT_CHANNEL defines the default channel used in the bundle.
# Add a new line here if you would like to change its default config. (E.g DEFAULT_CHANNEL = "stable")
# To re-generate a bundle for any other default channel without changing the default setup, you can:
# - use the DEFAULT_CHANNEL as arg of the bundle target (e.g make bundle DEFAULT_CHANNEL=stable)
# - use environment variables to overwrite this value (e.g export DEFAULT_CHANNEL="stable")
ifneq ($(origin DEFAULT_CHANNEL), undefined)
BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL)
endif
BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)
# IMAGE_TAG_BASE defines the docker.io namespace and part of the image name for remote images.
# This variable is used to construct full image tags for bundle and catalog images.
#
# For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both
# optimism-stacks.net/stackman-bundle:$VERSION and optimism-stacks.net/stackman-catalog:$VERSION.
IMAGE_TAG_BASE ?= optimism-stacks.net/stackman
# BUNDLE_IMG defines the image:tag used for the bundle.
# You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=<some-registry>/<project-name-bundle>:<tag>)
BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION)
# Image URL to use all building/pushing image targets
IMG ?= ethereumoptimism/stackman-controller:latest
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.22
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
GOBIN=$(shell go env GOPATH)/bin
else
GOBIN=$(shell go env GOBIN)
endif
# Setting SHELL to bash allows bash commands to be executed by recipes.
# This is a requirement for 'setup-envtest.sh' in the test target.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec
all: build
##@ General
# The help target prints out all targets with their descriptions organized
# beneath their categories. The categories are represented by '##@' and the
# target descriptions by '##'. The awk commands is responsible for reading the
# entire set of makefiles included in this invocation, looking for lines of the
# file as xyz: ## something, and then pretty-format the target and help. Then,
# if there's a line with ##@ something, that gets pretty-printed as a category.
# More info on the usage of ANSI control characters for terminal formatting:
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
##@ Development
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
fmt: ## Run go fmt against code.
go fmt ./...
vet: ## Run go vet against code.
go vet ./...
test: manifests generate fmt vet envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test ./... -coverprofile cover.out
##@ Build
build: generate fmt vet ## Build manager binary.
go build -o bin/manager main.go
run: manifests generate fmt vet ## Run a controller from your host.
go run ./main.go
docker-build: test ## Build docker image with the manager.
docker build -t ${IMG} .
docker-push: ## Push docker image with the manager.
docker push ${IMG}
##@ Deployment
install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
$(KUSTOMIZE) build config/crd | kubectl apply -f -
uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config.
$(KUSTOMIZE) build config/crd | kubectl delete -f -
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
$(KUSTOMIZE) build config/default | kubectl apply -f -
undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config.
$(KUSTOMIZE) build config/default | kubectl delete -f -
CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
controller-gen: ## Download controller-gen locally if necessary.
$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.7.0)
KUSTOMIZE = $(shell pwd)/bin/kustomize
kustomize: ## Download kustomize locally if necessary.
$(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)
ENVTEST = $(shell pwd)/bin/setup-envtest
envtest: ## Download envtest-setup locally if necessary.
$(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest)
# go-get-tool will 'go get' any package $2 and install it to $1.
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
define go-get-tool
@[ -f $(1) ] || { \
set -e ;\
TMP_DIR=$$(mktemp -d) ;\
cd $$TMP_DIR ;\
go mod init tmp ;\
echo "Downloading $(2)" ;\
GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\
rm -rf $$TMP_DIR ;\
}
endef
.PHONY: bundle
bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files.
operator-sdk generate kustomize manifests -q
cd config/manager && $(KUSTOMIZE) edit set image controller=$(IMG)
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
operator-sdk bundle validate ./bundle
.PHONY: bundle-build
bundle-build: ## Build the bundle image.
docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
.PHONY: bundle-push
bundle-push: ## Push the bundle image.
$(MAKE) docker-push IMG=$(BUNDLE_IMG)
.PHONY: opm
OPM = ./bin/opm
opm: ## Download opm locally if necessary.
ifeq (,$(wildcard $(OPM)))
ifeq (,$(shell which opm 2>/dev/null))
@{ \
set -e ;\
mkdir -p $(dir $(OPM)) ;\
OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \
curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.15.1/$${OS}-$${ARCH}-opm ;\
chmod +x $(OPM) ;\
}
else
OPM = $(shell which opm)
endif
endif
# A comma-separated list of bundle images (e.g. make catalog-build BUNDLE_IMGS=example.com/operator-bundle:v0.1.0,example.com/operator-bundle:v0.2.0).
# These images MUST exist in a registry and be pull-able.
BUNDLE_IMGS ?= $(BUNDLE_IMG)
# The image tag given to the resulting catalog image (e.g. make catalog-build CATALOG_IMG=example.com/operator-catalog:v0.2.0).
CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:v$(VERSION)
# Set CATALOG_BASE_IMG to an existing catalog image tag to add $BUNDLE_IMGS to that image.
ifneq ($(origin CATALOG_BASE_IMG), undefined)
FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG)
endif
# Build a catalog image by adding bundle images to an empty catalog using the operator package manager tool, 'opm'.
# This recipe invokes 'opm' in 'semver' bundle add mode. For more information on add modes, see:
# https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator
.PHONY: catalog-build
catalog-build: opm ## Build a catalog image.
$(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT)
# Push the catalog image.
.PHONY: catalog-push
catalog-push: ## Push a catalog image.
$(MAKE) docker-push IMG=$(CATALOG_IMG)
domain: optimism-stacks.net
layout:
- go.kubebuilder.io/v3
plugins:
manifests.sdk.operatorframework.io/v2: {}
scorecard.sdk.operatorframework.io/v2: {}
projectName: stackman
repo: github.com/ethereum-optimism/optimism/go/stackman
resources:
- api:
crdVersion: v1
namespaced: true
controller: true
domain: optimism-stacks.net
group: stack
kind: CliqueL1
path: github.com/ethereum-optimism/optimism/go/stackman/api/v1
version: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: optimism-stacks.net
group: stack
kind: Deployer
path: github.com/ethereum-optimism/optimism/go/stackman/api/v1
version: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: optimism-stacks.net
group: stack
kind: DataTransportLayer
path: github.com/ethereum-optimism/optimism/go/stackman/api/v1
version: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: optimism-stacks.net
group: stack
kind: Sequencer
path: github.com/ethereum-optimism/optimism/go/stackman/api/v1
version: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: optimism-stacks.net
group: stack
kind: BatchSubmitter
path: github.com/ethereum-optimism/optimism/go/stackman/api/v1
version: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: optimism-stacks.net
group: stack
kind: GasOracle
path: github.com/ethereum-optimism/optimism/go/stackman/api/v1
version: v1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: optimism-stacks.net
group: stack
kind: Actor
path: github.com/ethereum-optimism/optimism/go/stackman/api/v1
version: v1
version: "3"
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// ActorSpec defines the desired state of Actor
type ActorSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
Image string `json:"image,omitempty"`
L1URL string `json:"l1_url"`
L2URL string `json:"l2_url"`
PrivateKey *Valuer `json:"private_key,omitempty"`
AddressManagerAddress string `json:"address_manager_address"`
TestFilename string `json:"test_filename,omitempty"`
Concurrency int `json:"concurrency,omitempty"`
RunForMS int `json:"run_for_ms,omitempty"`
RunCount int `json:"run_count,omitempty"`
ThinkTimeMS int `json:"think_time_ms,omitempty"`
Env []corev1.EnvVar `json:"env,omitempty"`
}
// ActorStatus defines the observed state of Actor
type ActorStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
// Actor is the Schema for the actors API
type Actor struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ActorSpec `json:"spec,omitempty"`
Status ActorStatus `json:"status,omitempty"`
}
//+kubebuilder:object:root=true
// ActorList contains a list of Actor
type ActorList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Actor `json:"items"`
}
func init() {
SchemeBuilder.Register(&Actor{}, &ActorList{})
}
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// BatchSubmitterSpec defines the desired state of BatchSubmitter
type BatchSubmitterSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
Image string `json:"image,omitempty"`
L1URL string `json:"l1_url,omitempty"`
L1TimeoutSeconds int `json:"l1_timeout_seconds,omitempty"`
L2URL string `json:"l2_url,omitempty"`
L2TimeoutSeconds int `json:"l2_timeout_seconds,omitempty"`
DeployerURL string `json:"deployer_url,omitempty"`
DeployerTimeoutSeconds int `json:"deployer_timeout_seconds,omitempty"`
Env []corev1.EnvVar `json:"env,omitempty"`
}
// BatchSubmitterStatus defines the observed state of BatchSubmitter
type BatchSubmitterStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
// BatchSubmitter is the Schema for the batchsubmitters API
type BatchSubmitter struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec BatchSubmitterSpec `json:"spec,omitempty"`
Status BatchSubmitterStatus `json:"status,omitempty"`
}
//+kubebuilder:object:root=true
// BatchSubmitterList contains a list of BatchSubmitter
type BatchSubmitterList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []BatchSubmitter `json:"items"`
}
func init() {
SchemeBuilder.Register(&BatchSubmitter{}, &BatchSubmitterList{})
}
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// CliqueL1Spec defines the desired state of CliqueL1
type CliqueL1Spec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
Image string `json:"image,omitempty"`
GenesisFile *Valuer `json:"genesis_file,omitempty"`
SealerPrivateKey *Valuer `json:"sealer_private_key"`
SealerAddress string `json:"sealer_address,omitempty"`
ChainID int `json:"chain_id,omitempty"`
DataPVC *PVCConfig `json:"data_pvc,omitempty"`
AdditionalArgs []string `json:"additional_args,omitempty"`
}
// CliqueL1Status defines the observed state of CliqueL1
type CliqueL1Status struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
// CliqueL1 is the Schema for the cliquel1s API
type CliqueL1 struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec CliqueL1Spec `json:"spec,omitempty"`
Status CliqueL1Status `json:"status,omitempty"`
}
//+kubebuilder:object:root=true
// CliqueL1List contains a list of CliqueL1
type CliqueL1List struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []CliqueL1 `json:"items"`
}
func init() {
SchemeBuilder.Register(&CliqueL1{}, &CliqueL1List{})
}
package v1
import (
"gopkg.in/yaml.v2"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
)
type Valuer struct {
Value string `json:"value,omitempty"`
ValueFrom *corev1.EnvVarSource `json:"value_from,omitempty"`
}
func (v *Valuer) String() string {
out, err := yaml.Marshal(v)
if err != nil {
panic(err)
}
return string(out)
}
func (v *Valuer) EnvVar(name string) corev1.EnvVar {
return corev1.EnvVar{
Name: name,
Value: v.Value,
ValueFrom: v.ValueFrom,
}
}
type PVCConfig struct {
Name string `json:"name"`
Storage *resource.Quantity `json:"storage,omitempty"`
}
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// DataTransportLayerSpec defines the desired state of DataTransportLayer
type DataTransportLayerSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
Image string `json:"image,omitempty"`
L1URL string `json:"l1_url,omitempty"`
L1TimeoutSeconds int `json:"l1_timeout_seconds,omitempty"`
DeployerURL string `json:"deployer_url,omitempty"`
DeployerTimeoutSeconds int `json:"deployer_timeout_seconds,omitempty"`
DataPVC *PVCConfig `json:"data_pvc,omitempty"`
Env []v1.EnvVar `json:"env,omitempty"`
}
// DataTransportLayerStatus defines the observed state of DataTransportLayer
type DataTransportLayerStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
// DataTransportLayer is the Schema for the datatransportlayers API
type DataTransportLayer struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec DataTransportLayerSpec `json:"spec,omitempty"`
Status DataTransportLayerStatus `json:"status,omitempty"`
}
//+kubebuilder:object:root=true
// DataTransportLayerList contains a list of DataTransportLayer
type DataTransportLayerList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []DataTransportLayer `json:"items"`
}
func init() {
SchemeBuilder.Register(&DataTransportLayer{}, &DataTransportLayerList{})
}
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// DeployerSpec defines the desired state of Deployer
type DeployerSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
Image string `json:"image,omitempty"`
L1URL string `json:"l1_url,omitempty"`
L1TimeoutSeconds int `json:"l1_timeout_seconds,omitempty"`
Env []corev1.EnvVar `json:"env,omitempty"`
}
// DeployerStatus defines the observed state of Deployer
type DeployerStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
// Deployer is the Schema for the deployers API
type Deployer struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec DeployerSpec `json:"spec,omitempty"`
Status DeployerStatus `json:"status,omitempty"`
}
//+kubebuilder:object:root=true
// DeployerList contains a list of Deployer
type DeployerList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Deployer `json:"items"`
}
func init() {
SchemeBuilder.Register(&Deployer{}, &DeployerList{})
}
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// GasOracleSpec defines the desired state of GasOracle
type GasOracleSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
Image string `json:"image,omitempty"`
L1URL string `json:"l1_url,omitempty"`
L1TimeoutSeconds int `json:"l1_timeout_seconds,omitempty"`
L2URL string `json:"l2_url,omitempty"`
L2TimeoutSeconds int `json:"l2_timeout_seconds,omitempty"`
Env []v1.EnvVar `json:"env,omitempty"`
}
// GasOracleStatus defines the observed state of GasOracle
type GasOracleStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
// GasOracle is the Schema for the gasoracles API
type GasOracle struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec GasOracleSpec `json:"spec,omitempty"`
Status GasOracleStatus `json:"status,omitempty"`
}
//+kubebuilder:object:root=true
// GasOracleList contains a list of GasOracle
type GasOracleList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []GasOracle `json:"items"`
}
func init() {
SchemeBuilder.Register(&GasOracle{}, &GasOracleList{})
}
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package v1 contains API Schema definitions for the stack v1 API group
//+kubebuilder:object:generate=true
//+groupName=stack.optimism-stacks.net
package v1
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)
var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "stack.optimism-stacks.net", Version: "v1"}
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// SequencerSpec defines the desired state of Sequencer
type SequencerSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
Image string `json:"image,omitempty"`
L1URL string `json:"l1_url,omitempty"`
L1TimeoutSeconds int `json:"l1_timeout_seconds,omitempty"`
DeployerURL string `json:"deployer_url,omitempty"`
DeployerTimeoutSeconds int `json:"deployer_timeout_seconds,omitempty"`
DTLURL string `json:"dtl_url,omitempty"`
DTLTimeoutSeconds int `json:"dtl_timeout_seconds,omitempty"`
DataPVC *PVCConfig `json:"data_pvc,omitempty"`
Env []corev1.EnvVar `json:"env,omitempty"`
AdditionalArgs []string `json:"additional_args,omitempty"`
}
// SequencerStatus defines the observed state of Sequencer
type SequencerStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
// Sequencer is the Schema for the sequencers API
type Sequencer struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec SequencerSpec `json:"spec,omitempty"`
Status SequencerStatus `json:"status,omitempty"`
}
//+kubebuilder:object:root=true
// SequencerList contains a list of Sequencer
type SequencerList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []Sequencer `json:"items"`
}
func init() {
SchemeBuilder.Register(&Sequencer{}, &SequencerList{})
}
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by controller-gen. DO NOT EDIT.
package v1
import (
corev1 "k8s.io/api/core/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Actor) DeepCopyInto(out *Actor) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Actor.
func (in *Actor) DeepCopy() *Actor {
if in == nil {
return nil
}
out := new(Actor)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Actor) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActorList) DeepCopyInto(out *ActorList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Actor, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActorList.
func (in *ActorList) DeepCopy() *ActorList {
if in == nil {
return nil
}
out := new(ActorList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ActorList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActorSpec) DeepCopyInto(out *ActorSpec) {
*out = *in
if in.PrivateKey != nil {
in, out := &in.PrivateKey, &out.PrivateKey
*out = new(Valuer)
(*in).DeepCopyInto(*out)
}
if in.Env != nil {
in, out := &in.Env, &out.Env
*out = make([]corev1.EnvVar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActorSpec.
func (in *ActorSpec) DeepCopy() *ActorSpec {
if in == nil {
return nil
}
out := new(ActorSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ActorStatus) DeepCopyInto(out *ActorStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActorStatus.
func (in *ActorStatus) DeepCopy() *ActorStatus {
if in == nil {
return nil
}
out := new(ActorStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BatchSubmitter) DeepCopyInto(out *BatchSubmitter) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BatchSubmitter.
func (in *BatchSubmitter) DeepCopy() *BatchSubmitter {
if in == nil {
return nil
}
out := new(BatchSubmitter)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BatchSubmitter) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BatchSubmitterList) DeepCopyInto(out *BatchSubmitterList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]BatchSubmitter, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BatchSubmitterList.
func (in *BatchSubmitterList) DeepCopy() *BatchSubmitterList {
if in == nil {
return nil
}
out := new(BatchSubmitterList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *BatchSubmitterList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BatchSubmitterSpec) DeepCopyInto(out *BatchSubmitterSpec) {
*out = *in
if in.Env != nil {
in, out := &in.Env, &out.Env
*out = make([]corev1.EnvVar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BatchSubmitterSpec.
func (in *BatchSubmitterSpec) DeepCopy() *BatchSubmitterSpec {
if in == nil {
return nil
}
out := new(BatchSubmitterSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BatchSubmitterStatus) DeepCopyInto(out *BatchSubmitterStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BatchSubmitterStatus.
func (in *BatchSubmitterStatus) DeepCopy() *BatchSubmitterStatus {
if in == nil {
return nil
}
out := new(BatchSubmitterStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CliqueL1) DeepCopyInto(out *CliqueL1) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CliqueL1.
func (in *CliqueL1) DeepCopy() *CliqueL1 {
if in == nil {
return nil
}
out := new(CliqueL1)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CliqueL1) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CliqueL1List) DeepCopyInto(out *CliqueL1List) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]CliqueL1, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CliqueL1List.
func (in *CliqueL1List) DeepCopy() *CliqueL1List {
if in == nil {
return nil
}
out := new(CliqueL1List)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *CliqueL1List) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CliqueL1Spec) DeepCopyInto(out *CliqueL1Spec) {
*out = *in
if in.GenesisFile != nil {
in, out := &in.GenesisFile, &out.GenesisFile
*out = new(Valuer)
(*in).DeepCopyInto(*out)
}
if in.SealerPrivateKey != nil {
in, out := &in.SealerPrivateKey, &out.SealerPrivateKey
*out = new(Valuer)
(*in).DeepCopyInto(*out)
}
if in.DataPVC != nil {
in, out := &in.DataPVC, &out.DataPVC
*out = new(PVCConfig)
(*in).DeepCopyInto(*out)
}
if in.AdditionalArgs != nil {
in, out := &in.AdditionalArgs, &out.AdditionalArgs
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CliqueL1Spec.
func (in *CliqueL1Spec) DeepCopy() *CliqueL1Spec {
if in == nil {
return nil
}
out := new(CliqueL1Spec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CliqueL1Status) DeepCopyInto(out *CliqueL1Status) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CliqueL1Status.
func (in *CliqueL1Status) DeepCopy() *CliqueL1Status {
if in == nil {
return nil
}
out := new(CliqueL1Status)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DataTransportLayer) DeepCopyInto(out *DataTransportLayer) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataTransportLayer.
func (in *DataTransportLayer) DeepCopy() *DataTransportLayer {
if in == nil {
return nil
}
out := new(DataTransportLayer)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *DataTransportLayer) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DataTransportLayerList) DeepCopyInto(out *DataTransportLayerList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]DataTransportLayer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataTransportLayerList.
func (in *DataTransportLayerList) DeepCopy() *DataTransportLayerList {
if in == nil {
return nil
}
out := new(DataTransportLayerList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *DataTransportLayerList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DataTransportLayerSpec) DeepCopyInto(out *DataTransportLayerSpec) {
*out = *in
if in.DataPVC != nil {
in, out := &in.DataPVC, &out.DataPVC
*out = new(PVCConfig)
(*in).DeepCopyInto(*out)
}
if in.Env != nil {
in, out := &in.Env, &out.Env
*out = make([]corev1.EnvVar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataTransportLayerSpec.
func (in *DataTransportLayerSpec) DeepCopy() *DataTransportLayerSpec {
if in == nil {
return nil
}
out := new(DataTransportLayerSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DataTransportLayerStatus) DeepCopyInto(out *DataTransportLayerStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataTransportLayerStatus.
func (in *DataTransportLayerStatus) DeepCopy() *DataTransportLayerStatus {
if in == nil {
return nil
}
out := new(DataTransportLayerStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Deployer) DeepCopyInto(out *Deployer) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Deployer.
func (in *Deployer) DeepCopy() *Deployer {
if in == nil {
return nil
}
out := new(Deployer)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Deployer) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeployerList) DeepCopyInto(out *DeployerList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Deployer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeployerList.
func (in *DeployerList) DeepCopy() *DeployerList {
if in == nil {
return nil
}
out := new(DeployerList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *DeployerList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeployerSpec) DeepCopyInto(out *DeployerSpec) {
*out = *in
if in.Env != nil {
in, out := &in.Env, &out.Env
*out = make([]corev1.EnvVar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeployerSpec.
func (in *DeployerSpec) DeepCopy() *DeployerSpec {
if in == nil {
return nil
}
out := new(DeployerSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeployerStatus) DeepCopyInto(out *DeployerStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeployerStatus.
func (in *DeployerStatus) DeepCopy() *DeployerStatus {
if in == nil {
return nil
}
out := new(DeployerStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GasOracle) DeepCopyInto(out *GasOracle) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GasOracle.
func (in *GasOracle) DeepCopy() *GasOracle {
if in == nil {
return nil
}
out := new(GasOracle)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *GasOracle) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GasOracleList) DeepCopyInto(out *GasOracleList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]GasOracle, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GasOracleList.
func (in *GasOracleList) DeepCopy() *GasOracleList {
if in == nil {
return nil
}
out := new(GasOracleList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *GasOracleList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GasOracleSpec) DeepCopyInto(out *GasOracleSpec) {
*out = *in
if in.Env != nil {
in, out := &in.Env, &out.Env
*out = make([]corev1.EnvVar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GasOracleSpec.
func (in *GasOracleSpec) DeepCopy() *GasOracleSpec {
if in == nil {
return nil
}
out := new(GasOracleSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GasOracleStatus) DeepCopyInto(out *GasOracleStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GasOracleStatus.
func (in *GasOracleStatus) DeepCopy() *GasOracleStatus {
if in == nil {
return nil
}
out := new(GasOracleStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PVCConfig) DeepCopyInto(out *PVCConfig) {
*out = *in
if in.Storage != nil {
in, out := &in.Storage, &out.Storage
x := (*in).DeepCopy()
*out = &x
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PVCConfig.
func (in *PVCConfig) DeepCopy() *PVCConfig {
if in == nil {
return nil
}
out := new(PVCConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Sequencer) DeepCopyInto(out *Sequencer) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sequencer.
func (in *Sequencer) DeepCopy() *Sequencer {
if in == nil {
return nil
}
out := new(Sequencer)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Sequencer) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SequencerList) DeepCopyInto(out *SequencerList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Sequencer, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SequencerList.
func (in *SequencerList) DeepCopy() *SequencerList {
if in == nil {
return nil
}
out := new(SequencerList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *SequencerList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SequencerSpec) DeepCopyInto(out *SequencerSpec) {
*out = *in
if in.DataPVC != nil {
in, out := &in.DataPVC, &out.DataPVC
*out = new(PVCConfig)
(*in).DeepCopyInto(*out)
}
if in.Env != nil {
in, out := &in.Env, &out.Env
*out = make([]corev1.EnvVar, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.AdditionalArgs != nil {
in, out := &in.AdditionalArgs, &out.AdditionalArgs
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SequencerSpec.
func (in *SequencerSpec) DeepCopy() *SequencerSpec {
if in == nil {
return nil
}
out := new(SequencerSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SequencerStatus) DeepCopyInto(out *SequencerStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SequencerStatus.
func (in *SequencerStatus) DeepCopy() *SequencerStatus {
if in == nil {
return nil
}
out := new(SequencerStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Valuer) DeepCopyInto(out *Valuer) {
*out = *in
if in.ValueFrom != nil {
in, out := &in.ValueFrom, &out.ValueFrom
*out = new(corev1.EnvVarSource)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Valuer.
func (in *Valuer) DeepCopy() *Valuer {
if in == nil {
return nil
}
out := new(Valuer)
in.DeepCopyInto(out)
return out
}
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.7.0
creationTimestamp: null
name: cliquel1s.stack.optimism-stacks.net
spec:
group: stack.optimism-stacks.net
names:
kind: CliqueL1
listKind: CliqueL1List
plural: cliquel1s
singular: cliquel1
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: CliqueL1 is the Schema for the cliquel1s API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: CliqueL1Spec defines the desired state of CliqueL1
type: object
status:
description: CliqueL1Status defines the observed state of CliqueL1
properties:
pod_names:
items:
type: string
type: array
required:
- pod_names
type: object
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
# This kustomization.yaml is not intended to be run by itself,
# since it depends on service name and namespace that are out of this kustomize package.
# It should be run by config/default
resources:
- bases/stack.optimism-stacks.net_cliquel1s.yaml
- bases/stack.optimism-stacks.net_deployers.yaml
- bases/stack.optimism-stacks.net_datatransportlayers.yaml
- bases/stack.optimism-stacks.net_sequencers.yaml
- bases/stack.optimism-stacks.net_batchsubmitters.yaml
- bases/stack.optimism-stacks.net_gasoracles.yaml
- bases/stack.optimism-stacks.net_actors.yaml
#+kubebuilder:scaffold:crdkustomizeresource
patchesStrategicMerge:
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
# patches here are for enabling the conversion webhook for each CRD
#- patches/webhook_in_cliquel1s.yaml
#- patches/webhook_in_deployers.yaml
#- patches/webhook_in_datatransportlayers.yaml
#- patches/webhook_in_sequencers.yaml
#- patches/webhook_in_batchsubmitters.yaml
#- patches/webhook_in_gasoracles.yaml
#- patches/webhook_in_actors.yaml
#+kubebuilder:scaffold:crdkustomizewebhookpatch
# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
# patches here are for enabling the CA injection for each CRD
#- patches/cainjection_in_cliquel1s.yaml
#- patches/cainjection_in_deployers.yaml
#- patches/cainjection_in_datatransportlayers.yaml
#- patches/cainjection_in_sequencers.yaml
#- patches/cainjection_in_batchsubmitters.yaml
#- patches/cainjection_in_gasoracles.yaml
#- patches/cainjection_in_actors.yaml
#+kubebuilder:scaffold:crdkustomizecainjectionpatch
# the following config is for teaching kustomize how to do kustomization for CRDs.
configurations:
- kustomizeconfig.yaml
# This file is for teaching kustomize how to substitute name and namespace reference in CRD
nameReference:
- kind: Service
version: v1
fieldSpecs:
- kind: CustomResourceDefinition
version: v1
group: apiextensions.k8s.io
path: spec/conversion/webhook/clientConfig/service/name
namespace:
- kind: CustomResourceDefinition
version: v1
group: apiextensions.k8s.io
path: spec/conversion/webhook/clientConfig/service/namespace
create: false
varReference:
- path: metadata/annotations
# The following patch adds a directive for certmanager to inject CA into the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: actors.stack.optimism-stacks.net
# The following patch adds a directive for certmanager to inject CA into the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: batchsubmitters.stack.optimism-stacks.net
# The following patch adds a directive for certmanager to inject CA into the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: cliquel1s.stack.optimism-stacks.net
# The following patch adds a directive for certmanager to inject CA into the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: datatransportlayers.stack.optimism-stacks.net
# The following patch adds a directive for certmanager to inject CA into the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: deployers.stack.optimism-stacks.net
# The following patch adds a directive for certmanager to inject CA into the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: gasoracles.stack.optimism-stacks.net
# The following patch adds a directive for certmanager to inject CA into the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: sequencers.stack.optimism-stacks.net
# The following patch enables a conversion webhook for the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: actors.stack.optimism-stacks.net
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
service:
namespace: system
name: webhook-service
path: /convert
conversionReviewVersions:
- v1
# The following patch enables a conversion webhook for the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: batchsubmitters.stack.optimism-stacks.net
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
service:
namespace: system
name: webhook-service
path: /convert
conversionReviewVersions:
- v1
# The following patch enables a conversion webhook for the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: cliquel1s.stack.optimism-stacks.net
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
service:
namespace: system
name: webhook-service
path: /convert
conversionReviewVersions:
- v1
# The following patch enables a conversion webhook for the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: datatransportlayers.stack.optimism-stacks.net
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
service:
namespace: system
name: webhook-service
path: /convert
conversionReviewVersions:
- v1
# The following patch enables a conversion webhook for the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: deployers.stack.optimism-stacks.net
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
service:
namespace: system
name: webhook-service
path: /convert
conversionReviewVersions:
- v1
# The following patch enables a conversion webhook for the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: gasoracles.stack.optimism-stacks.net
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
service:
namespace: system
name: webhook-service
path: /convert
conversionReviewVersions:
- v1
# The following patch enables a conversion webhook for the CRD
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: sequencers.stack.optimism-stacks.net
spec:
conversion:
strategy: Webhook
webhook:
clientConfig:
service:
namespace: system
name: webhook-service
path: /convert
conversionReviewVersions:
- v1
# Adds namespace to all resources.
namespace: stackman-system
# Value of this field is prepended to the
# names of all resources, e.g. a deployment named
# "wordpress" becomes "alices-wordpress".
# Note that it should also match with the prefix (text before '-') of the namespace
# field above.
namePrefix: stackman-
# Labels to add to all resources and selectors.
#commonLabels:
# someName: someValue
bases:
- ../crd
- ../rbac
- ../manager
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- ../webhook
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
#- ../certmanager
# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
- ../prometheus
#patchesStrategicMerge:
# Protect the /metrics endpoint by putting it behind auth.
# If you want your controller-manager to expose the /metrics
# endpoint w/o any authn/z, please comment the following line.
#- manager_auth_proxy_patch.yaml
# Mount the controller config file for loading manager configurations
# through a ComponentConfig type
#- manager_config_patch.yaml
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
# crd/kustomization.yaml
#- manager_webhook_patch.yaml
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
# 'CERTMANAGER' needs to be enabled to use ca injection
#- webhookcainjection_patch.yaml
# the following config is for teaching kustomize how to do var substitution
vars:
# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # this name should match the one in certificate.yaml
# fieldref:
# fieldpath: metadata.namespace
#- name: CERTIFICATE_NAME
# objref:
# kind: Certificate
# group: cert-manager.io
# version: v1
# name: serving-cert # this name should match the one in certificate.yaml
#- name: SERVICE_NAMESPACE # namespace of the service
# objref:
# kind: Service
# version: v1
# name: webhook-service
# fieldref:
# fieldpath: metadata.namespace
#- name: SERVICE_NAME
# objref:
# kind: Service
# version: v1
# name: webhook-service
# This patch inject a sidecar container which is a HTTP proxy for the
# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: kube-rbac-proxy
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0
args:
- "--secure-listen-address=0.0.0.0:8443"
- "--upstream=http://127.0.0.1:8080/"
- "--logtostderr=true"
- "--v=10"
ports:
- containerPort: 8443
protocol: TCP
name: https
- name: manager
args:
- "--health-probe-bind-address=:8081"
- "--metrics-bind-address=127.0.0.1:8080"
- "--leader-elect"
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
spec:
template:
spec:
containers:
- name: manager
args:
- "--config=controller_manager_config.yaml"
volumeMounts:
- name: manager-config
mountPath: /controller_manager_config.yaml
subPath: controller_manager_config.yaml
volumes:
- name: manager-config
configMap:
name: manager-config
apiVersion: controller-runtime.sigs.k8s.io/v1alpha1
kind: ControllerManagerConfig
health:
healthProbeBindAddress: :8081
metrics:
bindAddress: 127.0.0.1:8080
webhook:
port: 9443
leaderElection:
leaderElect: true
resourceName: 8103f40b.optimism-stacks.net
resources:
- manager.yaml
generatorOptions:
disableNameSuffixHash: true
configMapGenerator:
- files:
- controller_manager_config.yaml
name: manager-config
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
images:
- name: controller
newName: controller
newTag: latest
apiVersion: v1
kind: Namespace
metadata:
labels:
control-plane: controller-manager
name: system
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: controller-manager
namespace: system
labels:
control-plane: controller-manager
spec:
selector:
matchLabels:
control-plane: controller-manager
replicas: 1
template:
metadata:
labels:
control-plane: controller-manager
spec:
securityContext:
runAsNonRoot: true
containers:
- command:
- /manager
args:
- --leader-elect
image: controller:latest
name: manager
securityContext:
allowPrivilegeEscalation: false
livenessProbe:
httpGet:
path: /healthz
port: 8081
initialDelaySeconds: 15
periodSeconds: 20
readinessProbe:
httpGet:
path: /readyz
port: 8081
initialDelaySeconds: 5
periodSeconds: 10
resources:
limits:
cpu: 200m
memory: 100Mi
requests:
cpu: 100m
memory: 20Mi
serviceAccountName: controller-manager
terminationGracePeriodSeconds: 10
# These resources constitute the fully configured set of manifests
# used to generate the 'manifests/' directory in a bundle.
resources:
- bases/stackman.clusterserviceversion.yaml
- ../default
- ../samples
- ../scorecard
# [WEBHOOK] To enable webhooks, uncomment all the sections with [WEBHOOK] prefix.
# Do NOT uncomment sections with prefix [CERTMANAGER], as OLM does not support cert-manager.
# These patches remove the unnecessary "cert" volume and its manager container volumeMount.
#patchesJson6902:
#- target:
# group: apps
# version: v1
# kind: Deployment
# name: controller-manager
# namespace: system
# patch: |-
# # Remove the manager container's "cert" volumeMount, since OLM will create and mount a set of certs.
# # Update the indices in this path if adding or removing containers/volumeMounts in the manager's Deployment.
# - op: remove
# path: /spec/template/spec/containers/1/volumeMounts/0
# # Remove the "cert" volume, since OLM will create and mount a set of certs.
# # Update the indices in this path if adding or removing volumes in the manager's Deployment.
# - op: remove
# path: /spec/template/spec/volumes/0
# Prometheus Monitor Service (Metrics)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
labels:
control-plane: controller-manager
name: controller-manager-metrics-monitor
namespace: system
spec:
endpoints:
- path: /metrics
port: https
scheme: https
bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
tlsConfig:
insecureSkipVerify: true
selector:
matchLabels:
control-plane: controller-manager
# permissions for end users to edit actors.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: actor-editor-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- actors
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- actors/status
verbs:
- get
# permissions for end users to view actors.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: actor-viewer-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- actors
verbs:
- get
- list
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- actors/status
verbs:
- get
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: metrics-reader
rules:
- nonResourceURLs:
- "/metrics"
verbs:
- get
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: proxy-role
rules:
- apiGroups:
- authentication.k8s.io
resources:
- tokenreviews
verbs:
- create
- apiGroups:
- authorization.k8s.io
resources:
- subjectaccessreviews
verbs:
- create
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: proxy-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: proxy-role
subjects:
- kind: ServiceAccount
name: controller-manager
namespace: system
apiVersion: v1
kind: Service
metadata:
labels:
control-plane: controller-manager
name: controller-manager-metrics-service
namespace: system
spec:
ports:
- name: https
port: 8443
protocol: TCP
targetPort: https
selector:
control-plane: controller-manager
# permissions for end users to edit batchsubmitters.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: batchsubmitter-editor-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- batchsubmitters
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- batchsubmitters/status
verbs:
- get
# permissions for end users to view batchsubmitters.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: batchsubmitter-viewer-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- batchsubmitters
verbs:
- get
- list
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- batchsubmitters/status
verbs:
- get
# permissions for end users to edit cliquel1s.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cliquel1-editor-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- cliquel1s
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- cliquel1s/status
verbs:
- get
# permissions for end users to view cliquel1s.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cliquel1-viewer-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- cliquel1s
verbs:
- get
- list
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- cliquel1s/status
verbs:
- get
# permissions for end users to edit datatransportlayers.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: datatransportlayer-editor-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- datatransportlayers
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- datatransportlayers/status
verbs:
- get
# permissions for end users to view datatransportlayers.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: datatransportlayer-viewer-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- datatransportlayers
verbs:
- get
- list
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- datatransportlayers/status
verbs:
- get
# permissions for end users to edit deployers.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: deployer-editor-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- deployers
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- deployers/status
verbs:
- get
# permissions for end users to view deployers.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: deployer-viewer-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- deployers
verbs:
- get
- list
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- deployers/status
verbs:
- get
# permissions for end users to edit gasoracles.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: gasoracle-editor-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- gasoracles
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- gasoracles/status
verbs:
- get
# permissions for end users to view gasoracles.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: gasoracle-viewer-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- gasoracles
verbs:
- get
- list
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- gasoracles/status
verbs:
- get
resources:
# All RBAC will be applied under this service account in
# the deployment namespace. You may comment out this resource
# if your manager will use a service account that exists at
# runtime. Be sure to update RoleBinding and ClusterRoleBinding
# subjects if changing service account names.
- service_account.yaml
- role.yaml
- role_binding.yaml
- leader_election_role.yaml
- leader_election_role_binding.yaml
# Comment the following 4 lines if you want to disable
# the auth proxy (https://github.com/brancz/kube-rbac-proxy)
# which protects your /metrics endpoint.
- auth_proxy_service.yaml
- auth_proxy_role.yaml
- auth_proxy_role_binding.yaml
- auth_proxy_client_clusterrole.yaml
# permissions to do leader election.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: leader-election-role
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- coordination.k8s.io
resources:
- leases
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: leader-election-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: leader-election-role
subjects:
- kind: ServiceAccount
name: controller-manager
namespace: system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
creationTimestamp: null
name: manager-role
rules:
- apiGroups:
- apps
resources:
- deployments
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- ""
resources:
- configmaps
- pods
- services
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- cliquel1s
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- cliquel1s/finalizers
verbs:
- update
- apiGroups:
- stack.optimism-stacks.net
resources:
- cliquel1s/status
verbs:
- get
- patch
- update
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: manager-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: manager-role
subjects:
- kind: ServiceAccount
name: controller-manager
namespace: system
# permissions for end users to edit sequencers.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: sequencer-editor-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- sequencers
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- sequencers/status
verbs:
- get
# permissions for end users to view sequencers.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: sequencer-viewer-role
rules:
- apiGroups:
- stack.optimism-stacks.net
resources:
- sequencers
verbs:
- get
- list
- watch
- apiGroups:
- stack.optimism-stacks.net
resources:
- sequencers/status
verbs:
- get
apiVersion: v1
kind: ServiceAccount
metadata:
name: controller-manager
namespace: system
## Append samples you want in your CSV to this file as resources ##
resources:
- stack_v1_cliquel1.yaml
- stack_v1_deployer.yaml
- stack_v1_datatransportlayer.yaml
- stack_v1_sequencer.yaml
- stack_v1_batchsubmitter.yaml
- stack_v1_gasoracle.yaml
- stack_v1_actor.yaml
#+kubebuilder:scaffold:manifestskustomizesamples
apiVersion: stack.optimism-stacks.net/v1
kind: Actor
metadata:
name: actor-sample
spec:
# Add fields here
apiVersion: stack.optimism-stacks.net/v1
kind: BatchSubmitter
metadata:
name: batchsubmitter-sample
spec:
# Add fields here
apiVersion: stack.optimism-stacks.net/v1
kind: CliqueL1
metadata:
name: cliquel1-sample
spec:
# Add fields here
apiVersion: stack.optimism-stacks.net/v1
kind: DataTransportLayer
metadata:
name: datatransportlayer-sample
spec:
# Add fields here
apiVersion: stack.optimism-stacks.net/v1
kind: Deployer
metadata:
name: deployer-sample
spec:
# Add fields here
apiVersion: stack.optimism-stacks.net/v1
kind: GasOracle
metadata:
name: gasoracle-sample
spec:
# Add fields here
apiVersion: stack.optimism-stacks.net/v1
kind: Sequencer
metadata:
name: sequencer-sample
spec:
# Add fields here
apiVersion: scorecard.operatorframework.io/v1alpha3
kind: Configuration
metadata:
name: config
stages:
- parallel: true
tests: []
resources:
- bases/config.yaml
patchesJson6902:
- path: patches/basic.config.yaml
target:
group: scorecard.operatorframework.io
version: v1alpha3
kind: Configuration
name: config
- path: patches/olm.config.yaml
target:
group: scorecard.operatorframework.io
version: v1alpha3
kind: Configuration
name: config
#+kubebuilder:scaffold:patchesJson6902
- op: add
path: /stages/0/tests/-
value:
entrypoint:
- scorecard-test
- basic-check-spec
image: quay.io/operator-framework/scorecard-test:v1.15.0
labels:
suite: basic
test: basic-check-spec-test
- op: add
path: /stages/0/tests/-
value:
entrypoint:
- scorecard-test
- olm-bundle-validation
image: quay.io/operator-framework/scorecard-test:v1.15.0
labels:
suite: olm
test: olm-bundle-validation-test
- op: add
path: /stages/0/tests/-
value:
entrypoint:
- scorecard-test
- olm-crds-have-validation
image: quay.io/operator-framework/scorecard-test:v1.15.0
labels:
suite: olm
test: olm-crds-have-validation-test
- op: add
path: /stages/0/tests/-
value:
entrypoint:
- scorecard-test
- olm-crds-have-resources
image: quay.io/operator-framework/scorecard-test:v1.15.0
labels:
suite: olm
test: olm-crds-have-resources-test
- op: add
path: /stages/0/tests/-
value:
entrypoint:
- scorecard-test
- olm-spec-descriptors
image: quay.io/operator-framework/scorecard-test:v1.15.0
labels:
suite: olm
test: olm-spec-descriptors-test
- op: add
path: /stages/0/tests/-
value:
entrypoint:
- scorecard-test
- olm-status-descriptors
image: quay.io/operator-framework/scorecard-test:v1.15.0
labels:
suite: olm
test: olm-status-descriptors-test
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"context"
"crypto/md5"
"encoding/hex"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"strconv"
"time"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
stackv1 "github.com/ethereum-optimism/optimism/go/stackman/api/v1"
)
// ActorReconciler reconciles a Actor object
type ActorReconciler struct {
client.Client
Scheme *runtime.Scheme
}
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=actors,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=actors/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=actors/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the Actor object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.10.0/pkg/reconcile
func (r *ActorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
lgr := log.FromContext(ctx)
crd := &stackv1.Actor{}
if err := r.Get(ctx, req.NamespacedName, crd); err != nil {
if errors.IsNotFound(err) {
lgr.Info("actor resource not found, ignoring")
return ctrl.Result{}, nil
}
lgr.Error(err, "error getting actor")
return ctrl.Result{}, err
}
deployment := &appsv1.Deployment{}
created, err := GetOrCreateResource(ctx, r, func() client.Object {
return r.deployment(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "actor"), deployment)
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
argsHash := r.argsHash(crd)
if deployment.Labels["args_hash"] != argsHash {
err := r.Update(ctx, r.deployment(crd))
if err != nil {
lgr.Error(err, "error updating actor deployment")
return ctrl.Result{}, err
}
return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
}
created, err = GetOrCreateResource(ctx, r, func() client.Object {
return r.service(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "actor"), &corev1.Service{})
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *ActorReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&stackv1.Actor{}).
Complete(r)
}
func (r *ActorReconciler) labels(crd *stackv1.Actor) map[string]string {
return map[string]string{
"actor": crd.ObjectMeta.Name,
}
}
func (r *ActorReconciler) argsHash(crd *stackv1.Actor) string {
h := md5.New()
h.Write([]byte(crd.Spec.Image))
h.Write([]byte(crd.Spec.L1URL))
h.Write([]byte(crd.Spec.L2URL))
h.Write([]byte(crd.Spec.PrivateKey.String()))
h.Write([]byte(crd.Spec.AddressManagerAddress))
h.Write([]byte(crd.Spec.TestFilename))
h.Write([]byte(strconv.Itoa(crd.Spec.Concurrency)))
h.Write([]byte(strconv.Itoa(crd.Spec.RunForMS)))
h.Write([]byte(strconv.Itoa(crd.Spec.RunCount)))
h.Write([]byte(strconv.Itoa(crd.Spec.ThinkTimeMS)))
return hex.EncodeToString(h.Sum(nil))
}
func (r *ActorReconciler) deployment(crd *stackv1.Actor) *appsv1.Deployment {
replicas := int32(1)
command := []string{
"yarn",
"test:actor",
"-f",
crd.Spec.TestFilename,
"-c",
strconv.Itoa(crd.Spec.Concurrency),
"--serve",
}
if crd.Spec.RunForMS != 0 {
command = append(command, "-t")
command = append(command, strconv.Itoa(crd.Spec.RunForMS))
} else if crd.Spec.RunCount != 0 {
command = append(command, "-r")
command = append(command, strconv.Itoa(crd.Spec.RunCount))
}
if crd.Spec.ThinkTimeMS != 0 {
command = append(command, "--think-time")
command = append(command, strconv.Itoa(crd.Spec.ThinkTimeMS))
}
env := append([]corev1.EnvVar{
crd.Spec.PrivateKey.EnvVar("PRIVATE_KEY"),
{
Name: "L1_URL",
Value: crd.Spec.L1URL,
},
{
Name: "L2_URL",
Value: crd.Spec.L2URL,
},
{
Name: "IS_LIVE_NETWORK",
Value: "true",
},
{
Name: "ADDRESS_MANAGER",
Value: crd.Spec.AddressManagerAddress,
},
}, crd.Spec.Env...)
deployment := &appsv1.Deployment{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "actor", map[string]string{
"args_hash": r.argsHash(crd),
"actor": crd.ObjectMeta.Name,
}),
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: r.labels(crd),
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: r.labels(crd),
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyAlways,
Containers: []corev1.Container{
{
Name: "actor",
Image: crd.Spec.Image,
ImagePullPolicy: corev1.PullAlways,
WorkingDir: "/opt/optimism/integration-tests",
Command: command,
Env: env,
Ports: []corev1.ContainerPort{
{
Name: "metrics",
ContainerPort: 8545,
},
},
},
},
},
},
},
}
ctrl.SetControllerReference(crd, deployment, r.Scheme)
return deployment
}
func (r *ActorReconciler) service(crd *stackv1.Actor) *corev1.Service {
service := &corev1.Service{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "actor", r.labels(crd)),
Spec: corev1.ServiceSpec{
Selector: r.labels(crd),
Type: corev1.ServiceTypeClusterIP,
Ports: []corev1.ServicePort{
{
Name: "metrics",
Port: 8545,
},
},
},
}
ctrl.SetControllerReference(crd, service, r.Scheme)
return service
}
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"context"
"crypto/md5"
"encoding/hex"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"strconv"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
stackv1 "github.com/ethereum-optimism/optimism/go/stackman/api/v1"
)
// BatchSubmitterReconciler reconciles a BatchSubmitter object
type BatchSubmitterReconciler struct {
client.Client
Scheme *runtime.Scheme
}
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=batchsubmitters,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=batchsubmitters/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=batchsubmitters/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the BatchSubmitter object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.10.0/pkg/reconcile
func (r *BatchSubmitterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
lgr := log.FromContext(ctx)
crd := &stackv1.BatchSubmitter{}
if err := r.Get(ctx, req.NamespacedName, crd); err != nil {
if errors.IsNotFound(err) {
lgr.Info("batch submitter resource not found, ignoring")
return ctrl.Result{}, nil
}
lgr.Error(err, "error getting batch submitter")
return ctrl.Result{}, err
}
created, err := GetOrCreateResource(ctx, r, func() client.Object {
return r.entrypointsCfgMap(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "batch-submitter-entrypoints"), &corev1.ConfigMap{})
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
statefulSet := &appsv1.StatefulSet{}
created, err = GetOrCreateResource(ctx, r, func() client.Object {
return r.statefulSet(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "batch-submitter"), statefulSet)
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
argsHash := r.statefulSetArgsHash(crd)
if statefulSet.Labels["args_hash"] != argsHash {
err := r.Update(ctx, r.statefulSet(crd))
if err != nil {
lgr.Error(err, "error updating batch submitter statefulSet")
return ctrl.Result{}, err
}
return ctrl.Result{Requeue: true}, nil
}
created, err = GetOrCreateResource(ctx, r, func() client.Object {
return r.service(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "batch-submitter"), &corev1.Service{})
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *BatchSubmitterReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&stackv1.BatchSubmitter{}).
Complete(r)
}
func (r *BatchSubmitterReconciler) labels() map[string]string {
return map[string]string{
"app": "batch-submitter",
}
}
func (r *BatchSubmitterReconciler) entrypointsCfgMap(crd *stackv1.BatchSubmitter) *corev1.ConfigMap {
cfgMap := &corev1.ConfigMap{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "batch-submitter-entrypoints", r.labels()),
Data: map[string]string{
"entrypoint.sh": BatchSubmitterEntrypoint,
},
}
ctrl.SetControllerReference(crd, cfgMap, r.Scheme)
return cfgMap
}
func (r *BatchSubmitterReconciler) statefulSetArgsHash(crd *stackv1.BatchSubmitter) string {
h := md5.New()
h.Write([]byte(crd.Spec.Image))
h.Write([]byte(crd.Spec.L1URL))
h.Write([]byte(strconv.Itoa(crd.Spec.L1TimeoutSeconds)))
h.Write([]byte(crd.Spec.L2URL))
h.Write([]byte(strconv.Itoa(crd.Spec.L2TimeoutSeconds)))
h.Write([]byte(crd.Spec.DeployerURL))
h.Write([]byte(strconv.Itoa(crd.Spec.DeployerTimeoutSeconds)))
for _, ev := range crd.Spec.Env {
h.Write([]byte(ev.String()))
}
return hex.EncodeToString(h.Sum(nil))
}
func (r *BatchSubmitterReconciler) statefulSet(crd *stackv1.BatchSubmitter) *appsv1.StatefulSet {
replicas := int32(1)
defaultMode := int32(0o777)
labels := r.labels()
labels["args_hash"] = r.statefulSetArgsHash(crd)
initContainers := []corev1.Container{
{
Name: "wait-for-l1",
Image: "mslipper/wait-for-it:latest",
ImagePullPolicy: corev1.PullAlways,
Args: []string{
Hostify(crd.Spec.L1URL),
"-t",
strconv.Itoa(crd.Spec.L1TimeoutSeconds),
},
},
{
Name: "wait-for-l2",
Image: "mslipper/wait-for-it:latest",
ImagePullPolicy: corev1.PullAlways,
Args: []string{
Hostify(crd.Spec.L2URL),
"-t",
strconv.Itoa(crd.Spec.L2TimeoutSeconds),
},
},
}
baseEnv := []corev1.EnvVar{
{
Name: "L1_NODE_WEB3_URL",
Value: crd.Spec.L1URL,
},
{
Name: "L2_NODE_WEB3_URL",
Value: crd.Spec.L2URL,
},
{
Name: "RUN_METRICS_SERVER",
Value: "true",
},
{
Name: "METRICS_HOSTNAME",
Value: "0.0.0.0",
},
{
Name: "METRICS_PORT",
Value: "9090",
},
}
if crd.Spec.DeployerURL != "" {
initContainers = append(initContainers, corev1.Container{
Name: "wait-for-deployer",
Image: "mslipper/wait-for-it:latest",
ImagePullPolicy: corev1.PullAlways,
Args: []string{
Hostify(crd.Spec.DeployerURL),
"-t",
strconv.Itoa(crd.Spec.DeployerTimeoutSeconds),
},
})
baseEnv = append(baseEnv, []corev1.EnvVar{
{
Name: "ROLLUP_STATE_DUMP_PATH",
Value: "http://deployer:8081/state-dump.latest.json",
},
{
Name: "DEPLOYER_URL",
Value: crd.Spec.DeployerURL,
},
}...)
}
statefulSet := &appsv1.StatefulSet{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "batch-submitter", labels),
Spec: appsv1.StatefulSetSpec{
Replicas: &replicas,
Selector: &v1.LabelSelector{
MatchLabels: map[string]string{
"app": "batch-submitter",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{
Labels: r.labels(),
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyAlways,
InitContainers: initContainers,
Containers: []corev1.Container{
{
Name: "batch-submitter",
Image: crd.Spec.Image,
ImagePullPolicy: corev1.PullAlways,
WorkingDir: "/opt/optimism/packages/batch-submitter",
Command: []string{
"/bin/sh",
"/opt/entrypoints/entrypoint.sh",
"npm",
"run",
"start",
},
Env: append(baseEnv, crd.Spec.Env...),
VolumeMounts: []corev1.VolumeMount{
{
Name: "entrypoints",
MountPath: "/opt/entrypoints",
},
},
Ports: []corev1.ContainerPort{
{
Name: "metrics",
ContainerPort: 9090,
},
},
},
},
Volumes: []corev1.Volume{
{
Name: "entrypoints",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: ObjectName(crd.ObjectMeta, "batch-submitter-entrypoints"),
},
DefaultMode: &defaultMode,
},
},
},
},
},
},
},
}
ctrl.SetControllerReference(crd, statefulSet, r.Scheme)
return statefulSet
}
func (r *BatchSubmitterReconciler) service(crd *stackv1.BatchSubmitter) *corev1.Service {
service := &corev1.Service{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "batch-submitter", r.labels()),
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": "batch-submitter",
},
Ports: []corev1.ServicePort{
{
Name: "metrics",
Port: 9090,
},
},
},
}
ctrl.SetControllerReference(crd, service, r.Scheme)
return service
}
const BatchSubmitterEntrypoint = `
#!/bin/sh
if [ -n "$DEPLOYER_URL" ]; then
echo "Loading addresses from $DEPLOYER_URL."
ADDRESSES=$(curl --fail --show-error --silent "$DEPLOYER_URL/addresses.json")
export ADDRESS_MANAGER_ADDRESS=$(echo $ADDRESSES | jq -r ".AddressManager")
fi
exec "$@"
`
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"context"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"strconv"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
stackv1 "github.com/ethereum-optimism/optimism/go/stackman/api/v1"
)
// CliqueL1Reconciler reconciles a CliqueL1 object
type CliqueL1Reconciler struct {
client.Client
Scheme *runtime.Scheme
}
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=cliquel1s,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=cliquel1s/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=cliquel1s/finalizers,verbs=update
//+kubebuilder:rbac:groups=apps,resources=deployments;statefulsets,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=core,resources=services;pods;configmaps,verbs=get;list;watch;create;update;patch;delete
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the CliqueL1 object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.10.0/pkg/reconcile
func (r *CliqueL1Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
lgr := log.FromContext(ctx)
crd := &stackv1.CliqueL1{}
if err := r.Get(ctx, req.NamespacedName, crd); err != nil {
if errors.IsNotFound(err) {
lgr.Info("clique resource, not found, ignoring")
return ctrl.Result{}, nil
}
lgr.Error(err, "error getting clique")
return ctrl.Result{}, err
}
created, err := GetOrCreateResource(ctx, r, func() client.Object {
return r.entrypointsCfgMap(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "clique-entrypoints"), &corev1.ConfigMap{})
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
nsName := ObjectNamespacedName(crd.ObjectMeta, "clique")
created, err = GetOrCreateResource(ctx, r, func() client.Object {
return r.statefulSet(crd)
}, nsName, &appsv1.StatefulSet{})
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
created, err = GetOrCreateResource(ctx, r, func() client.Object {
return r.service(crd)
}, nsName, &corev1.Service{})
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *CliqueL1Reconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&stackv1.CliqueL1{}).
Owns(&corev1.ConfigMap{}).
Owns(&appsv1.Deployment{}).
Owns(&corev1.Service{}).
Complete(r)
}
func (r *CliqueL1Reconciler) entrypointsCfgMap(crd *stackv1.CliqueL1) *corev1.ConfigMap {
cfgMap := &corev1.ConfigMap{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "clique-entrypoints", map[string]string{
"app": "clique",
}),
Data: map[string]string{
"entrypoint.sh": CliqueEntrypoint,
},
}
ctrl.SetControllerReference(crd, cfgMap, r.Scheme)
return cfgMap
}
func (r *CliqueL1Reconciler) statefulSet(crd *stackv1.CliqueL1) *appsv1.StatefulSet {
replicas := int32(1)
labels := map[string]string{
"app": "clique",
}
defaultMode := int32(0o777)
volumes := []corev1.Volume{
{
Name: "entrypoints",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: ObjectName(crd.ObjectMeta, "clique-entrypoints"),
},
DefaultMode: &defaultMode,
},
},
},
}
var volumeClaimTemplates []corev1.PersistentVolumeClaim
dbVolumeName := "db"
if crd.Spec.DataPVC != nil {
storage := resource.MustParse("128Gi")
dbVolumeName = crd.Spec.DataPVC.Name
if crd.Spec.DataPVC.Storage != nil {
storage = *crd.Spec.DataPVC.Storage
}
volumeClaimTemplates = []corev1.PersistentVolumeClaim{
{
ObjectMeta: metav1.ObjectMeta{
Name: crd.Spec.DataPVC.Name,
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{
corev1.ReadWriteOnce,
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceStorage: storage,
},
},
},
},
}
} else {
volumes = append(volumes, corev1.Volume{
Name: dbVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
})
}
statefulSet := &appsv1.StatefulSet{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "clique", labels),
Spec: appsv1.StatefulSetSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "clique",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyAlways,
Containers: []corev1.Container{
{
Name: "geth",
Image: crd.Spec.Image,
ImagePullPolicy: corev1.PullAlways,
Command: append([]string{
"/bin/sh",
"/opt/entrypoints/entrypoint.sh",
}, crd.Spec.AdditionalArgs...),
Env: []corev1.EnvVar{
crd.Spec.SealerPrivateKey.EnvVar("BLOCK_SIGNER_PRIVATE_KEY"),
crd.Spec.GenesisFile.EnvVar("GENESIS_DATA"),
{
Name: "BLOCK_SIGNER_ADDRESS",
Value: crd.Spec.SealerAddress,
},
{
Name: "CHAIN_ID",
Value: strconv.Itoa(crd.Spec.ChainID),
},
},
VolumeMounts: []corev1.VolumeMount{
{
Name: "entrypoints",
MountPath: "/opt/entrypoints",
},
{
Name: dbVolumeName,
MountPath: "/db",
},
},
Ports: []corev1.ContainerPort{
{
ContainerPort: 8085,
},
{
ContainerPort: 8086,
},
},
},
},
Volumes: volumes,
},
},
VolumeClaimTemplates: volumeClaimTemplates,
},
}
ctrl.SetControllerReference(crd, statefulSet, r.Scheme)
return statefulSet
}
func (r *CliqueL1Reconciler) service(crd *stackv1.CliqueL1) *corev1.Service {
labels := map[string]string{
"app": "clique",
}
service := &corev1.Service{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "clique", labels),
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": "clique",
},
Ports: []corev1.ServicePort{
{
Name: "rpc",
Port: 8545,
TargetPort: intstr.FromInt(8545),
},
{
Name: "ws",
Port: 8546,
TargetPort: intstr.FromInt(8546),
},
},
},
}
ctrl.SetControllerReference(crd, service, r.Scheme)
return service
}
const CliqueEntrypoint = `
#!/bin/sh
set -exu
VERBOSITY=${VERBOSITY:-9}
GETH_DATA_DIR=/db
GETH_CHAINDATA_DIR="$GETH_DATA_DIR/geth/chaindata"
GETH_KEYSTORE_DIR="$GETH_DATA_DIR/keystore"
if [ ! -d "$GETH_KEYSTORE_DIR" ]; then
echo "$GETH_KEYSTORE_DIR missing, running account import"
echo -n "pwd" > "$GETH_DATA_DIR"/password
echo -n "$BLOCK_SIGNER_PRIVATE_KEY" | sed 's/0x//' > "$GETH_DATA_DIR"/block-signer-key
geth account import \
--datadir="$GETH_DATA_DIR" \
--password="$GETH_DATA_DIR"/password \
"$GETH_DATA_DIR"/block-signer-key
else
echo "$GETH_KEYSTORE_DIR exists."
fi
if [ ! -d "$GETH_CHAINDATA_DIR" ]; then
echo "$GETH_CHAINDATA_DIR missing, running init"
echo "Creating genesis file."
echo -n "$GENESIS_DATA" > "$GETH_DATA_DIR/genesis.json"
geth --verbosity="$VERBOSITY" init \
--datadir="$GETH_DATA_DIR" \
"$GETH_DATA_DIR/genesis.json"
else
echo "$GETH_CHAINDATA_DIR exists."
fi
geth \
--datadir="$GETH_DATA_DIR" \
--verbosity="$VERBOSITY" \
--http \
--http.corsdomain="*" \
--http.vhosts="*" \
--http.addr=0.0.0.0 \
--http.port=8545 \
--ws.addr=0.0.0.0 \
--ws.port=8546 \
--ws.origins="*" \
--syncmode=full \
--nodiscover \
--maxpeers=1 \
--networkid=$CHAIN_ID \
--unlock=$BLOCK_SIGNER_ADDRESS \
--mine \
--miner.etherbase=$BLOCK_SIGNER_ADDRESS \
--password="$GETH_DATA_DIR"/password \
--allow-insecure-unlock \
"$@"
`
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"context"
"crypto/md5"
"encoding/hex"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"strconv"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
stackv1 "github.com/ethereum-optimism/optimism/go/stackman/api/v1"
)
// DataTransportLayerReconciler reconciles a DataTransportLayer object
type DataTransportLayerReconciler struct {
client.Client
Scheme *runtime.Scheme
}
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=datatransportlayers,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=datatransportlayers/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=datatransportlayers/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the DataTransportLayer object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.10.0/pkg/reconcile
func (r *DataTransportLayerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
lgr := log.FromContext(ctx)
crd := &stackv1.DataTransportLayer{}
if err := r.Get(ctx, req.NamespacedName, crd); err != nil {
if errors.IsNotFound(err) {
lgr.Info("dtl resource not found, ignoring")
return ctrl.Result{}, nil
}
lgr.Error(err, "error getting dtl")
return ctrl.Result{}, err
}
created, err := GetOrCreateResource(ctx, r, func() client.Object {
return r.entrypointsCfgMap(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "dtl-entrypoints"), &corev1.ConfigMap{})
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
statefulSet := &appsv1.StatefulSet{}
created, err = GetOrCreateResource(ctx, r, func() client.Object {
return r.statefulSet(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "dtl"), statefulSet)
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
argsHash := r.argsHash(crd)
if statefulSet.Labels["args_hash"] != argsHash {
err := r.Update(ctx, r.statefulSet(crd))
if err != nil {
lgr.Error(err, "error updating dtl statefulSet")
return ctrl.Result{}, err
}
return ctrl.Result{Requeue: true}, nil
}
created, err = GetOrCreateResource(ctx, r, func() client.Object {
return r.service(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "dtl"), &corev1.Service{})
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *DataTransportLayerReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&stackv1.DataTransportLayer{}).
Owns(&corev1.ConfigMap{}).
Owns(&appsv1.Deployment{}).
Owns(&corev1.Service{}).
Complete(r)
}
func (r *DataTransportLayerReconciler) labels() map[string]string {
return map[string]string{
"app": "dtl",
}
}
func (r *DataTransportLayerReconciler) argsHash(crd *stackv1.DataTransportLayer) string {
h := md5.New()
h.Write([]byte(crd.Spec.Image))
h.Write([]byte(crd.Spec.L1URL))
h.Write([]byte(strconv.Itoa(crd.Spec.L1TimeoutSeconds)))
h.Write([]byte(crd.Spec.DeployerURL))
h.Write([]byte(strconv.Itoa(crd.Spec.DeployerTimeoutSeconds)))
if crd.Spec.DataPVC != nil {
h.Write([]byte(crd.Spec.DataPVC.Name))
h.Write([]byte(crd.Spec.DataPVC.Storage.String()))
}
for _, ev := range crd.Spec.Env {
h.Write([]byte(ev.String()))
}
return hex.EncodeToString(h.Sum(nil))
}
func (r *DataTransportLayerReconciler) entrypointsCfgMap(crd *stackv1.DataTransportLayer) *corev1.ConfigMap {
cfgMap := &corev1.ConfigMap{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "dtl-entrypoints", r.labels()),
Data: map[string]string{
"entrypoint.sh": DTLEntrypoint,
},
}
ctrl.SetControllerReference(crd, cfgMap, r.Scheme)
return cfgMap
}
func (r *DataTransportLayerReconciler) statefulSet(crd *stackv1.DataTransportLayer) *appsv1.StatefulSet {
replicas := int32(1)
defaultMode := int32(0o777)
initContainers := []corev1.Container{
{
Name: "wait-for-l1",
Image: "mslipper/wait-for-it:latest",
ImagePullPolicy: corev1.PullAlways,
Args: []string{
Hostify(crd.Spec.L1URL),
"-t",
strconv.Itoa(crd.Spec.L1TimeoutSeconds),
},
},
}
baseEnv := []corev1.EnvVar{
{
Name: "DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT",
Value: crd.Spec.L1URL,
},
}
if crd.Spec.DeployerURL != "" {
initContainers = append(initContainers, corev1.Container{
Name: "wait-for-deployer",
Image: "mslipper/wait-for-it:latest",
ImagePullPolicy: corev1.PullAlways,
Args: []string{
Hostify(crd.Spec.DeployerURL),
"-t",
strconv.Itoa(crd.Spec.DeployerTimeoutSeconds),
},
})
baseEnv = append(baseEnv, corev1.EnvVar{
Name: "DEPLOYER_URL",
Value: crd.Spec.DeployerURL,
})
}
volumes := []corev1.Volume{
{
Name: "entrypoints",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: ObjectName(crd.ObjectMeta, "dtl-entrypoints"),
},
DefaultMode: &defaultMode,
},
},
},
}
var volumeClaimTemplates []corev1.PersistentVolumeClaim
dbVolumeName := "db"
if crd.Spec.DataPVC != nil {
storage := resource.MustParse("10Gi")
dbVolumeName = crd.Spec.DataPVC.Name
if crd.Spec.DataPVC.Storage != nil {
storage = *crd.Spec.DataPVC.Storage
}
volumeClaimTemplates = []corev1.PersistentVolumeClaim{
{
ObjectMeta: v1.ObjectMeta{
Name: crd.Spec.DataPVC.Name,
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{
corev1.ReadWriteOnce,
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceStorage: storage,
},
},
},
},
}
} else {
volumes = append(volumes, corev1.Volume{
Name: dbVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
})
}
statefulSet := &appsv1.StatefulSet{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "dtl", r.labels()),
Spec: appsv1.StatefulSetSpec{
Replicas: &replicas,
Selector: &v1.LabelSelector{
MatchLabels: map[string]string{
"app": "dtl",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{
Labels: r.labels(),
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyAlways,
InitContainers: initContainers,
Containers: []corev1.Container{
{
Name: "dtl",
Image: crd.Spec.Image,
ImagePullPolicy: corev1.PullAlways,
Command: []string{
"/bin/sh",
"/opt/entrypoints/entrypoint.sh",
"node",
"/opt/optimism/packages/data-transport-layer/dist/src/services/run.js",
},
Env: append(baseEnv, crd.Spec.Env...),
VolumeMounts: []corev1.VolumeMount{
{
Name: dbVolumeName,
MountPath: "/db",
},
{
Name: "entrypoints",
MountPath: "/opt/entrypoints",
},
},
Ports: []corev1.ContainerPort{
{
Name: "api",
ContainerPort: 7878,
},
{
Name: "metrics",
ContainerPort: 3000,
},
},
LivenessProbe: &corev1.Probe{
Handler: corev1.Handler{
HTTPGet: &corev1.HTTPGetAction{
Path: "/eth/syncing",
Port: intstr.FromInt(7878),
},
},
},
},
},
Volumes: volumes,
},
},
VolumeClaimTemplates: volumeClaimTemplates,
},
}
ctrl.SetControllerReference(crd, statefulSet, r.Scheme)
return statefulSet
}
func (r *DataTransportLayerReconciler) service(crd *stackv1.DataTransportLayer) *corev1.Service {
service := &corev1.Service{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "dtl", r.labels()),
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": "dtl",
},
Ports: []corev1.ServicePort{
{
Port: 3000,
},
{
Port: 7878,
},
},
},
}
ctrl.SetControllerReference(crd, service, r.Scheme)
return service
}
const DTLEntrypoint = `
#!/bin/sh
if [ -n "$DEPLOYER_URL" ]; then
echo "Loading addresses from $DEPLOYER_URL."
ADDRESSES=$(curl --fail --show-error --silent "$DEPLOYER_URL/addresses.json")
export DATA_TRANSPORT_LAYER__ADDRESS_MANAGER=$(echo $ADDRESSES | jq -r ".AddressManager")
fi
exec "$@"
`
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"context"
"crypto/md5"
"encoding/hex"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"strconv"
"time"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
stackv1 "github.com/ethereum-optimism/optimism/go/stackman/api/v1"
)
// DeployerReconciler reconciles a Deployer object
type DeployerReconciler struct {
client.Client
Scheme *runtime.Scheme
}
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=deployers,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=deployers/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=deployers/finalizers,verbs=update
//+kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=core,resources=services;pods;configmaps,verbs=get;list;watch;create;update;patch;delete
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the Deployer object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.10.0/pkg/reconcile
func (r *DeployerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
lgr := log.FromContext(ctx)
crd := &stackv1.Deployer{}
if err := r.Get(ctx, req.NamespacedName, crd); err != nil {
if errors.IsNotFound(err) {
lgr.Info("deployer resource not found, ignoring")
return ctrl.Result{}, nil
}
lgr.Error(err, "error getting deployer")
return ctrl.Result{}, err
}
created, err := GetOrCreateResource(ctx, r, func() client.Object {
return r.entrypointsCfgMap(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "entrypoints"), &corev1.ConfigMap{})
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
deployment := &appsv1.Deployment{}
created, err = GetOrCreateResource(ctx, r, func() client.Object {
return r.deployment(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "deployer"), deployment)
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
argsHash := r.deploymentArgsHash(crd)
if deployment.Labels["args_hash"] != argsHash {
err := r.Update(ctx, r.deployment(crd))
if err != nil {
lgr.Error(err, "error updating deployer deployment")
return ctrl.Result{}, err
}
return ctrl.Result{RequeueAfter: 30 * time.Second}, nil
}
created, err = GetOrCreateResource(ctx, r, func() client.Object {
return r.service(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "deployer"), &corev1.Service{})
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *DeployerReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&stackv1.Deployer{}).
Owns(&corev1.ConfigMap{}).
Owns(&appsv1.Deployment{}).
Owns(&corev1.Service{}).
Complete(r)
}
func (r *DeployerReconciler) labels(crd *stackv1.Deployer) map[string]string {
return map[string]string{
"app": "deployer",
"deployer_crd": crd.Namespace,
}
}
func (r *DeployerReconciler) entrypointsCfgMap(crd *stackv1.Deployer) *corev1.ConfigMap {
cfgMap := &corev1.ConfigMap{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "entrypoints", r.labels(crd)),
Data: map[string]string{
"entrypoint.sh": DeployerEntrypoint,
},
}
ctrl.SetControllerReference(crd, cfgMap, r.Scheme)
return cfgMap
}
func (r *DeployerReconciler) deploymentArgsHash(crd *stackv1.Deployer) string {
h := md5.New()
h.Write([]byte(crd.Spec.Image))
h.Write([]byte(crd.Spec.L1URL))
h.Write([]byte(strconv.Itoa(crd.Spec.L1TimeoutSeconds)))
for _, ev := range crd.Spec.Env {
h.Write([]byte(ev.String()))
}
return hex.EncodeToString(h.Sum(nil))
}
func (r *DeployerReconciler) deployment(crd *stackv1.Deployer) *appsv1.Deployment {
replicas := int32(1)
defaultMode := int32(0o777)
deployment := &appsv1.Deployment{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "deployer", map[string]string{
"app": "deployer",
"deployer_crd": crd.Namespace,
"args_hash": r.deploymentArgsHash(crd),
}),
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &v1.LabelSelector{
MatchLabels: map[string]string{
"app": "deployer",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: v1.ObjectMeta{
Labels: r.labels(crd),
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyAlways,
InitContainers: []corev1.Container{
{
Name: "wait-for-l1",
Image: "mslipper/wait-for-it:latest",
ImagePullPolicy: corev1.PullAlways,
Args: []string{
Hostify(crd.Spec.L1URL),
"-t",
strconv.Itoa(crd.Spec.L1TimeoutSeconds),
},
},
},
Containers: []corev1.Container{
{
Name: "deployer",
Image: crd.Spec.Image,
ImagePullPolicy: corev1.PullAlways,
Command: []string{
"/bin/bash",
"/opt/entrypoints/entrypoint.sh",
},
Env: append([]corev1.EnvVar{
{
Name: "L1_NODE_WEB3_URL",
Value: crd.Spec.L1URL,
},
{
Name: "NO_COMPILE",
Value: "1",
},
{
Name: "AUTOMATICALLY_TRANSFER_OWNERSHIP",
Value: "true",
},
}, crd.Spec.Env...),
VolumeMounts: []corev1.VolumeMount{
{
Name: "entrypoints",
MountPath: "/opt/entrypoints",
},
},
Ports: []corev1.ContainerPort{
{
ContainerPort: 8081,
},
},
},
},
Volumes: []corev1.Volume{
{
Name: "entrypoints",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: ObjectName(crd.ObjectMeta, "entrypoints"),
},
DefaultMode: &defaultMode,
},
},
},
},
},
},
},
}
ctrl.SetControllerReference(crd, deployment, r.Scheme)
return deployment
}
func (r *DeployerReconciler) service(crd *stackv1.Deployer) *corev1.Service {
service := &corev1.Service{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "deployer", r.labels(crd)),
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": "deployer",
},
Ports: []corev1.ServicePort{
{
Name: "web",
Port: 8081,
},
},
},
}
ctrl.SetControllerReference(crd, service, r.Scheme)
return service
}
const DeployerEntrypoint = `
#!/bin/bash
set -e
cd /optimism/packages/contracts
yarn run deploy
function envSet() {
VAR=$1
export $VAR=$(cat ./dist/dumps/addresses.json | jq -r ".$2")
}
# set the address to the proxy gateway if possible
envSet L1_STANDARD_BRIDGE_ADDRESS Proxy__OVM_L1StandardBridge
if [ $L1_STANDARD_BRIDGE_ADDRESS == null ]; then
envSet L1_STANDARD_BRIDGE_ADDRESS L1StandardBridge
fi
envSet L1_CROSS_DOMAIN_MESSENGER_ADDRESS Proxy__OVM_L1CrossDomainMessenger
if [ $L1_CROSS_DOMAIN_MESSENGER_ADDRESS == null ]; then
envSet L1_CROSS_DOMAIN_MESSENGER_ADDRESS L1CrossDomainMessenger
fi
# build the dump file
yarn run build:dump
echo "Starting server."
# service the addresses and dumps
python3 -m http.server \
--bind "0.0.0.0" 8081 \
--directory ./dist/dumps
`
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"context"
"crypto/md5"
"encoding/hex"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"strconv"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
stackv1 "github.com/ethereum-optimism/optimism/go/stackman/api/v1"
)
// GasOracleReconciler reconciles a GasOracle object
type GasOracleReconciler struct {
client.Client
Scheme *runtime.Scheme
}
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=gasoracles,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=gasoracles/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=gasoracles/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the GasOracle object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.10.0/pkg/reconcile
func (r *GasOracleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
lgr := log.FromContext(ctx)
crd := &stackv1.GasOracle{}
if err := r.Get(ctx, req.NamespacedName, crd); err != nil {
if errors.IsNotFound(err) {
lgr.Info("gas oracle resource not found, ignoring")
return ctrl.Result{}, nil
}
lgr.Error(err, "error getting gas oracle")
return ctrl.Result{}, err
}
deployment := &appsv1.Deployment{}
created, err := GetOrCreateResource(ctx, r, func() client.Object {
return r.deployment(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "gas-oracle"), deployment)
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
argsHash := r.deploymentArgsHash(crd)
if deployment.Labels["args_hash"] != argsHash {
err := r.Update(ctx, r.deployment(crd))
if err != nil {
lgr.Error(err, "error updating gas oracle deployment")
return ctrl.Result{}, err
}
return ctrl.Result{Requeue: true}, nil
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *GasOracleReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&stackv1.GasOracle{}).
Complete(r)
}
func (r *GasOracleReconciler) labels() map[string]string {
return map[string]string{
"app": "gas-oracle",
}
}
func (r *GasOracleReconciler) deploymentArgsHash(crd *stackv1.GasOracle) string {
h := md5.New()
h.Write([]byte(crd.Spec.Image))
h.Write([]byte(crd.Spec.L1URL))
h.Write([]byte(strconv.Itoa(crd.Spec.L1TimeoutSeconds)))
h.Write([]byte(crd.Spec.L2URL))
h.Write([]byte(strconv.Itoa(crd.Spec.L2TimeoutSeconds)))
for _, ev := range crd.Spec.Env {
h.Write([]byte(ev.String()))
}
return hex.EncodeToString(h.Sum(nil))
}
func (r *GasOracleReconciler) deployment(crd *stackv1.GasOracle) *appsv1.Deployment {
replicas := int32(1)
labels := r.labels()
labels["args_hash"] = r.deploymentArgsHash(crd)
baseEnv := []corev1.EnvVar{
{
Name: "GAS_PRICE_ORACLE_ETHEREUM_HTTP_URL",
Value: crd.Spec.L1URL,
},
{
Name: "GAS_PRICE_ORACLE_LAYER_TWO_HTTP_URL",
Value: crd.Spec.L2URL,
},
}
deployment := &appsv1.Deployment{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "gas-oracle", labels),
Spec: appsv1.DeploymentSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "gas-oracle",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: r.labels(),
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyAlways,
InitContainers: []corev1.Container{
{
Name: "wait-for-l1",
Image: "mslipper/wait-for-it:latest",
ImagePullPolicy: corev1.PullAlways,
Args: []string{
Hostify(crd.Spec.L1URL),
"-t",
strconv.Itoa(crd.Spec.L1TimeoutSeconds),
},
},
{
Name: "wait-for-l2",
Image: "mslipper/wait-for-it:latest",
ImagePullPolicy: corev1.PullAlways,
Args: []string{
Hostify(crd.Spec.L2URL),
"-t",
strconv.Itoa(crd.Spec.L2TimeoutSeconds),
},
},
},
Containers: []corev1.Container{
{
Name: "gpo",
Image: crd.Spec.Image,
ImagePullPolicy: corev1.PullAlways,
Env: append(baseEnv, crd.Spec.Env...),
},
},
},
},
},
}
ctrl.SetControllerReference(crd, deployment, r.Scheme)
return deployment
}
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"context"
"crypto/md5"
"encoding/hex"
"fmt"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
"strconv"
stackv1 "github.com/ethereum-optimism/optimism/go/stackman/api/v1"
)
// SequencerReconciler reconciles a Sequencer object
type SequencerReconciler struct {
client.Client
Scheme *runtime.Scheme
}
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=sequencers,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=sequencers/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=stack.optimism-stacks.net,resources=sequencers/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the Sequencer object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.10.0/pkg/reconcile
func (r *SequencerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
lgr := log.FromContext(ctx)
crd := &stackv1.Sequencer{}
if err := r.Get(ctx, req.NamespacedName, crd); err != nil {
if errors.IsNotFound(err) {
lgr.Info("sequencer resource not found, ignoring")
return ctrl.Result{}, nil
}
lgr.Error(err, "error getting sequencer")
return ctrl.Result{}, err
}
created, err := GetOrCreateResource(ctx, r, func() client.Object {
return r.entrypointsCfgMap(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "sequencer-entrypoints"), &corev1.ConfigMap{})
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
statefulSet := &appsv1.StatefulSet{}
created, err = GetOrCreateResource(ctx, r, func() client.Object {
return r.statefulSet(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "sequencer"), statefulSet)
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
argsHash := r.deploymentArgsHash(crd)
if statefulSet.Labels["args_hash"] != argsHash {
err := r.Update(ctx, r.statefulSet(crd))
if err != nil {
lgr.Error(err, "error updating sequencer statefulSet")
return ctrl.Result{}, err
}
return ctrl.Result{Requeue: true}, nil
}
created, err = GetOrCreateResource(ctx, r, func() client.Object {
return r.service(crd)
}, ObjectNamespacedName(crd.ObjectMeta, "sequencer"), &corev1.Service{})
if err != nil {
return ctrl.Result{}, err
}
if created {
return ctrl.Result{Requeue: true}, nil
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *SequencerReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&stackv1.Sequencer{}).
Complete(r)
}
func (r *SequencerReconciler) labels() map[string]string {
return map[string]string{
"app": "sequencer",
}
}
func (r *SequencerReconciler) entrypointsCfgMap(crd *stackv1.Sequencer) *corev1.ConfigMap {
cfgMap := &corev1.ConfigMap{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "sequencer-entrypoints", r.labels()),
Data: map[string]string{
"entrypoint.sh": SequencerEntrypoint,
},
}
ctrl.SetControllerReference(crd, cfgMap, r.Scheme)
return cfgMap
}
func (r *SequencerReconciler) statefulSet(crd *stackv1.Sequencer) *appsv1.StatefulSet {
replicas := int32(1)
defaultMode := int32(0o777)
om := ObjectMeta(crd.ObjectMeta, "sequencer", r.labels())
om.Labels["args_hash"] = r.deploymentArgsHash(crd)
initContainers := []corev1.Container{
{
Name: "wait-for-dtl",
Image: "mslipper/wait-for-it:latest",
ImagePullPolicy: corev1.PullAlways,
Args: []string{
Hostify(crd.Spec.DTLURL),
"-t",
strconv.Itoa(crd.Spec.DTLTimeoutSeconds),
},
},
}
baseEnv := []corev1.EnvVar{
{
Name: "ROLLUP_CLIENT_HTTP",
Value: crd.Spec.DTLURL,
},
}
if crd.Spec.DeployerURL != "" {
initContainers = append(initContainers, corev1.Container{
Name: "wait-for-deployer",
Image: "mslipper/wait-for-it:latest",
ImagePullPolicy: corev1.PullAlways,
Args: []string{
Hostify(crd.Spec.DeployerURL),
"-t",
strconv.Itoa(crd.Spec.DeployerTimeoutSeconds),
},
})
baseEnv = append(baseEnv, []corev1.EnvVar{
{
Name: "L2GETH_GENESIS_URL",
Value: fmt.Sprintf("%s/state-dump.latest.json", crd.Spec.DeployerURL),
},
{
Name: "DEPLOYER_URL",
Value: crd.Spec.DeployerURL,
},
}...)
}
volumes := []corev1.Volume{
{
Name: "entrypoints",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: ObjectName(crd.ObjectMeta, "sequencer-entrypoints"),
},
DefaultMode: &defaultMode,
},
},
},
}
var volumeClaimTemplates []corev1.PersistentVolumeClaim
dbVolumeName := "db"
if crd.Spec.DataPVC != nil {
dbVolumeName = crd.Spec.DataPVC.Name
storage := resource.MustParse("128Gi")
if crd.Spec.DataPVC.Storage != nil {
storage = *crd.Spec.DataPVC.Storage
}
volumeClaimTemplates = []corev1.PersistentVolumeClaim{
{
ObjectMeta: metav1.ObjectMeta{
Name: crd.Spec.DataPVC.Name,
},
Spec: corev1.PersistentVolumeClaimSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{
corev1.ReadWriteOnce,
},
Resources: corev1.ResourceRequirements{
Requests: corev1.ResourceList{
corev1.ResourceStorage: storage,
},
},
},
},
}
} else {
volumes = append(volumes, corev1.Volume{
Name: dbVolumeName,
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{},
},
})
}
statefulSet := &appsv1.StatefulSet{
ObjectMeta: om,
Spec: appsv1.StatefulSetSpec{
Replicas: &replicas,
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "sequencer",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: r.labels(),
},
Spec: corev1.PodSpec{
RestartPolicy: corev1.RestartPolicyAlways,
InitContainers: initContainers,
Containers: []corev1.Container{
{
Name: "sequencer",
Image: crd.Spec.Image,
ImagePullPolicy: corev1.PullAlways,
Command: append([]string{
"/bin/sh",
"/opt/entrypoints/entrypoint.sh",
}, crd.Spec.AdditionalArgs...),
Env: append(baseEnv, crd.Spec.Env...),
VolumeMounts: []corev1.VolumeMount{
{
Name: dbVolumeName,
MountPath: "/db",
},
{
Name: "entrypoints",
MountPath: "/opt/entrypoints",
},
},
Ports: []corev1.ContainerPort{
{
ContainerPort: 8545,
},
{
ContainerPort: 8546,
},
},
},
},
Volumes: volumes,
},
},
VolumeClaimTemplates: volumeClaimTemplates,
},
}
ctrl.SetControllerReference(crd, statefulSet, r.Scheme)
return statefulSet
}
func (r *SequencerReconciler) service(crd *stackv1.Sequencer) *corev1.Service {
service := &corev1.Service{
ObjectMeta: ObjectMeta(crd.ObjectMeta, "sequencer", r.labels()),
Spec: corev1.ServiceSpec{
Selector: map[string]string{
"app": "sequencer",
},
Type: corev1.ServiceTypeClusterIP,
Ports: []corev1.ServicePort{
{
Name: "rpc",
Port: 8545,
},
{
Name: "websocket",
Port: 8546,
},
},
},
}
ctrl.SetControllerReference(crd, service, r.Scheme)
return service
}
func (r *SequencerReconciler) deploymentArgsHash(crd *stackv1.Sequencer) string {
h := md5.New()
h.Write([]byte(crd.Spec.Image))
h.Write([]byte(crd.Spec.L1URL))
h.Write([]byte(strconv.Itoa(crd.Spec.L1TimeoutSeconds)))
h.Write([]byte(crd.Spec.DeployerURL))
h.Write([]byte(strconv.Itoa(crd.Spec.DeployerTimeoutSeconds)))
h.Write([]byte(crd.Spec.DTLURL))
h.Write([]byte(strconv.Itoa(crd.Spec.DTLTimeoutSeconds)))
if crd.Spec.DataPVC != nil {
h.Write([]byte(crd.Spec.DataPVC.Name))
h.Write([]byte(crd.Spec.DataPVC.Storage.String()))
}
for _, ev := range crd.Spec.Env {
h.Write([]byte(ev.String()))
}
for _, arg := range crd.Spec.AdditionalArgs {
h.Write([]byte(arg))
}
return hex.EncodeToString(h.Sum(nil))
}
const SequencerEntrypoint = `
#!/bin/sh
set -exu
VERBOSITY=${VERBOSITY:-9}
GETH_DATA_DIR=/db
GETH_CHAINDATA_DIR="$GETH_DATA_DIR/geth/chaindata"
GETH_KEYSTORE_DIR="$GETH_DATA_DIR/keystore"
if [ ! -d "$GETH_KEYSTORE_DIR" ]; then
echo "$GETH_KEYSTORE_DIR missing, running account import"
echo -n "pwd" > "$GETH_DATA_DIR"/password
echo -n "$BLOCK_SIGNER_PRIVATE_KEY" | sed 's/0x//' > "$GETH_DATA_DIR"/block-signer-key
geth account import \
--datadir="$GETH_DATA_DIR" \
--password="$GETH_DATA_DIR"/password \
"$GETH_DATA_DIR"/block-signer-key
else
echo "$GETH_KEYSTORE_DIR exists."
fi
if [ ! -d "$GETH_CHAINDATA_DIR" ]; then
echo "$GETH_CHAINDATA_DIR missing, running init"
echo "Retrieving genesis file $L2GETH_GENESIS_URL"
curl --silent -o "$GETH_DATA_DIR/genesis.json" "$L2GETH_GENESIS_URL"
geth --verbosity="$VERBOSITY" init \
--datadir="$GETH_DATA_DIR" \
"$GETH_DATA_DIR/genesis.json"
else
echo "$GETH_CHAINDATA_DIR exists."
fi
geth \
--verbosity="$VERBOSITY" \
--datadir="$GETH_DATA_DIR" \
--password="$GETH_DATA_DIR/password" \
--allow-insecure-unlock \
--unlock="$BLOCK_SIGNER_ADDRESS" \
--mine \
--miner.etherbase=$BLOCK_SIGNER_ADDRESS \
"$@"
`
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controllers
import (
"path/filepath"
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/envtest"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
stackv1 "github.com/ethereum-optimism/optimism/go/stackman/api/v1"
//+kubebuilder:scaffold:imports
)
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
var cfg *rest.Config
var k8sClient client.Client
var testEnv *envtest.Environment
func TestAPIs(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t,
"Controller Suite",
[]Reporter{printer.NewlineReporter{}})
}
var _ = BeforeSuite(func() {
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
By("bootstrapping test environment")
testEnv = &envtest.Environment{
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
ErrorIfCRDPathMissing: true,
}
cfg, err := testEnv.Start()
Expect(err).NotTo(HaveOccurred())
Expect(cfg).NotTo(BeNil())
err = stackv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = stackv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = stackv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = stackv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = stackv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = stackv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = stackv1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
//+kubebuilder:scaffold:scheme
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
Expect(err).NotTo(HaveOccurred())
Expect(k8sClient).NotTo(BeNil())
}, 60)
var _ = AfterSuite(func() {
By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})
package controllers
import (
"context"
"fmt"
"k8s.io/apimachinery/pkg/api/errors"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"regexp"
"sigs.k8s.io/controller-runtime/pkg/client"
"strconv"
"strings"
)
var httpRe *regexp.Regexp
var pathRe *regexp.Regexp
var portRe *regexp.Regexp
func Hostify(in string) string {
port := 80
if strings.HasPrefix(in, "https://") {
port = 443
}
out := httpRe.ReplaceAllString(in, "")
out = pathRe.ReplaceAllString(out, "")
if portRe.MatchString(out) {
return out
}
return out + ":" + strconv.Itoa(port)
}
func ObjectName(om v1.ObjectMeta, name string) string {
return fmt.Sprintf("%s-%s", om.Name, name)
}
func ObjectNamespacedName(om v1.ObjectMeta, name string) types.NamespacedName {
return types.NamespacedName{
Name: ObjectName(om, name),
Namespace: om.Namespace,
}
}
func ObjectMeta(om v1.ObjectMeta, name string, labels map[string]string) v1.ObjectMeta {
return v1.ObjectMeta{
Name: ObjectName(om, name),
Namespace: om.Namespace,
Labels: labels,
}
}
type Creator func() client.Object
type ReaderWriter interface {
client.Reader
client.Writer
}
func GetOrCreateResource(
ctx context.Context,
client ReaderWriter,
creator Creator,
name types.NamespacedName,
proto client.Object,
) (bool, error) {
err := client.Get(ctx, name, proto)
if err == nil {
return false, nil
}
if errors.IsNotFound(err) {
err = client.Create(ctx, creator())
if err != nil {
return false, err
}
return true, nil
}
return false, err
}
func init() {
httpRe = regexp.MustCompile("^http(s)?://")
pathRe = regexp.MustCompile("/(.*)$")
portRe = regexp.MustCompile(":([\\d]+)$")
}
package controllers
import (
"github.com/stretchr/testify/require"
"testing"
)
func TestHostify(t *testing.T) {
tests := [][2]string{
{
"https://test.infura.io/v1/123456",
"test.infura.io:443",
},
{
"http://test.infura.io/v1/123456",
"test.infura.io:80",
},
{
"test.infura.io/v1/123456",
"test.infura.io:80",
},
{
"test.infura.io",
"test.infura.io:80",
},
{
"http://sequencer:8545",
"sequencer:8545",
},
}
for _, tt := range tests {
t.Run(tt[0], func(t *testing.T) {
require.Equal(t, tt[1], Hostify(tt[0]))
})
}
}
module github.com/ethereum-optimism/optimism/go/stackman
go 1.16
require (
github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.15.0
github.com/stretchr/testify v1.7.0
gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/api v0.22.1
k8s.io/apimachinery v0.22.1
k8s.io/client-go v0.22.1
sigs.k8s.io/controller-runtime v0.10.0
)
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0 h1:3ithwDMr7/3vpAMXiH+ZQnYbuIsh+OPhUPMFC9enmn0=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM=
github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.3.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/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs=
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
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 v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM=
github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
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.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
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/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
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.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
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.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
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/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
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.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 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
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-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
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-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
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.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
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.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
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.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
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.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
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/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
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 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU=
github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
github.com/prometheus/client_golang v1.11.0/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/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
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.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
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/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE=
go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc=
go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=
go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo=
go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM=
go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU=
go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw=
go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc=
go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE=
go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE=
go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/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-20181114220301-adae6a3d119a/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/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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
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-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/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-20200831180312-196b9ba8737a/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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2 h1:c8PlLMqBbOHoqtjteWm5/kbe6rNY2pbRfbIMVnepueo=
golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
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.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=
gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
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.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
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/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
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=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.22.1 h1:ISu3tD/jRhYfSW8jI/Q1e+lRxkR7w9UwQEZ7FgslrwY=
k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY=
k8s.io/apiextensions-apiserver v0.22.1 h1:YSJYzlFNFSfUle+yeEXX0lSQyLEoxoPJySRupepb0gE=
k8s.io/apiextensions-apiserver v0.22.1/go.mod h1:HeGmorjtRmRLE+Q8dJu6AYRoZccvCMsghwS8XTUYb2c=
k8s.io/apimachinery v0.22.1 h1:DTARnyzmdHMz7bFWFDDm22AM4pLWTQECMpRTFu2d2OM=
k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
k8s.io/apiserver v0.22.1/go.mod h1:2mcM6dzSt+XndzVQJX21Gx0/Klo7Aen7i0Ai6tIa400=
k8s.io/client-go v0.22.1 h1:jW0ZSHi8wW260FvcXHkIa0NLxFBQszTlhiAVsU5mopw=
k8s.io/client-go v0.22.1/go.mod h1:BquC5A4UOo4qVDUtoc04/+Nxp1MeHcVc1HJm1KmG8kk=
k8s.io/code-generator v0.22.1/go.mod h1:eV77Y09IopzeXOJzndrDyCI88UBok2h6WxAlBwpxa+o=
k8s.io/component-base v0.22.1 h1:SFqIXsEN3v3Kkr1bS6rstrs1wd45StJqbtgbQ4nRQdo=
k8s.io/component-base v0.22.1/go.mod h1:0D+Bl8rrnsPN9v0dyYvkqFfBeAd4u7n77ze+p8CMiPo=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
k8s.io/utils v0.0.0-20210707171843-4b05e18ac7d9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/utils v0.0.0-20210802155522-efc7438f0176 h1:Mx0aa+SUAcNRQbs5jUzV8lkDlGFU8laZsY9jrcVX5SY=
k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
sigs.k8s.io/controller-runtime v0.10.0 h1:HgyZmMpjUOrtkaFtCnfxsR1bGRuFoAczSNbn2MoKj5U=
sigs.k8s.io/controller-runtime v0.10.0/go.mod h1:GCdh6kqV6IY4LK0JLwX0Zm6g233RtVGdb/f0+KSfprg=
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
\ No newline at end of file
/*
Copyright 2021.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"flag"
"os"
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
// to ensure that exec-entrypoint and run can make use of them.
_ "k8s.io/client-go/plugin/pkg/client/auth"
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
stackv1 "github.com/ethereum-optimism/optimism/go/stackman/api/v1"
"github.com/ethereum-optimism/optimism/go/stackman/controllers"
//+kubebuilder:scaffold:imports
)
var (
scheme = runtime.NewScheme()
setupLog = ctrl.Log.WithName("setup")
)
func init() {
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
utilruntime.Must(stackv1.AddToScheme(scheme))
//+kubebuilder:scaffold:scheme
}
func main() {
var metricsAddr string
var enableLeaderElection bool
var probeAddr string
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
opts := zap.Options{
Development: true,
}
opts.BindFlags(flag.CommandLine)
flag.Parse()
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
MetricsBindAddress: metricsAddr,
Port: 9443,
HealthProbeBindAddress: probeAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "8103f40b.optimism-stacks.net",
})
if err != nil {
setupLog.Error(err, "unable to start manager")
os.Exit(1)
}
if err = (&controllers.CliqueL1Reconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "CliqueL1")
os.Exit(1)
}
if err = (&controllers.DeployerReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Deployer")
os.Exit(1)
}
if err = (&controllers.DataTransportLayerReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "DataTransportLayer")
os.Exit(1)
}
if err = (&controllers.SequencerReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Sequencer")
os.Exit(1)
}
if err = (&controllers.BatchSubmitterReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "BatchSubmitter")
os.Exit(1)
}
if err = (&controllers.GasOracleReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "GasOracle")
os.Exit(1)
}
if err = (&controllers.ActorReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Actor")
os.Exit(1)
}
//+kubebuilder:scaffold:builder
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up health check")
os.Exit(1)
}
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
setupLog.Error(err, "unable to set up ready check")
os.Exit(1)
}
setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
os.Exit(1)
}
}
......@@ -2,11 +2,12 @@ FROM golang:1.18.0-alpine3.15 as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash
COPY ./go/indexer /go/indexer
COPY ./go/indexer/go.mod ./go/indexer/go.sum /go/indexer/
COPY ./indexer /go/indexer
COPY ./l2geth /go/l2geth
COPY ./indexer/docker.go.work /go/go.work
WORKDIR /go/indexer
RUN go mod graph | grep -v indexer | awk '{if ($1 !~ "@") print $2}' | xargs -n 1 go get
COPY ./go/indexer/ ./
RUN make
FROM alpine:3.15
......
......@@ -8,8 +8,8 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/go/indexer"
"github.com/ethereum-optimism/optimism/go/indexer/flags"
"github.com/ethereum-optimism/optimism/indexer"
"github.com/ethereum-optimism/optimism/indexer/flags"
)
var (
......
......@@ -7,7 +7,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/go/indexer/flags"
"github.com/ethereum-optimism/optimism/indexer/flags"
)
var (
......
......@@ -4,7 +4,7 @@ import (
"fmt"
"testing"
indexer "github.com/ethereum-optimism/optimism/go/indexer"
indexer "github.com/ethereum-optimism/optimism/indexer"
"github.com/stretchr/testify/require"
)
......
......@@ -5,7 +5,7 @@ import (
"errors"
"testing"
indexer "github.com/ethereum-optimism/optimism/go/indexer"
indexer "github.com/ethereum-optimism/optimism/indexer"
l2common "github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum/go-ethereum/common"
"github.com/stretchr/testify/require"
......
go 1.18
use (
./indexer
./l2geth
)
module github.com/ethereum-optimism/optimism/go/indexer
module github.com/ethereum-optimism/optimism/indexer
go 1.17
replace github.com/ethereum-optimism/optimism/l2geth v0.0.0 => ../l2geth
require (
github.com/ethereum-optimism/optimism/l2geth v0.0.0-20220104205740-f39387287484
github.com/ethereum-optimism/optimism/l2geth v0.0.0
github.com/ethereum/go-ethereum v1.10.16
github.com/getsentry/sentry-go v0.12.0
github.com/google/uuid v1.3.0
......
......@@ -9,17 +9,17 @@ import (
"strconv"
"time"
"github.com/ethereum-optimism/optimism/go/indexer/services"
"github.com/ethereum-optimism/optimism/indexer/services"
l2rpc "github.com/ethereum-optimism/optimism/l2geth/rpc"
"github.com/ethereum-optimism/optimism/go/indexer/metrics"
"github.com/ethereum-optimism/optimism/go/indexer/server"
"github.com/ethereum-optimism/optimism/indexer/metrics"
"github.com/ethereum-optimism/optimism/indexer/server"
"github.com/rs/cors"
database "github.com/ethereum-optimism/optimism/go/indexer/db"
"github.com/ethereum-optimism/optimism/go/indexer/services/l1"
"github.com/ethereum-optimism/optimism/go/indexer/services/l2"
database "github.com/ethereum-optimism/optimism/indexer/db"
"github.com/ethereum-optimism/optimism/indexer/services/l1"
"github.com/ethereum-optimism/optimism/indexer/services/l2"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
......
......@@ -6,7 +6,7 @@ import (
"runtime/debug"
"time"
"github.com/ethereum-optimism/optimism/go/indexer/metrics"
"github.com/ethereum-optimism/optimism/indexer/metrics"
"github.com/ethereum/go-ethereum/log"
)
......
......@@ -3,9 +3,9 @@ package services
import (
"net/http"
"github.com/ethereum-optimism/optimism/go/indexer/db"
"github.com/ethereum-optimism/optimism/go/indexer/metrics"
"github.com/ethereum-optimism/optimism/go/indexer/server"
"github.com/ethereum-optimism/optimism/indexer/db"
"github.com/ethereum-optimism/optimism/indexer/metrics"
"github.com/ethereum-optimism/optimism/indexer/server"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/gorilla/mux"
......
......@@ -3,7 +3,7 @@ package bridge
import (
"fmt"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/address_manager"
"github.com/ethereum-optimism/optimism/indexer/bindings/address_manager"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)
......
......@@ -5,9 +5,9 @@ import (
"errors"
"math/big"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/l1bridge"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/scc"
"github.com/ethereum-optimism/optimism/go/indexer/db"
"github.com/ethereum-optimism/optimism/indexer/bindings/l1bridge"
"github.com/ethereum-optimism/optimism/indexer/bindings/scc"
"github.com/ethereum-optimism/optimism/indexer/db"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)
......
......@@ -3,8 +3,8 @@ package bridge
import (
"context"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/l1bridge"
"github.com/ethereum-optimism/optimism/go/indexer/db"
"github.com/ethereum-optimism/optimism/indexer/bindings/l1bridge"
"github.com/ethereum-optimism/optimism/indexer/db"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)
......
......@@ -4,8 +4,8 @@ import (
"context"
"time"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/l1bridge"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/scc"
"github.com/ethereum-optimism/optimism/indexer/bindings/l1bridge"
"github.com/ethereum-optimism/optimism/indexer/bindings/scc"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
)
......
......@@ -3,8 +3,8 @@ package bridge
import (
"context"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/l1bridge"
"github.com/ethereum-optimism/optimism/go/indexer/db"
"github.com/ethereum-optimism/optimism/indexer/bindings/l1bridge"
"github.com/ethereum-optimism/optimism/indexer/db"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)
......
......@@ -7,7 +7,7 @@ import (
"math/big"
"time"
"github.com/ethereum-optimism/optimism/go/indexer/services/util"
"github.com/ethereum-optimism/optimism/indexer/services/util"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
......
......@@ -3,12 +3,12 @@ package l1
import (
"context"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/l1erc20"
"github.com/ethereum-optimism/optimism/indexer/bindings/l1erc20"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/scc"
"github.com/ethereum-optimism/optimism/go/indexer/db"
"github.com/ethereum-optimism/optimism/go/indexer/services/l1/bridge"
"github.com/ethereum-optimism/optimism/indexer/bindings/scc"
"github.com/ethereum-optimism/optimism/indexer/db"
"github.com/ethereum-optimism/optimism/indexer/services/l1/bridge"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)
......
......@@ -11,16 +11,16 @@ import (
"sync/atomic"
"time"
"github.com/ethereum-optimism/optimism/go/indexer/metrics"
"github.com/ethereum-optimism/optimism/indexer/metrics"
"github.com/prometheus/client_golang/prometheus"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/scc"
"github.com/ethereum-optimism/optimism/go/indexer/server"
"github.com/ethereum-optimism/optimism/go/indexer/services/l1/bridge"
"github.com/ethereum-optimism/optimism/indexer/bindings/scc"
"github.com/ethereum-optimism/optimism/indexer/server"
"github.com/ethereum-optimism/optimism/indexer/services/l1/bridge"
_ "github.com/lib/pq"
"github.com/ethereum-optimism/optimism/go/indexer/db"
"github.com/ethereum-optimism/optimism/indexer/db"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
......
......@@ -5,8 +5,8 @@ import (
"errors"
"math/big"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/l2bridge"
"github.com/ethereum-optimism/optimism/go/indexer/db"
"github.com/ethereum-optimism/optimism/indexer/bindings/l2bridge"
"github.com/ethereum-optimism/optimism/indexer/db"
"github.com/ethereum-optimism/optimism/l2geth/accounts/abi/bind"
"github.com/ethereum-optimism/optimism/l2geth/common"
......
......@@ -4,7 +4,7 @@ import (
"context"
"time"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/l2bridge"
"github.com/ethereum-optimism/optimism/indexer/bindings/l2bridge"
"github.com/ethereum-optimism/optimism/l2geth/accounts/abi/bind"
)
......
......@@ -3,8 +3,8 @@ package bridge
import (
"context"
"github.com/ethereum-optimism/optimism/go/indexer/bindings/l2bridge"
"github.com/ethereum-optimism/optimism/go/indexer/db"
"github.com/ethereum-optimism/optimism/indexer/bindings/l2bridge"
"github.com/ethereum-optimism/optimism/indexer/db"
"github.com/ethereum-optimism/optimism/l2geth/accounts/abi/bind"
"github.com/ethereum-optimism/optimism/l2geth/common"
......
......@@ -6,7 +6,7 @@ import (
"math/big"
"time"
"github.com/ethereum-optimism/optimism/go/indexer/services/util"
"github.com/ethereum-optimism/optimism/indexer/services/util"
"github.com/ethereum-optimism/optimism/l2geth/rpc"
"github.com/ethereum-optimism/optimism/l2geth/core/types"
......
package l2
import (
"github.com/ethereum-optimism/optimism/go/indexer/bindings/l2erc20"
"github.com/ethereum-optimism/optimism/go/indexer/db"
"github.com/ethereum-optimism/optimism/indexer/bindings/l2erc20"
"github.com/ethereum-optimism/optimism/indexer/db"
"github.com/ethereum-optimism/optimism/l2geth/accounts/abi/bind"
l2common "github.com/ethereum-optimism/optimism/l2geth/common"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
......
......@@ -12,12 +12,12 @@ import (
l2rpc "github.com/ethereum-optimism/optimism/l2geth/rpc"
"github.com/ethereum-optimism/optimism/go/indexer/metrics"
"github.com/ethereum-optimism/optimism/go/indexer/server"
"github.com/ethereum-optimism/optimism/indexer/metrics"
"github.com/ethereum-optimism/optimism/indexer/server"
"github.com/prometheus/client_golang/prometheus"
"github.com/ethereum-optimism/optimism/go/indexer/db"
"github.com/ethereum-optimism/optimism/go/indexer/services/l2/bridge"
"github.com/ethereum-optimism/optimism/indexer/db"
"github.com/ethereum-optimism/optimism/indexer/services/l2/bridge"
"github.com/ethereum-optimism/optimism/l2geth/common"
"github.com/ethereum-optimism/optimism/l2geth/core/types"
l2ethclient "github.com/ethereum-optimism/optimism/l2geth/ethclient"
......
import { task } from 'hardhat/config'
import { providers } from 'ethers'
import { getChainId } from '@eth-optimism/core-utils'
import { die, logStderr } from '../test/shared/utils'
......@@ -13,22 +14,22 @@ task(
const providerA = new providers.JsonRpcProvider(replicaA)
const providerB = new providers.JsonRpcProvider(replicaB)
let netA
let netB
let chainIdA
let chainIdB
try {
netA = await providerA.getNetwork()
chainIdA = await getChainId(providerA)
} catch (e) {
console.error(`Error getting network from ${replicaA}:`)
console.error(`Error getting network chainId from ${replicaA}:`)
die(e)
}
try {
netB = await providerA.getNetwork()
chainIdB = await getChainId(providerB)
} catch (e) {
console.error(`Error getting network from ${replicaB}:`)
console.error(`Error getting network chainId from ${replicaB}:`)
die(e)
}
if (netA.chainId !== netB.chainId) {
if (chainIdA !== chainIdB) {
die('Chain IDs do not match')
return
}
......
/* Imports: External */
import { expectApprox, sleep } from '@eth-optimism/core-utils'
import { expectApprox, getChainId, sleep } from '@eth-optimism/core-utils'
import { Wallet, BigNumber, Contract, ContractFactory, constants } from 'ethers'
import { serialize } from '@ethersproject/transactions'
import { ethers } from 'hardhat'
......@@ -471,8 +471,7 @@ describe('Basic RPC tests', () => {
describe('eth_chainId', () => {
it('should get the correct chainid', async () => {
const { chainId } = await env.l2Provider.getNetwork()
expect(chainId).to.be.eq(L2_CHAINID)
expect(await getChainId(env.l2Provider)).to.be.eq(L2_CHAINID)
})
})
......
FROM golang:1.16 as builder
FROM golang:1.18.0-alpine3.15 as builder
COPY ./l2geth-exporter /app
COPY ./go/l2geth-exporter /app/
WORKDIR /app/
RUN make build
......
module github.com/ethereum-optimism/optimism/go/l2geth-exporter
module github.com/ethereum-optimism/optimism/l2geth-exporter
go 1.16
......
......@@ -4,7 +4,7 @@ import (
"context"
"math/big"
"github.com/ethereum-optimism/optimism/go/l2geth-exporter/bindings"
"github.com/ethereum-optimism/optimism/l2geth-exporter/bindings"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
......
......@@ -7,7 +7,7 @@ import (
"os"
"time"
"github.com/ethereum-optimism/optimism/go/l2geth-exporter/l1contracts"
"github.com/ethereum-optimism/optimism/l2geth-exporter/l1contracts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
......
......@@ -3,17 +3,16 @@ FROM golang:1.18.0-alpine3.15 as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git
COPY ./l2geth/go.mod ./l2geth/go.sum /go-ethereum/
WORKDIR /go-ethereum
RUN go mod download
COPY ./l2geth ./
COPY ./l2geth /app/l2geth
WORKDIR /app/l2geth
RUN make geth
# Pull Geth into a second stage deploy alpine container
FROM alpine:3.15
RUN apk add --no-cache ca-certificates jq curl
COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/
COPY --from=builder /app/l2geth/build/bin/geth /usr/local/bin/
WORKDIR /usr/local/bin/
EXPOSE 8545 8546 8547
......
FROM golang:1.16 as builder
FROM golang:1.18.0-alpine3.15 as builder
COPY ./op-exporter /app
COPY ./go/op-exporter /app/
WORKDIR /app/
RUN make build
......
......@@ -10,9 +10,9 @@ ifndef BUILDDATE
BUILDDATE := `date +%Y-%m-%d`
endif
LDFLAGSSTRING :=-X github.com/ethereum-optimism/optimism/go/op_exporter/version.Version=$(VERSION)
LDFLAGSSTRING +=-X github.com/ethereum-optimism/optimism/go/op_exporter/version.GitCommit=$(GITCOMMIT)
LDFLAGSSTRING +=-X github.com/ethereum-optimism/optimism/go/op_exporter/version.BuildDate=$(BUILDDATE)
LDFLAGSSTRING :=-X github.com/ethereum-optimism/op_exporter/version.Version=$(VERSION)
LDFLAGSSTRING +=-X github.com/ethereum-optimism/op_exporter/version.GitCommit=$(GITCOMMIT)
LDFLAGSSTRING +=-X github.com/ethereum-optimism/op_exporter/version.BuildDate=$(BUILDDATE)
LDFLAGS :=-ldflags "$(LDFLAGSSTRING)"
......
module github.com/ethereum-optimism/optimism/go/op-exporter
module github.com/ethereum-optimism/optimism/op-exporter
go 1.16
require (
github.com/ethereum-optimism/optimism/go/op_exporter v0.0.0-20211207210647-c5a8db939ad4
github.com/ethereum/go-ethereum v1.10.4
github.com/prometheus/client_golang v1.4.0
github.com/sirupsen/logrus v1.4.2
......
......@@ -124,8 +124,8 @@ github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkg
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/optimism/go/op_exporter v0.0.0-20211207210647-c5a8db939ad4 h1:tTFuDWKAWayPWkqWd8CPwSfDoO4tMVScZgM5nhXs2+c=
github.com/ethereum-optimism/optimism/go/op_exporter v0.0.0-20211207210647-c5a8db939ad4/go.mod h1:w2HqYJpeDcqB6ZpPDsD7NmkvJCDAs23uNW7YHTn4g0M=
github.com/ethereum-optimism/op_exporter v0.0.0-20211207210647-c5a8db939ad4 h1:tTFuDWKAWayPWkqWd8CPwSfDoO4tMVScZgM5nhXs2+c=
github.com/ethereum-optimism/op_exporter v0.0.0-20211207210647-c5a8db939ad4/go.mod h1:w2HqYJpeDcqB6ZpPDsD7NmkvJCDAs23uNW7YHTn4g0M=
github.com/ethereum/go-ethereum v1.10.4 h1:JPZPL2MHbegfFStcaOrrggMVIcf57OQHQ0J3UhjQ+xQ=
github.com/ethereum/go-ethereum v1.10.4/go.mod h1:nEE0TP5MtxGzOMd7egIrbPJMQBnhVU3ELNxhBglIzhg=
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
......
......@@ -10,8 +10,8 @@ import (
"sync"
"time"
"github.com/ethereum-optimism/optimism/go/op_exporter/k8sClient"
"github.com/ethereum-optimism/optimism/go/op_exporter/version"
"github.com/ethereum-optimism/op_exporter/k8sClient"
"github.com/ethereum-optimism/op_exporter/version"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/prometheus/client_golang/prometheus/promhttp"
log "github.com/sirupsen/logrus"
......
......@@ -73,7 +73,7 @@ services:
- deployer
build:
context: ..
dockerfile: ./ops/docker/Dockerfile.geth
dockerfile: ./l2geth/Dockerfile
image: ethereumoptimism/l2geth:${DOCKER_TAG_L2GETH:-latest}
# override with the geth script and the env vars required for it
entrypoint: sh ./geth.sh
......@@ -127,7 +127,7 @@ services:
replicas: 1
build:
context: ..
dockerfile: ./ops/docker/Dockerfile.geth
dockerfile: ./l2geth/Dockerfile
image: ethereumoptimism/l2geth:${DOCKER_TAG_L2GETH:-latest}
entrypoint: sh ./geth.sh
env_file:
......@@ -154,7 +154,7 @@ services:
replicas: 1
build:
context: ..
dockerfile: ./ops/docker/Dockerfile.geth
dockerfile: ./l2geth/Dockerfile
image: ethereumoptimism/l2geth:${DOCKER_TAG_L2GETH:-latest}
entrypoint: sh ./geth.sh
env_file:
......@@ -221,7 +221,7 @@ services:
replicas: 0
build:
context: ..
dockerfile: ./ops/docker/Dockerfile.gas-oracle
dockerfile: ./gas-oracle/Dockerfile
image: ethereumoptimism/gas-oracle:${DOCKER_TAG_GAS_ORACLE:-latest}
environment:
GAS_PRICE_ORACLE_ETHEREUM_HTTP_URL: http://l1_chain:8545
......@@ -236,7 +236,7 @@ services:
- l2geth
build:
context: ..
dockerfile: ./ops/docker/Dockerfile.batch-submitter-service
dockerfile: ./batch-submitter/Dockerfile
image: ethereumoptimism/batch-submitter-service:${DOCKER_TAG_BATCH_SUBMITTER_SERVICE:-latest}
entrypoint: ./batch-submitter.sh
env_file:
......
......@@ -15,7 +15,7 @@ function build() {
}
mkdir -p /tmp/.buildx-cache-new
build l2geth "ethereumoptimism/l2geth:latest" "./ops/docker/Dockerfile.geth" .
build l2geth "ethereumoptimism/l2geth:latest" "./l2geth/Dockerfile" .
build l1chain "ethereumoptimism/hardhat:latest" "./ops/docker/hardhat/Dockerfile" ./ops/docker/hardhat
wait
......
......@@ -6,13 +6,19 @@
"workspaces": {
"packages": [
"packages/*",
"l2geth",
"batch-submitter",
"bss-core",
"gas-oracle",
"indexer",
"integration-tests",
"go/*",
"l2geth-exporter",
"l2geth",
"ops/docker/rpc-proxy",
"ops/docker/hardhat",
"ops/docker/go-builder",
"ops/docker/js-builder"
"ops/docker/js-builder",
"proxyd",
"teleportr"
],
"nohoist": [
"**/typechain",
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract Helper_SimpleProxy {
address internal owner;
address internal target;
constructor() {
owner = msg.sender;
}
fallback() external {
makeExternalCall(target, msg.data);
}
function setTarget(address _target) public {
if (msg.sender == owner) {
target = _target;
} else {
makeExternalCall(target, msg.data);
}
}
function makeExternalCall(address _target, bytes memory _calldata) internal {
(bool success, bytes memory returndata) = _target.call(_calldata);
if (success) {
assembly {
return(add(returndata, 0x20), mload(returndata))
}
} else {
assembly {
revert(add(returndata, 0x20), mload(returndata))
}
}
}
}
import { ethers, Contract } from 'ethers'
import { Provider } from '@ethersproject/abstract-provider'
import { Signer } from '@ethersproject/abstract-signer'
import { sleep, awaitCondition } from '@eth-optimism/core-utils'
import { sleep, awaitCondition, getChainId } from '@eth-optimism/core-utils'
import { HttpNetworkConfig } from 'hardhat/types'
import { getDeployConfig } from './deploy-config'
......@@ -270,8 +270,7 @@ export const getContractFromArtifact = async (
}
export const isHardhatNode = async (hre) => {
const { chainId } = await hre.ethers.provider.getNetwork()
return chainId === 31337
return (await getChainId(hre.ethers.provider)) === 31337
}
// Large balance to fund accounts with.
......
......@@ -34,7 +34,7 @@ describe('L1CrossDomainMessenger', () => {
let Fake__L2CrossDomainMessenger: FakeContract
let Fake__StateCommitmentChain: FakeContract
before(async () => {
Fake__TargetContract = await smock.fake<Contract>('Helper_SimpleProxy')
Fake__TargetContract = await smock.fake<Contract>('TestERC20')
Fake__L2CrossDomainMessenger = await smock.fake<Contract>(
'L2CrossDomainMessenger',
{
......@@ -403,20 +403,18 @@ describe('L1CrossDomainMessenger', () => {
let calldata: string
before(async () => {
target = Fake__TargetContract.address
message = Fake__TargetContract.interface.encodeFunctionData('setTarget', [
message = Fake__TargetContract.interface.encodeFunctionData('mint', [
NON_ZERO_ADDRESS,
ethers.utils.parseEther('1'),
])
const mockProof = await generateMockRelayMessageProof(
;({ proof, calldata } = await generateMockRelayMessageProof(
target,
signer1.address,
message
)
proof = mockProof.proof
calldata = mockProof.calldata
))
})
beforeEach(async () => {
beforeEach(() => {
Fake__StateCommitmentChain.verifyStateCommitment.returns(true)
Fake__StateCommitmentChain.insideFraudProofWindow.returns(false)
})
......
......@@ -24,7 +24,7 @@ describe('L2CrossDomainMessenger', () => {
let Fake__L1CrossDomainMessenger: FakeContract
let Fake__OVM_L2ToL1MessagePasser: FakeContract
before(async () => {
Fake__TargetContract = await smock.fake<Contract>('Helper_SimpleProxy')
Fake__TargetContract = await smock.fake<Contract>('TestERC20')
Fake__L1CrossDomainMessenger = await smock.fake<Contract>(
'L1CrossDomainMessenger'
)
......@@ -45,6 +45,7 @@ describe('L2CrossDomainMessenger', () => {
let L2CrossDomainMessenger: Contract
beforeEach(async () => {
L2CrossDomainMessenger = await deploy('L2CrossDomainMessenger', {
signer: impersonatedL1CrossDomainMessengerSender,
args: [Fake__L1CrossDomainMessenger.address],
})
})
......@@ -72,7 +73,7 @@ describe('L2CrossDomainMessenger', () => {
describe('sendMessage', () => {
it('should be able to send a single message', async () => {
await expect(
L2CrossDomainMessenger.sendMessage(
L2CrossDomainMessenger.connect(signer).sendMessage(
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
100_000
......@@ -84,7 +85,7 @@ describe('L2CrossDomainMessenger', () => {
).to.deep.equal(
encodeXDomainCalldata(
NON_ZERO_ADDRESS,
await signer.getAddress(),
signer.address,
NON_NULL_BYTES32,
0
)
......@@ -92,14 +93,14 @@ describe('L2CrossDomainMessenger', () => {
})
it('should be able to send the same message twice', async () => {
await L2CrossDomainMessenger.sendMessage(
await L2CrossDomainMessenger.connect(signer).sendMessage(
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
100_000
)
await expect(
L2CrossDomainMessenger.sendMessage(
L2CrossDomainMessenger.connect(signer).sendMessage(
NON_ZERO_ADDRESS,
NON_NULL_BYTES32,
100_000
......@@ -109,32 +110,36 @@ describe('L2CrossDomainMessenger', () => {
})
describe('relayMessage', () => {
let target: string
let message: string
before(() => {
target = Fake__TargetContract.address
message = Fake__TargetContract.interface.encodeFunctionData('mint', [
NON_ZERO_ADDRESS,
ethers.utils.parseEther('1'),
])
})
it('should revert if the L1 message sender is not the L1CrossDomainMessenger', async () => {
await expect(
L2CrossDomainMessenger.connect(signer).relayMessage(
Fake__TargetContract.address,
target,
signer.address,
Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
message,
0
)
).to.be.revertedWith('Provided message could not be verified.')
})
it('should send a call to the target contract', async () => {
await L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender
).relayMessage(
Fake__TargetContract.address,
await L2CrossDomainMessenger.relayMessage(
target,
signer.address,
Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
message,
0
)
expect(Fake__TargetContract.setTarget.getCall(0).args[0]).to.deep.equal(
expect(Fake__TargetContract.mint.getCall(0).args[0]).to.deep.equal(
NON_ZERO_ADDRESS
)
})
......@@ -144,14 +149,10 @@ describe('L2CrossDomainMessenger', () => {
L2CrossDomainMessenger.xDomainMessageSender()
).to.be.revertedWith('xDomainMessageSender is not set')
await L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender
).relayMessage(
Fake__TargetContract.address,
await L2CrossDomainMessenger.relayMessage(
target,
signer.address,
Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
message,
0
)
......@@ -161,35 +162,20 @@ describe('L2CrossDomainMessenger', () => {
})
it('should revert if trying to send the same message twice', async () => {
await L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender
).relayMessage(
Fake__TargetContract.address,
await L2CrossDomainMessenger.relayMessage(
target,
signer.address,
Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
message,
0
)
await expect(
L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender
).relayMessage(
Fake__TargetContract.address,
signer.address,
Fake__TargetContract.interface.encodeFunctionData('setTarget', [
NON_ZERO_ADDRESS,
]),
0
)
L2CrossDomainMessenger.relayMessage(target, signer.address, message, 0)
).to.be.revertedWith('Provided message has already been received.')
})
it('should not make a call if the target is the L2 MessagePasser', async () => {
const resProm = L2CrossDomainMessenger.connect(
impersonatedL1CrossDomainMessengerSender
).relayMessage(
const tx = await L2CrossDomainMessenger.relayMessage(
predeploys.OVM_L2ToL1MessagePasser,
signer.address,
Fake__OVM_L2ToL1MessagePasser.interface.encodeFunctionData(
......@@ -199,21 +185,10 @@ describe('L2CrossDomainMessenger', () => {
0
)
// The call to relayMessage() should succeed.
await expect(resProm).to.not.be.reverted
// There should be no 'relayedMessage' event logged in the receipt.
expect(
(
await Fake__OVM_L2ToL1MessagePasser.provider.getTransactionReceipt(
(
await resProm
).hash
)
).logs
(await ethers.provider.getTransactionReceipt(tx.hash)).logs
).to.deep.equal([])
// The message should be registered as successful.
expect(
await L2CrossDomainMessenger.successfulMessages(
ethers.utils.solidityKeccak256(
......
/* External Imports */
import { ethers } from 'hardhat'
import { Contract } from 'ethers'
/* Internal Imports */
import { expect } from '../../../setup'
import { NON_ZERO_ADDRESS } from '../../../helpers'
import { NON_ZERO_ADDRESS, deploy } from '../../../helpers'
describe('Lib_OVMCodec', () => {
let Lib_OVMCodec: Contract
before(async () => {
Lib_OVMCodec = await (
await ethers.getContractFactory('TestLib_OVMCodec')
).deploy()
Lib_OVMCodec = await deploy('TestLib_OVMCodec')
})
describe('hashTransaction', () => {
......
/* Internal Imports */
import { Lib_RLPReader_TEST_JSON } from '../../../data'
import { runJsonTest } from '../../../helpers'
......
/* External Imports */
import { ethers } from 'hardhat'
import { Contract } from 'ethers'
/* Internal Imports */
import { expect } from '../../../setup'
import { Lib_RLPWriter_TEST_JSON } from '../../../data'
import { deploy } from '../../../helpers'
const encode = async (Lib_RLPWriter: Contract, input: any): Promise<void> => {
if (Array.isArray(input)) {
const elements = await Promise.all(
return Lib_RLPWriter.writeList(
await Promise.all(
input.map(async (el) => {
return encode(Lib_RLPWriter, el)
})
)
return Lib_RLPWriter.writeList(elements)
)
} else if (Number.isInteger(input)) {
return Lib_RLPWriter.writeUint(input)
} else {
......@@ -25,9 +23,7 @@ const encode = async (Lib_RLPWriter: Contract, input: any): Promise<void> => {
describe('Lib_RLPWriter', () => {
let Lib_RLPWriter: Contract
before(async () => {
Lib_RLPWriter = await (
await ethers.getContractFactory('TestLib_RLPWriter')
).deploy()
Lib_RLPWriter = await deploy('TestLib_RLPWriter')
})
describe('Official Ethereum RLP Tests', () => {
......@@ -50,14 +46,11 @@ describe('Lib_RLPWriter', () => {
describe('Use of library with other memory-modifying operations', () => {
it('should allow creation of a contract beforehand and still work', async () => {
const randomAddress = '0x1234123412341234123412341234123412341234'
const rlpEncodedRandomAddress =
'0x941234123412341234123412341234123412341234'
const encoded =
expect(
await Lib_RLPWriter.callStatic.writeAddressWithTaintedMemory(
randomAddress
'0x1234123412341234123412341234123412341234'
)
expect(encoded).to.eq(rlpEncodedRandomAddress)
).to.eq('0x941234123412341234123412341234123412341234')
})
})
})
/* External Imports */
import { ethers } from 'hardhat'
import { Contract } from 'ethers'
import { applyL1ToL2Alias, undoL1ToL2Alias } from '@eth-optimism/core-utils'
import { expect } from '../../../setup'
import { deploy } from '../../../helpers'
describe('AddressAliasHelper', () => {
let AddressAliasHelper: Contract
before(async () => {
AddressAliasHelper = await (
await ethers.getContractFactory('TestLib_AddressAliasHelper')
).deploy()
AddressAliasHelper = await deploy('TestLib_AddressAliasHelper')
})
describe('applyL1ToL2Alias', () => {
......
/* External Imports */
import * as rlp from 'rlp'
import { ethers } from 'hardhat'
import { Contract } from 'ethers'
import { toHexString } from '@eth-optimism/core-utils'
/* Internal Imports */
import { expect } from '../../../setup'
import { TrieTestGenerator } from '../../../helpers'
import { deploy, TrieTestGenerator } from '../../../helpers'
const NODE_COUNTS = [1, 2, 32, 128]
describe('Lib_MerkleTrie', () => {
let Lib_MerkleTrie: Contract
before(async () => {
Lib_MerkleTrie = await (
await ethers.getContractFactory('TestLib_MerkleTrie')
).deploy()
Lib_MerkleTrie = await deploy('TestLib_MerkleTrie')
})
// Eth-foundation tests: https://github.com/ethereum/tests/tree/develop/TrieTests
......
/* External Imports */
import { ethers } from 'hardhat'
import { Contract } from 'ethers'
/* Internal Imports */
import { expect } from '../../../setup'
import { TrieTestGenerator } from '../../../helpers'
import { deploy, TrieTestGenerator } from '../../../helpers'
const NODE_COUNTS = [1, 2, 128]
describe('Lib_SecureMerkleTrie', () => {
let Lib_SecureMerkleTrie: Contract
before(async () => {
Lib_SecureMerkleTrie = await (
await ethers.getContractFactory('TestLib_SecureMerkleTrie')
).deploy()
Lib_SecureMerkleTrie = await deploy('TestLib_SecureMerkleTrie')
})
describe('verifyInclusionProof', () => {
......
import hre from 'hardhat'
import { Contract, ethers } from 'ethers'
import { expect } from '../../../setup'
import { deploy } from '../../../helpers'
describe('Lib_Buffer', () => {
let Lib_Buffer: Contract
beforeEach(async () => {
const Factory__Lib_Buffer = await hre.ethers.getContractFactory(
'TestLib_Buffer'
)
Lib_Buffer = await Factory__Lib_Buffer.deploy()
Lib_Buffer = await deploy('TestLib_Buffer')
})
describe('push(bytes32,bytes27)', () => {
......
/* Internal Imports */
import { Lib_Bytes32Utils_TEST_JSON } from '../../../data'
import { runJsonTest } from '../../../helpers'
......
/* Internal Imports */
import { ethers } from 'hardhat'
import { Contract } from 'ethers'
/* External Imports */
import { Lib_BytesUtils_TEST_JSON } from '../../../data'
import { runJsonTest } from '../../../helpers'
import { expect } from '../../../setup'
import { Lib_BytesUtils_TEST_JSON } from '../../../data'
import { deploy, runJsonTest } from '../../../helpers'
describe('Lib_BytesUtils', () => {
describe('JSON tests', () => {
......@@ -15,18 +12,17 @@ describe('Lib_BytesUtils', () => {
describe('Use of library with other memory-modifying operations', () => {
let TestLib_BytesUtils: Contract
before(async () => {
TestLib_BytesUtils = await (
await ethers.getContractFactory('TestLib_BytesUtils')
).deploy()
TestLib_BytesUtils = await deploy('TestLib_BytesUtils')
})
it('should allow creation of a contract beforehand and still work', async () => {
const slice = await TestLib_BytesUtils.callStatic.sliceWithTaintedMemory(
expect(
await TestLib_BytesUtils.callStatic.sliceWithTaintedMemory(
'0x123412341234',
0,
0
)
expect(slice).to.eq('0x')
).to.eq('0x')
})
})
})
/* External Imports */
import { ethers } from 'hardhat'
import { Contract, BigNumber } from 'ethers'
import { MerkleTree } from 'merkletreejs'
import { fromHexString, toHexString } from '@eth-optimism/core-utils'
import { smock, FakeContract } from '@defi-wonderland/smock'
/* Internal Imports */
import { expect } from '../../../setup'
import { NON_NULL_BYTES32 } from '../../../helpers'
import { deploy, NON_NULL_BYTES32 } from '../../../helpers'
const NODE_COUNTS = [
2, 3, 7, 9, 13, 63, 64, 123, 128, 129, 255, 1021, 1023, 1024,
]
const hash = (el: Buffer | string): Buffer => {
return Buffer.from(ethers.utils.keccak256(el).slice(2), 'hex')
}
const fillDefaultHashes = (elements: string[]): string[] => {
const makeMerkleTree = (elements: string[]): MerkleTree => {
const filled: string[] = []
for (let i = 0; i < Math.pow(2, Math.ceil(Math.log2(elements.length))); i++) {
if (i < elements.length) {
filled.push(elements[i])
......@@ -28,17 +21,19 @@ const fillDefaultHashes = (elements: string[]): string[] => {
}
}
return filled
return new MerkleTree(
filled.map(fromHexString),
(el: Buffer | string): Buffer => {
return fromHexString(ethers.utils.keccak256(el))
}
)
}
describe('Lib_MerkleTree', () => {
let Lib_MerkleTree: Contract
let Fake__LibMerkleTree: FakeContract<Contract>
before(async () => {
Lib_MerkleTree = await (
await ethers.getContractFactory('TestLib_MerkleTree')
).deploy()
Lib_MerkleTree = await deploy('TestLib_MerkleTree')
Fake__LibMerkleTree = await smock.fake('TestLib_MerkleTree')
})
......@@ -66,17 +61,13 @@ describe('Lib_MerkleTree', () => {
describe('when more than one element is provided', () => {
for (const size of NODE_COUNTS) {
it(`should generate the correct root when ${size} elements are provided`, async () => {
const elements = [...Array(size)].map((_, i) => {
const tree = makeMerkleTree(
[...Array(size)].map((_, i) => {
return ethers.utils.keccak256(BigNumber.from(i).toHexString())
})
)
const bufs = fillDefaultHashes(elements).map((element) => {
return fromHexString(element)
})
const tree = new MerkleTree(bufs, hash)
expect(await Lib_MerkleTree.getMerkleRoot(bufs)).to.equal(
expect(await Lib_MerkleTree.getMerkleRoot(tree.getLeaves())).to.equal(
toHexString(tree.getRoot())
)
})
......@@ -116,24 +107,6 @@ describe('Lib_MerkleTree', () => {
})
})
describe('when total elements is zero', () => {
const totalLeaves = 0
it('should revert', async () => {
await expect(
Lib_MerkleTree.verify(
ethers.constants.HashZero,
ethers.constants.HashZero,
0,
[],
totalLeaves
)
).to.be.revertedWith(
'Lib_MerkleTree: Total leaves must be greater than zero.'
)
})
})
describe('when an index is out of bounds', () => {
const totalLeaves = 1
const index = 2
......@@ -187,26 +160,24 @@ describe('Lib_MerkleTree', () => {
describe('with valid proof for more than one element', () => {
for (const size of NODE_COUNTS) {
describe(`for a tree with ${size} total elements`, () => {
const elements = [...Array(size)].map((_, i) => {
const tree = makeMerkleTree(
[...Array(size)].map((_, i) => {
return ethers.utils.keccak256(BigNumber.from(i).toHexString())
})
const bufs = fillDefaultHashes(elements).map((element) => {
return fromHexString(element)
})
const tree = new MerkleTree(bufs, hash)
)
for (let i = 0; i < size; i += Math.ceil(size / 8)) {
it(`should verify a proof for the ${i}(th/st/rd, whatever) element`, async () => {
const proof = tree.getProof(bufs[i], i).map((element) => {
const proof = tree
.getProof(tree.getLeaves()[i], i)
.map((element) => {
return element.data
})
expect(
await Lib_MerkleTree.verify(
tree.getRoot(),
bufs[i],
tree.getLeaves()[i],
i,
proof,
size
......
/* Imports: External */
import { BaseService, Metrics } from '@eth-optimism/common-ts'
import { StaticJsonRpcProvider } from '@ethersproject/providers'
import { sleep, toRpcHexString } from '@eth-optimism/core-utils'
import { getChainId, sleep, toRpcHexString } from '@eth-optimism/core-utils'
import { BigNumber } from 'ethers'
import { LevelUp } from 'levelup'
import axios from 'axios'
......@@ -127,9 +127,9 @@ export class L2IngestionService extends BaseService<L2IngestionServiceOptions> {
}
protected async checkConsistency(): Promise<void> {
const network = await this.state.l2RpcProvider.getNetwork()
const chainId = await getChainId(this.state.l2RpcProvider)
const shouldDoCheck = !(await this.state.db.getConsistencyCheckFlag())
if (shouldDoCheck && network.chainId === 69) {
if (shouldDoCheck && chainId === 69) {
this.logger.info('performing consistency check')
const highestBlock =
await this.state.db.getHighestSyncedUnconfirmedBlock()
......
/* Imports: External */
import { Signer } from 'ethers'
import { sleep } from '@eth-optimism/core-utils'
import { getChainId, sleep } from '@eth-optimism/core-utils'
import {
BaseServiceV2,
validators,
......@@ -80,12 +80,10 @@ export class MessageRelayerService extends BaseServiceV2<
this.options.l1RpcProvider
)
const l1Network = await this.state.wallet.provider.getNetwork()
const l1ChainId = l1Network.chainId
this.state.messenger = new CrossChainMessenger({
l1SignerOrProvider: this.state.wallet,
l2SignerOrProvider: this.options.l2RpcProvider,
l1ChainId,
l1ChainId: await getChainId(this.state.wallet.provider),
})
this.state.highestCheckedL2Tx = this.options.fromL2TransactionIndex || 1
......
......@@ -6,14 +6,15 @@ ARG GITVERSION=docker
RUN apk add make jq git gcc musl-dev linux-headers
COPY ./proxyd /app
WORKDIR /app
COPY ./go/proxyd /app
RUN make proxyd
FROM alpine:3.15
COPY ./go/proxyd/entrypoint.sh /bin/entrypoint.sh
COPY ./proxyd/entrypoint.sh /bin/entrypoint.sh
RUN apk update && \
apk add ca-certificates && \
......
......@@ -12,6 +12,7 @@ import (
"math"
"math/rand"
"net/http"
"sort"
"strconv"
"strings"
"time"
......@@ -199,13 +200,13 @@ func NewBackend(
return backend
}
func (b *Backend) Forward(ctx context.Context, req *RPCReq) (*RPCRes, error) {
func (b *Backend) Forward(ctx context.Context, reqs []*RPCReq, isBatch bool) ([]*RPCRes, error) {
if !b.Online() {
RecordRPCError(ctx, b.Name, req.Method, ErrBackendOffline)
RecordBatchRPCError(ctx, b.Name, reqs, ErrBackendOffline)
return nil, ErrBackendOffline
}
if b.IsRateLimited() {
RecordRPCError(ctx, b.Name, req.Method, ErrBackendOverCapacity)
RecordBatchRPCError(ctx, b.Name, reqs, ErrBackendOverCapacity)
return nil, ErrBackendOverCapacity
}
......@@ -213,9 +214,20 @@ func (b *Backend) Forward(ctx context.Context, req *RPCReq) (*RPCRes, error) {
// <= to account for the first attempt not technically being
// a retry
for i := 0; i <= b.maxRetries; i++ {
RecordRPCForward(ctx, b.Name, req.Method, RPCRequestSourceHTTP)
respTimer := prometheus.NewTimer(rpcBackendRequestDurationSumm.WithLabelValues(b.Name, req.Method))
res, err := b.doForward(ctx, req)
RecordBatchRPCForward(ctx, b.Name, reqs, RPCRequestSourceHTTP)
metricLabelMethod := reqs[0].Method
if isBatch {
metricLabelMethod = "<batch>"
}
timer := prometheus.NewTimer(
rpcBackendRequestDurationSumm.WithLabelValues(
b.Name,
metricLabelMethod,
strconv.FormatBool(isBatch),
),
)
res, err := b.doForward(ctx, reqs, isBatch)
if err != nil {
lastError = err
log.Warn(
......@@ -224,31 +236,14 @@ func (b *Backend) Forward(ctx context.Context, req *RPCReq) (*RPCRes, error) {
"req_id", GetReqID(ctx),
"err", err,
)
respTimer.ObserveDuration()
RecordRPCError(ctx, b.Name, req.Method, err)
timer.ObserveDuration()
RecordBatchRPCError(ctx, b.Name, reqs, err)
sleepContext(ctx, calcBackoff(i))
continue
}
respTimer.ObserveDuration()
if res.IsError() {
RecordRPCError(ctx, b.Name, req.Method, res.Error)
log.Info(
"backend responded with RPC error",
"backend", b.Name,
"code", res.Error.Code,
"msg", res.Error.Message,
"req_id", GetReqID(ctx),
"source", "rpc",
"auth", GetAuthCtx(ctx),
)
} else {
log.Info("forwarded RPC request",
"backend", b.Name,
"method", req.Method,
"auth", GetAuthCtx(ctx),
"req_id", GetReqID(ctx),
)
}
timer.ObserveDuration()
MaybeRecordErrorsInRPCRes(ctx, b.Name, reqs, res)
return res, nil
}
......@@ -337,8 +332,8 @@ func (b *Backend) setOffline() {
}
}
func (b *Backend) doForward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, error) {
body := mustMarshalJSON(rpcReq)
func (b *Backend) doForward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool) ([]*RPCRes, error) {
body := mustMarshalJSON(rpcReqs)
httpReq, err := http.NewRequestWithContext(ctx, "POST", b.rpcURL, bytes.NewReader(body))
if err != nil {
......@@ -367,11 +362,16 @@ func (b *Backend) doForward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, error
return nil, wrapErr(err, "error in backend request")
}
metricLabelMethod := rpcReqs[0].Method
if isBatch {
metricLabelMethod = "<batch>"
}
rpcBackendHTTPResponseCodesTotal.WithLabelValues(
GetAuthCtx(ctx),
b.Name,
rpcReq.Method,
metricLabelMethod,
strconv.Itoa(httpRes.StatusCode),
strconv.FormatBool(isBatch),
).Inc()
// Alchemy returns a 400 on bad JSONs, so handle that case
......@@ -385,30 +385,60 @@ func (b *Backend) doForward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, error
return nil, wrapErr(err, "error reading response body")
}
res := new(RPCRes)
if err := json.Unmarshal(resB, res); err != nil {
var res []*RPCRes
if err := json.Unmarshal(resB, &res); err != nil {
return nil, ErrBackendBadResponse
}
// Alas! Certain node providers (Infura) always return a single JSON object for some types of errors
if len(rpcReqs) != len(res) {
return nil, ErrBackendBadResponse
}
// capture the HTTP status code in the response. this will only
// ever be 400 given the status check on line 318 above.
if httpRes.StatusCode != 200 {
for _, res := range res {
res.Error.HTTPErrorCode = httpRes.StatusCode
}
}
sortBatchRPCResponse(rpcReqs, res)
return res, nil
}
// sortBatchRPCResponse sorts the RPCRes slice according to the position of its corresponding ID in the RPCReq slice
func sortBatchRPCResponse(req []*RPCReq, res []*RPCRes) {
pos := make(map[string]int, len(req))
for i, r := range req {
key := string(r.ID)
if _, ok := pos[key]; ok {
panic("bug! detected requests with duplicate IDs")
}
pos[key] = i
}
sort.Slice(res, func(i, j int) bool {
l := res[i].ID
r := res[j].ID
return pos[string(l)] < pos[string(r)]
})
}
type BackendGroup struct {
Name string
Backends []*Backend
}
func (b *BackendGroup) Forward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, error) {
func (b *BackendGroup) Forward(ctx context.Context, rpcReqs []*RPCReq, isBatch bool) ([]*RPCRes, error) {
if len(rpcReqs) == 0 {
return nil, nil
}
rpcRequestsTotal.Inc()
for _, back := range b.Backends {
res, err := back.Forward(ctx, rpcReq)
res, err := back.Forward(ctx, rpcReqs, isBatch)
if errors.Is(err, ErrMethodNotWhitelisted) {
return nil, err
}
......@@ -712,3 +742,44 @@ func (c *LimitedHTTPClient) DoLimited(req *http.Request) (*http.Response, error)
defer c.sem.Release(1)
return c.Do(req)
}
func RecordBatchRPCError(ctx context.Context, backendName string, reqs []*RPCReq, err error) {
for _, req := range reqs {
RecordRPCError(ctx, backendName, req.Method, err)
}
}
func MaybeRecordErrorsInRPCRes(ctx context.Context, backendName string, reqs []*RPCReq, resBatch []*RPCRes) {
log.Info("forwarded RPC request",
"backend", backendName,
"auth", GetAuthCtx(ctx),
"req_id", GetReqID(ctx),
"batch_size", len(reqs),
)
var lastError *RPCErr
for i, res := range resBatch {
if res.IsError() {
lastError = res.Error
RecordRPCError(ctx, backendName, reqs[i].Method, res.Error)
}
}
if lastError != nil {
log.Info(
"backend responded with RPC error",
"backend", backendName,
"last_error_code", lastError.Code,
"last_error_msg", lastError.Message,
"req_id", GetReqID(ctx),
"source", "rpc",
"auth", GetAuthCtx(ctx),
)
}
}
func RecordBatchRPCForward(ctx context.Context, backendName string, reqs []*RPCReq, source string) {
for _, req := range reqs {
RecordRPCForward(ctx, backendName, req.Method, source)
}
}
......@@ -6,7 +6,7 @@ import (
"syscall"
"github.com/BurntSushi/toml"
"github.com/ethereum-optimism/optimism/go/proxyd"
"github.com/ethereum-optimism/optimism/proxyd"
"github.com/ethereum/go-ethereum/log"
)
......
......@@ -16,6 +16,8 @@ type ServerConfig struct {
// TimeoutSeconds specifies the maximum time spent serving an HTTP request. Note that isn't used for websocket connections
TimeoutSeconds int `toml:"timeout_seconds"`
MaxUpstreamBatchSize int `toml:"max_upstream_batch_size"`
}
type CacheConfig struct {
......
module github.com/ethereum-optimism/optimism/go/proxyd
module github.com/ethereum-optimism/optimism/proxyd
go 1.16
......
......@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/ethereum-optimism/optimism/go/proxyd"
"github.com/ethereum-optimism/optimism/proxyd"
"github.com/stretchr/testify/require"
)
......@@ -29,7 +29,7 @@ func TestBatchTimeout(t *testing.T) {
slowBackend.SetHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// check the config. The sleep duration should be at least double the server.timeout_seconds config to prevent flakes
time.Sleep(time.Second * 2)
SingleResponseHandler(200, goodResponse)(w, r)
BatchedResponseHandler(200, goodResponse)(w, r)
}))
res, statusCode, err := client.SendBatchRPC(
NewRPCReq("1", "eth_chainId", nil),
......
package integration_tests
import (
"net/http"
"os"
"testing"
"github.com/ethereum-optimism/optimism/proxyd"
"github.com/stretchr/testify/require"
)
func TestBatching(t *testing.T) {
config := ReadConfig("batching")
chainIDResponse1 := `{"jsonrpc": "2.0", "result": "hello1", "id": 1}`
chainIDResponse2 := `{"jsonrpc": "2.0", "result": "hello2", "id": 2}`
chainIDResponse3 := `{"jsonrpc": "2.0", "result": "hello3", "id": 3}`
netVersionResponse1 := `{"jsonrpc": "2.0", "result": "1.0", "id": 1}`
callResponse1 := `{"jsonrpc": "2.0", "result": "ekans1", "id": 1}`
type mockResult struct {
method string
id string
result interface{}
}
chainIDMock1 := mockResult{"eth_chainId", "1", "hello1"}
chainIDMock2 := mockResult{"eth_chainId", "2", "hello2"}
chainIDMock3 := mockResult{"eth_chainId", "3", "hello3"}
netVersionMock1 := mockResult{"net_version", "1", "1.0"}
callMock1 := mockResult{"eth_call", "1", "ekans1"}
tests := []struct {
name string
handler http.Handler
mocks []mockResult
reqs []*proxyd.RPCReq
expectedRes string
maxBatchSize int
numExpectedForwards int
}{
{
name: "backend returns batches out of order",
mocks: []mockResult{chainIDMock1, chainIDMock2, chainIDMock3},
reqs: []*proxyd.RPCReq{
NewRPCReq("1", "eth_chainId", nil),
NewRPCReq("2", "eth_chainId", nil),
NewRPCReq("3", "eth_chainId", nil),
},
expectedRes: asArray(chainIDResponse1, chainIDResponse2, chainIDResponse3),
maxBatchSize: 2,
numExpectedForwards: 2,
},
{
// infura behavior
name: "backend returns single RPC response object as error",
handler: SingleResponseHandler(500, `{"jsonrpc":"2.0","error":{"code":-32001,"message":"internal server error"},"id":1}`),
reqs: []*proxyd.RPCReq{
NewRPCReq("1", "eth_chainId", nil),
NewRPCReq("2", "eth_chainId", nil),
},
expectedRes: asArray(
`{"error":{"code":-32011,"message":"no backends available for method"},"id":1,"jsonrpc":"2.0"}`,
`{"error":{"code":-32011,"message":"no backends available for method"},"id":2,"jsonrpc":"2.0"}`,
),
maxBatchSize: 10,
numExpectedForwards: 1,
},
{
name: "backend returns single RPC response object for minibatches",
handler: SingleResponseHandler(500, `{"jsonrpc":"2.0","error":{"code":-32001,"message":"internal server error"},"id":1}`),
reqs: []*proxyd.RPCReq{
NewRPCReq("1", "eth_chainId", nil),
NewRPCReq("2", "eth_chainId", nil),
},
expectedRes: asArray(
`{"error":{"code":-32011,"message":"no backends available for method"},"id":1,"jsonrpc":"2.0"}`,
`{"error":{"code":-32011,"message":"no backends available for method"},"id":2,"jsonrpc":"2.0"}`,
),
maxBatchSize: 1,
numExpectedForwards: 2,
},
{
name: "duplicate request ids are on distinct batches",
mocks: []mockResult{
netVersionMock1,
chainIDMock2,
chainIDMock1,
callMock1,
},
reqs: []*proxyd.RPCReq{
NewRPCReq("1", "net_version", nil),
NewRPCReq("2", "eth_chainId", nil),
NewRPCReq("1", "eth_chainId", nil),
NewRPCReq("1", "eth_call", nil),
},
expectedRes: asArray(netVersionResponse1, chainIDResponse2, chainIDResponse1, callResponse1),
maxBatchSize: 2,
numExpectedForwards: 3,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
config.Server.MaxUpstreamBatchSize = tt.maxBatchSize
handler := tt.handler
if handler == nil {
router := NewBatchRPCResponseRouter()
for _, mock := range tt.mocks {
router.SetRoute(mock.method, mock.id, mock.result)
}
handler = router
}
goodBackend := NewMockBackend(handler)
defer goodBackend.Close()
require.NoError(t, os.Setenv("GOOD_BACKEND_RPC_URL", goodBackend.URL()))
client := NewProxydClient("http://127.0.0.1:8545")
shutdown, err := proxyd.Start(config)
require.NoError(t, err)
defer shutdown()
res, statusCode, err := client.SendBatchRPC(tt.reqs...)
require.NoError(t, err)
require.Equal(t, http.StatusOK, statusCode)
RequireEqualJSON(t, []byte(tt.expectedRes), res)
if tt.numExpectedForwards != 0 {
require.Equal(t, tt.numExpectedForwards, len(goodBackend.Requests()))
}
if handler, ok := handler.(*BatchRPCResponseRouter); ok {
for i, mock := range tt.mocks {
require.Equal(t, 1, handler.GetNumCalls(mock.method, mock.id), i)
}
}
})
}
}
......@@ -8,7 +8,7 @@ import (
"time"
"github.com/alicebob/miniredis"
"github.com/ethereum-optimism/optimism/go/proxyd"
"github.com/ethereum-optimism/optimism/proxyd"
"github.com/stretchr/testify/require"
)
......@@ -17,13 +17,17 @@ func TestCaching(t *testing.T) {
require.NoError(t, err)
defer redis.Close()
hdlr := NewRPCResponseHandler(map[string]interface{}{
"eth_chainId": "0x420",
"net_version": "0x1234",
"eth_blockNumber": "0x64",
"eth_getBlockByNumber": "dummy_block",
"eth_call": "dummy_call",
})
hdlr := NewBatchRPCResponseRouter()
hdlr.SetRoute("eth_chainId", "999", "0x420")
hdlr.SetRoute("net_version", "999", "0x1234")
hdlr.SetRoute("eth_blockNumber", "999", "0x64")
hdlr.SetRoute("eth_getBlockByNumber", "999", "dummy_block")
hdlr.SetRoute("eth_call", "999", "dummy_call")
// mock LVC requests
hdlr.SetFallbackRoute("eth_blockNumber", "0x64")
hdlr.SetFallbackRoute("eth_gasPrice", "0x420")
backend := NewMockBackend(hdlr)
defer backend.Close()
......@@ -125,7 +129,7 @@ func TestCaching(t *testing.T) {
}
t.Run("block numbers update", func(t *testing.T) {
hdlr.SetResponse("eth_blockNumber", "0x100")
hdlr.SetFallbackRoute("eth_blockNumber", "0x100")
time.Sleep(1500 * time.Millisecond)
resRaw, _, err := client.SendRPC("eth_blockNumber", nil)
require.NoError(t, err)
......@@ -134,7 +138,7 @@ func TestCaching(t *testing.T) {
})
t.Run("nil responses should not be cached", func(t *testing.T) {
hdlr.SetResponse("eth_getBlockByNumber", nil)
hdlr.SetRoute("eth_getBlockByNumber", "999", nil)
resRaw, _, err := client.SendRPC("eth_getBlockByNumber", []interface{}{"0x123"})
require.NoError(t, err)
resCache, _, err := client.SendRPC("eth_getBlockByNumber", []interface{}{"0x123"})
......@@ -145,6 +149,61 @@ func TestCaching(t *testing.T) {
})
}
func TestBatchCaching(t *testing.T) {
redis, err := miniredis.Run()
require.NoError(t, err)
defer redis.Close()
hdlr := NewBatchRPCResponseRouter()
hdlr.SetRoute("eth_chainId", "1", "0x420")
hdlr.SetRoute("net_version", "1", "0x1234")
hdlr.SetRoute("eth_call", "1", "dummy_call")
// mock LVC requests
hdlr.SetFallbackRoute("eth_blockNumber", "0x64")
hdlr.SetFallbackRoute("eth_gasPrice", "0x420")
backend := NewMockBackend(hdlr)
defer backend.Close()
require.NoError(t, os.Setenv("GOOD_BACKEND_RPC_URL", backend.URL()))
require.NoError(t, os.Setenv("REDIS_URL", fmt.Sprintf("redis://127.0.0.1:%s", redis.Port())))
config := ReadConfig("caching")
client := NewProxydClient("http://127.0.0.1:8545")
shutdown, err := proxyd.Start(config)
require.NoError(t, err)
defer shutdown()
// allow time for the block number fetcher to fire
time.Sleep(1500 * time.Millisecond)
goodChainIdResponse := "{\"jsonrpc\": \"2.0\", \"result\": \"0x420\", \"id\": 1}"
goodNetVersionResponse := "{\"jsonrpc\": \"2.0\", \"result\": \"0x1234\", \"id\": 1}"
goodEthCallResponse := "{\"jsonrpc\": \"2.0\", \"result\": \"dummy_call\", \"id\": 1}"
res, _, err := client.SendBatchRPC(
NewRPCReq("1", "eth_chainId", nil),
NewRPCReq("1", "net_version", nil),
)
require.NoError(t, err)
RequireEqualJSON(t, []byte(asArray(goodChainIdResponse, goodNetVersionResponse)), res)
require.Equal(t, 1, countRequests(backend, "eth_chainId"))
require.Equal(t, 1, countRequests(backend, "net_version"))
backend.Reset()
res, _, err = client.SendBatchRPC(
NewRPCReq("1", "eth_chainId", nil),
NewRPCReq("1", "eth_call", []interface{}{`{"to":"0x1234"}`, "pending"}),
NewRPCReq("1", "net_version", nil),
)
require.NoError(t, err)
RequireEqualJSON(t, []byte(asArray(goodChainIdResponse, goodEthCallResponse, goodNetVersionResponse)), res)
require.Equal(t, 0, countRequests(backend, "eth_chainId"))
require.Equal(t, 0, countRequests(backend, "net_version"))
require.Equal(t, 1, countRequests(backend, "eth_call"))
}
func countRequests(backend *MockBackend, name string) int {
var count int
for _, req := range backend.Requests() {
......
......@@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/ethereum-optimism/optimism/go/proxyd"
"github.com/ethereum-optimism/optimism/proxyd"
"github.com/stretchr/testify/require"
)
......@@ -18,7 +18,7 @@ const (
)
func TestFailover(t *testing.T) {
goodBackend := NewMockBackend(SingleResponseHandler(200, goodResponse))
goodBackend := NewMockBackend(BatchedResponseHandler(200, goodResponse))
defer goodBackend.Close()
badBackend := NewMockBackend(nil)
defer badBackend.Close()
......@@ -88,7 +88,7 @@ func TestFailover(t *testing.T) {
t.Run("backend times out and falls back to another", func(t *testing.T) {
badBackend.SetHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(2 * time.Second)
_, _ = w.Write([]byte("{}"))
_, _ = w.Write([]byte("[{}]"))
}))
res, statusCode, err := client.SendRPC("eth_chainId", nil)
require.NoError(t, err)
......@@ -101,23 +101,26 @@ func TestFailover(t *testing.T) {
})
t.Run("works with a batch request", func(t *testing.T) {
goodBackend.SetHandler(BatchedResponseHandler(200, goodResponse, goodResponse))
badBackend.SetHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
}))
res, statusCode, err := client.SendBatchRPC(
NewRPCReq("1", "eth_chainId", nil),
NewRPCReq("1", "eth_chainId", nil),
NewRPCReq("2", "eth_chainId", nil),
)
require.NoError(t, err)
require.Equal(t, 200, statusCode)
RequireEqualJSON(t, []byte(asArray(goodResponse, goodResponse)), res)
require.Equal(t, 2, len(badBackend.Requests()))
require.Equal(t, 2, len(goodBackend.Requests()))
require.Equal(t, 1, len(badBackend.Requests()))
require.Equal(t, 1, len(goodBackend.Requests()))
goodBackend.Reset()
badBackend.Reset()
})
}
func TestRetries(t *testing.T) {
backend := NewMockBackend(SingleResponseHandler(200, goodResponse))
backend := NewMockBackend(BatchedResponseHandler(200, goodResponse))
defer backend.Close()
require.NoError(t, os.Setenv("GOOD_BACKEND_RPC_URL", backend.URL()))
......@@ -134,7 +137,7 @@ func TestRetries(t *testing.T) {
w.WriteHeader(500)
return
}
_, _ = w.Write([]byte(goodResponse))
BatchedResponseHandler(200, goodResponse)(w, r)
}))
// test case where request eventually succeeds
......@@ -155,7 +158,8 @@ func TestRetries(t *testing.T) {
}
func TestOutOfServiceInterval(t *testing.T) {
goodBackend := NewMockBackend(SingleResponseHandler(200, goodResponse))
okHandler := BatchedResponseHandler(200, goodResponse)
goodBackend := NewMockBackend(okHandler)
defer goodBackend.Close()
badBackend := NewMockBackend(nil)
defer badBackend.Close()
......@@ -169,13 +173,9 @@ func TestOutOfServiceInterval(t *testing.T) {
require.NoError(t, err)
defer shutdown()
okHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(goodResponse))
})
badBackend.SetHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(503)
}))
goodBackend.SetHandler(okHandler)
res, statusCode, err := client.SendRPC("eth_chainId", nil)
require.NoError(t, err)
......@@ -210,3 +210,33 @@ func TestOutOfServiceInterval(t *testing.T) {
require.Equal(t, 3, len(badBackend.Requests()))
require.Equal(t, 4, len(goodBackend.Requests()))
}
func TestBatchWithPartialFailover(t *testing.T) {
config := ReadConfig("failover")
config.Server.MaxUpstreamBatchSize = 2
goodBackend := NewMockBackend(BatchedResponseHandler(200, goodResponse, goodResponse))
defer goodBackend.Close()
badBackend := NewMockBackend(SingleResponseHandler(200, "this data is not JSON!"))
defer badBackend.Close()
require.NoError(t, os.Setenv("GOOD_BACKEND_RPC_URL", goodBackend.URL()))
require.NoError(t, os.Setenv("BAD_BACKEND_RPC_URL", badBackend.URL()))
client := NewProxydClient("http://127.0.0.1:8545")
shutdown, err := proxyd.Start(config)
require.NoError(t, err)
defer shutdown()
res, statusCode, err := client.SendBatchRPC(
NewRPCReq("1", "eth_chainId", nil),
NewRPCReq("2", "eth_chainId", nil),
NewRPCReq("3", "eth_chainId", nil),
NewRPCReq("4", "eth_chainId", nil),
)
require.NoError(t, err)
require.Equal(t, 200, statusCode)
RequireEqualJSON(t, []byte(asArray(goodResponse, goodResponse, goodResponse, goodResponse)), res)
require.Equal(t, 2, len(badBackend.Requests()))
require.Equal(t, 2, len(goodBackend.Requests()))
}
......@@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/ethereum-optimism/optimism/go/proxyd"
"github.com/ethereum-optimism/optimism/proxyd"
"github.com/stretchr/testify/require"
)
......@@ -27,7 +27,7 @@ func TestMaxConcurrentRPCs(t *testing.T) {
mu.Unlock()
time.Sleep(time.Second * 2)
SingleResponseHandler(200, goodResponse)(w, r)
BatchedResponseHandler(200, goodResponse)(w, r)
mu.Lock()
concurrentRPCs--
......
......@@ -9,7 +9,7 @@ import (
"net/http/httptest"
"sync"
"github.com/ethereum-optimism/optimism/go/proxyd"
"github.com/ethereum-optimism/optimism/proxyd"
)
type RecordedRequest struct {
......@@ -32,52 +32,163 @@ func SingleResponseHandler(code int, response string) http.HandlerFunc {
}
}
type RPCResponseHandler struct {
mtx sync.RWMutex
rpcResponses map[string]interface{}
func BatchedResponseHandler(code int, responses ...string) http.HandlerFunc {
// all proxyd upstream requests are batched
return func(w http.ResponseWriter, r *http.Request) {
var body string
body += "["
for i, response := range responses {
body += response
if i+1 < len(responses) {
body += ","
}
}
body += "]"
SingleResponseHandler(code, body)(w, r)
}
}
type responseMapping struct {
result interface{}
calls int
}
type BatchRPCResponseRouter struct {
m map[string]map[string]*responseMapping
fallback map[string]interface{}
mtx sync.Mutex
}
func NewRPCResponseHandler(rpcResponses map[string]interface{}) *RPCResponseHandler {
return &RPCResponseHandler{
rpcResponses: rpcResponses,
func NewBatchRPCResponseRouter() *BatchRPCResponseRouter {
return &BatchRPCResponseRouter{
m: make(map[string]map[string]*responseMapping),
fallback: make(map[string]interface{}),
}
}
func (h *BatchRPCResponseRouter) SetRoute(method string, id string, result interface{}) {
h.mtx.Lock()
defer h.mtx.Unlock()
switch result.(type) {
case string:
case nil:
break
default:
panic("invalid result type")
}
m := h.m[method]
if m == nil {
m = make(map[string]*responseMapping)
}
m[id] = &responseMapping{result: result}
h.m[method] = m
}
func (h *RPCResponseHandler) SetResponse(method string, response interface{}) {
func (h *BatchRPCResponseRouter) SetFallbackRoute(method string, result interface{}) {
h.mtx.Lock()
defer h.mtx.Unlock()
switch response.(type) {
switch result.(type) {
case string:
case nil:
break
default:
panic("invalid response type")
panic("invalid result type")
}
h.rpcResponses[method] = response
h.fallback[method] = result
}
func (h *RPCResponseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func (h *BatchRPCResponseRouter) GetNumCalls(method string, id string) int {
h.mtx.Lock()
defer h.mtx.Unlock()
if m := h.m[method]; m != nil {
if rm := m[id]; rm != nil {
return rm.calls
}
}
return 0
}
func (h *BatchRPCResponseRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.mtx.Lock()
defer h.mtx.Unlock()
body, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err)
}
if proxyd.IsBatch(body) {
batch, err := proxyd.ParseBatchRPCReq(body)
if err != nil {
panic(err)
}
out := make([]*proxyd.RPCRes, len(batch))
for i := range batch {
req, err := proxyd.ParseRPCReq(batch[i])
if err != nil {
panic(err)
}
var result interface{}
var resultHasValue bool
if mappings, exists := h.m[req.Method]; exists {
if rm := mappings[string(req.ID)]; rm != nil {
result = rm.result
resultHasValue = true
rm.calls++
}
}
if !resultHasValue {
result, resultHasValue = h.fallback[req.Method]
}
if !resultHasValue {
w.WriteHeader(400)
return
}
out[i] = &proxyd.RPCRes{
JSONRPC: proxyd.JSONRPCVersion,
Result: result,
ID: req.ID,
}
}
if err := json.NewEncoder(w).Encode(out); err != nil {
panic(err)
}
return
}
req, err := proxyd.ParseRPCReq(body)
if err != nil {
panic(err)
}
h.mtx.RLock()
res := h.rpcResponses[req.Method]
h.mtx.RUnlock()
if res == "" {
var result interface{}
var resultHasValue bool
if mappings, exists := h.m[req.Method]; exists {
if rm := mappings[string(req.ID)]; rm != nil {
result = rm.result
resultHasValue = true
rm.calls++
}
}
if !resultHasValue {
result, resultHasValue = h.fallback[req.Method]
}
if !resultHasValue {
w.WriteHeader(400)
return
}
out := &proxyd.RPCRes{
JSONRPC: proxyd.JSONRPCVersion,
Result: res,
Result: result,
ID: req.ID,
}
enc := json.NewEncoder(w)
......
......@@ -4,7 +4,7 @@ import (
"os"
"testing"
"github.com/ethereum-optimism/optimism/go/proxyd"
"github.com/ethereum-optimism/optimism/proxyd"
"github.com/stretchr/testify/require"
)
......@@ -14,7 +14,7 @@ type resWithCode struct {
}
func TestMaxRPSLimit(t *testing.T) {
goodBackend := NewMockBackend(SingleResponseHandler(200, goodResponse))
goodBackend := NewMockBackend(BatchedResponseHandler(200, goodResponse))
defer goodBackend.Close()
require.NoError(t, os.Setenv("GOOD_BACKEND_RPC_URL", goodBackend.URL()))
......
[server]
rpc_port = 8545
timeout_seconds = 1
max_upstream_batch_size = 1
[backend]
response_timeout_seconds = 1
......
[server]
rpc_port = 8545
[backend]
response_timeout_seconds = 1
[backends]
[backends.good]
rpc_url = "$GOOD_BACKEND_RPC_URL"
ws_url = "$GOOD_BACKEND_RPC_URL"
[backend_groups]
[backend_groups.main]
backends = ["good"]
[rpc_method_mappings]
eth_chainId = "main"
net_version = "main"
eth_call = "main"
......@@ -6,10 +6,12 @@ import (
"fmt"
"io/ioutil"
"net/http"
"os"
"testing"
"github.com/BurntSushi/toml"
"github.com/ethereum-optimism/optimism/go/proxyd"
"github.com/ethereum-optimism/optimism/proxyd"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
......@@ -95,3 +97,13 @@ func NewRPCReq(id string, method string, params []interface{}) *proxyd.RPCReq {
ID: []byte(id),
}
}
func InitLogger() {
log.Root().SetHandler(
log.LvlFilterHandler(log.LvlDebug,
log.StreamHandler(
os.Stdout,
log.TerminalFormat(false),
)),
)
}
......@@ -5,7 +5,7 @@ import (
"strings"
"testing"
"github.com/ethereum-optimism/optimism/go/proxyd"
"github.com/ethereum-optimism/optimism/proxyd"
"github.com/stretchr/testify/require"
)
......@@ -19,7 +19,7 @@ const (
)
func TestSingleRPCValidation(t *testing.T) {
goodBackend := NewMockBackend(SingleResponseHandler(200, goodResponse))
goodBackend := NewMockBackend(BatchedResponseHandler(200, goodResponse))
defer goodBackend.Close()
require.NoError(t, os.Setenv("GOOD_BACKEND_RPC_URL", goodBackend.URL()))
......@@ -103,7 +103,7 @@ func TestSingleRPCValidation(t *testing.T) {
}
func TestBatchRPCValidation(t *testing.T) {
goodBackend := NewMockBackend(SingleResponseHandler(200, goodResponse))
goodBackend := NewMockBackend(BatchedResponseHandler(200, goodResponse))
defer goodBackend.Close()
require.NoError(t, os.Setenv("GOOD_BACKEND_RPC_URL", goodBackend.URL()))
......
......@@ -51,6 +51,7 @@ var (
"backend_name",
"method_name",
"status_code",
"batched",
})
rpcErrorsTotal = promauto.NewCounterVec(prometheus.CounterOpts{
......@@ -83,6 +84,7 @@ var (
}, []string{
"backend_name",
"method_name",
"batched",
})
activeClientWsConnsGauge = promauto.NewGaugeVec(prometheus.GaugeOpts{
......
......@@ -220,6 +220,7 @@ func Start(config *Config) (func(), error) {
config.Server.MaxBodySizeBytes,
resolvedAuth,
secondsToDuration(config.Server.TimeoutSeconds),
config.Server.MaxUpstreamBatchSize,
rpcCache,
)
......
......@@ -28,6 +28,7 @@ const (
cacheStatusHdr = "X-Proxyd-Cache-Status"
defaultServerTimeout = time.Second * 10
maxLogLength = 2000
defaultMaxUpstreamBatchSize = 10
)
type Server struct {
......@@ -38,6 +39,7 @@ type Server struct {
maxBodySize int64
authenticatedPaths map[string]string
timeout time.Duration
maxUpstreamBatchSize int
upgrader *websocket.Upgrader
rpcServer *http.Server
wsServer *http.Server
......@@ -52,6 +54,7 @@ func NewServer(
maxBodySize int64,
authenticatedPaths map[string]string,
timeout time.Duration,
maxUpstreamBatchSize int,
cache RPCCache,
) *Server {
if cache == nil {
......@@ -66,6 +69,10 @@ func NewServer(
timeout = defaultServerTimeout
}
if maxUpstreamBatchSize == 0 {
maxUpstreamBatchSize = defaultMaxUpstreamBatchSize
}
return &Server{
backendGroups: backendGroups,
wsBackendGroup: wsBackendGroup,
......@@ -74,6 +81,7 @@ func NewServer(
maxBodySize: maxBodySize,
authenticatedPaths: authenticatedPaths,
timeout: timeout,
maxUpstreamBatchSize: maxUpstreamBatchSize,
cache: cache,
upgrader: &websocket.Upgrader{
HandshakeTimeout: 5 * time.Second,
......@@ -177,34 +185,14 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
return
}
batchRes := make([]*RPCRes, len(reqs))
var batchContainsCached bool
for i := 0; i < len(reqs); i++ {
if ctx.Err() == context.DeadlineExceeded {
log.Info(
"short-circuiting batch RPC",
"req_id", GetReqID(ctx),
"auth", GetAuthCtx(ctx),
"index", i,
"batch_size", len(reqs),
)
batchRPCShortCircuitsTotal.Inc()
batchRes, batchContainsCached, err := s.handleBatchRPC(ctx, reqs, true)
if err == context.DeadlineExceeded {
writeRPCError(ctx, w, nil, ErrGatewayTimeout)
return
}
req, err := ParseRPCReq(reqs[i])
if err != nil {
log.Info("error parsing RPC call", "source", "rpc", "err", err)
batchRes[i] = NewRPCErrorRes(nil, err)
continue
}
var cached bool
batchRes[i], cached = s.handleSingleRPC(ctx, req)
if cached {
batchContainsCached = true
}
writeRPCError(ctx, w, nil, ErrInternal)
return
}
setCacheHeader(w, batchContainsCached)
......@@ -212,25 +200,47 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
return
}
req, err := ParseRPCReq(body)
rawBody := json.RawMessage(body)
backendRes, cached, err := s.handleBatchRPC(ctx, []json.RawMessage{rawBody}, false)
if err != nil {
log.Info("error parsing RPC call", "source", "rpc", "err", err)
writeRPCError(ctx, w, nil, err)
writeRPCError(ctx, w, nil, ErrInternal)
return
}
backendRes, cached := s.handleSingleRPC(ctx, req)
setCacheHeader(w, cached)
writeRPCRes(ctx, w, backendRes)
writeRPCRes(ctx, w, backendRes[0])
}
func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) (*RPCRes, bool) {
if err := ValidateRPCReq(req); err != nil {
func (s *Server) handleBatchRPC(ctx context.Context, reqs []json.RawMessage, isBatch bool) ([]*RPCRes, bool, error) {
// A request set is transformed into groups of batches.
// Each batch group maps to a forwarded JSON-RPC batch request (subject to maxUpstreamBatchSize constraints)
// A groupID is used to decouple Requests that have duplicate ID so they're not part of the same batch that's
// forwarded to the backend. This is done to ensure that the order of JSON-RPC Responses match the Request order
// as the backend MAY return Responses out of order.
// NOTE: Duplicate request ids induces 1-sized JSON-RPC batches
type batchGroup struct {
groupID int
backendGroup string
}
responses := make([]*RPCRes, len(reqs))
batches := make(map[batchGroup][]batchElem)
ids := make(map[string]int, len(reqs))
for i := range reqs {
parsedReq, err := ParseRPCReq(reqs[i])
if err != nil {
log.Info("error parsing RPC call", "source", "rpc", "err", err)
responses[i] = NewRPCErrorRes(nil, err)
continue
}
if err := ValidateRPCReq(parsedReq); err != nil {
RecordRPCError(ctx, BackendProxyd, MethodUnknown, err)
return NewRPCErrorRes(nil, err), false
responses[i] = NewRPCErrorRes(nil, err)
continue
}
group := s.rpcMethodMappings[req.Method]
group := s.rpcMethodMappings[parsedReq.Method]
if group == "" {
// use unknown below to prevent DOS vector that fills up memory
// with arbitrary method names.
......@@ -238,38 +248,71 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) (*RPCRes, boo
"blocked request for non-whitelisted method",
"source", "rpc",
"req_id", GetReqID(ctx),
"method", req.Method,
"method", parsedReq.Method,
)
RecordRPCError(ctx, BackendProxyd, MethodUnknown, ErrMethodNotWhitelisted)
return NewRPCErrorRes(req.ID, ErrMethodNotWhitelisted), false
responses[i] = NewRPCErrorRes(parsedReq.ID, ErrMethodNotWhitelisted)
continue
}
var backendRes *RPCRes
backendRes, err := s.cache.GetRPC(ctx, req)
if err != nil {
log.Warn(
"cache lookup error",
"req_id", GetReqID(ctx),
"err", err,
)
id := string(parsedReq.ID)
// If this is a duplicate Request ID, move the Request to a new batchGroup
ids[id]++
batchGroupID := ids[id]
batchGroup := batchGroup{groupID: batchGroupID, backendGroup: group}
batches[batchGroup] = append(batches[batchGroup], batchElem{parsedReq, i})
}
var cached bool
for group, batch := range batches {
var cacheMisses []batchElem
for _, req := range batch {
backendRes, _ := s.cache.GetRPC(ctx, req.Req)
if backendRes != nil {
return backendRes, true
responses[req.Index] = backendRes
cached = true
} else {
cacheMisses = append(cacheMisses, req)
}
}
backendRes, err = s.backendGroups[group].Forward(ctx, req)
// Create minibatches - each minibatch must be no larger than the maxUpstreamBatchSize
numBatches := int(math.Ceil(float64(len(cacheMisses)) / float64(s.maxUpstreamBatchSize)))
for i := 0; i < numBatches; i++ {
if ctx.Err() == context.DeadlineExceeded {
log.Info("short-circuiting batch RPC",
"req_id", GetReqID(ctx),
"auth", GetAuthCtx(ctx),
"batch_index", i,
)
batchRPCShortCircuitsTotal.Inc()
return nil, false, context.DeadlineExceeded
}
start := i * s.maxUpstreamBatchSize
end := int(math.Min(float64(start+s.maxUpstreamBatchSize), float64(len(cacheMisses))))
elems := cacheMisses[start:end]
res, err := s.backendGroups[group.backendGroup].Forward(ctx, createBatchRequest(elems), isBatch)
if err != nil {
log.Error(
"error forwarding RPC request",
"method", req.Method,
"req_id", GetReqID(ctx),
"error forwarding RPC batch",
"batch_size", len(elems),
"backend_group", group,
"err", err,
)
return NewRPCErrorRes(req.ID, err), false
res = nil
for _, elem := range elems {
res = append(res, NewRPCErrorRes(elem.Req.ID, err))
}
}
for i := range elems {
responses[elems[i].Index] = res[i]
if backendRes.Error == nil && backendRes.Result != nil {
if err = s.cache.PutRPC(ctx, req, backendRes); err != nil {
// TODO(inphi): batch put these
if res[i].Error == nil && res[i].Result != nil {
if err := s.cache.PutRPC(ctx, elems[i].Req, res[i]); err != nil {
log.Warn(
"cache put error",
"req_id", GetReqID(ctx),
......@@ -277,8 +320,11 @@ func (s *Server) handleSingleRPC(ctx context.Context, req *RPCReq) (*RPCRes, boo
)
}
}
}
}
}
return backendRes, false
return responses, cached, nil
}
func (s *Server) HandleWS(w http.ResponseWriter, r *http.Request) {
......@@ -472,3 +518,16 @@ func truncate(str string) string {
return str
}
}
type batchElem struct {
Req *RPCReq
Index int
}
func createBatchRequest(elems []batchElem) []*RPCReq {
batch := make([]*RPCReq, len(elems))
for i := range elems {
batch[i] = elems[i].Req
}
return batch
}
......@@ -2,11 +2,11 @@ FROM golang:1.18.0-alpine3.15 as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git jq bash
COPY ./go/bss-core /go/bss-core
COPY ./go/teleportr/go.mod ./go/teleportr/go.sum /go/teleportr/
COPY ./bss-core /go/bss-core
COPY ./teleportr /go/teleportr
COPY ./teleportr/docker.go.work /go/go.work
WORKDIR /go/teleportr
RUN go mod graph | grep -v bss-core | awk '{if ($1 !~ "@") print $2}' | xargs -n 1 go get
COPY ./go/teleportr/ ./
RUN make teleportr teleportr-api
FROM alpine:3.15
......
......@@ -15,13 +15,13 @@ import (
"syscall"
"time"
bsscore "github.com/ethereum-optimism/optimism/go/bss-core"
"github.com/ethereum-optimism/optimism/go/bss-core/dial"
"github.com/ethereum-optimism/optimism/go/bss-core/drivers"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/go/teleportr/bindings/deposit"
"github.com/ethereum-optimism/optimism/go/teleportr/db"
"github.com/ethereum-optimism/optimism/go/teleportr/flags"
bsscore "github.com/ethereum-optimism/optimism/bss-core"
"github.com/ethereum-optimism/optimism/bss-core/dial"
"github.com/ethereum-optimism/optimism/bss-core/drivers"
"github.com/ethereum-optimism/optimism/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/teleportr/bindings/deposit"
"github.com/ethereum-optimism/optimism/teleportr/db"
"github.com/ethereum-optimism/optimism/teleportr/flags"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
......@@ -478,8 +478,8 @@ func (s *Server) HandleTrack(
var confsRemaining uint64
if teleport.Deposit.BlockNumber+s.numConfirmations > blockNumber+1 {
confsRemaining = blockNumber + 1 -
(teleport.Deposit.BlockNumber + s.numConfirmations)
confsRemaining = teleport.Deposit.BlockNumber + s.numConfirmations -
(blockNumber + 1)
}
resp := TrackResponse{
......
......@@ -7,8 +7,8 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/go/teleportr/api"
"github.com/ethereum-optimism/optimism/go/teleportr/flags"
"github.com/ethereum-optimism/optimism/teleportr/api"
"github.com/ethereum-optimism/optimism/teleportr/flags"
)
var (
......
......@@ -7,8 +7,8 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
"github.com/ethereum-optimism/optimism/go/teleportr"
"github.com/ethereum-optimism/optimism/go/teleportr/flags"
"github.com/ethereum-optimism/optimism/teleportr"
"github.com/ethereum-optimism/optimism/teleportr/flags"
)
var (
......
......@@ -3,7 +3,7 @@ package teleportr
import (
"time"
"github.com/ethereum-optimism/optimism/go/teleportr/flags"
"github.com/ethereum-optimism/optimism/teleportr/flags"
"github.com/urfave/cli"
)
......
......@@ -7,7 +7,7 @@ import (
"testing"
"time"
"github.com/ethereum-optimism/optimism/go/teleportr/db"
"github.com/ethereum-optimism/optimism/teleportr/db"
"github.com/ethereum/go-ethereum/common"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
......
go 1.18
use (
./bss-core
./teleportr
)
......@@ -9,11 +9,11 @@ import (
"strings"
"time"
"github.com/ethereum-optimism/optimism/go/bss-core/metrics"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/go/teleportr/bindings/deposit"
"github.com/ethereum-optimism/optimism/go/teleportr/bindings/disburse"
"github.com/ethereum-optimism/optimism/go/teleportr/db"
"github.com/ethereum-optimism/optimism/bss-core/metrics"
"github.com/ethereum-optimism/optimism/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/teleportr/bindings/deposit"
"github.com/ethereum-optimism/optimism/teleportr/bindings/disburse"
"github.com/ethereum-optimism/optimism/teleportr/db"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
......
......@@ -3,7 +3,7 @@ package disburser_test
import (
"testing"
"github.com/ethereum-optimism/optimism/go/teleportr/drivers/disburser"
"github.com/ethereum-optimism/optimism/teleportr/drivers/disburser"
"github.com/stretchr/testify/require"
)
......
package disburser
import (
"github.com/ethereum-optimism/optimism/go/bss-core/metrics"
"github.com/ethereum-optimism/optimism/bss-core/metrics"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
......
module github.com/ethereum-optimism/optimism/go/teleportr
module github.com/ethereum-optimism/optimism/teleportr
go 1.17
replace github.com/ethereum-optimism/optimism/bss-core v0.0.0 => ../bss-core
require (
github.com/ethereum-optimism/optimism/go/bss-core v0.0.0-20220218171106-67a0414d7606
github.com/ethereum-optimism/optimism/bss-core v0.0.0
github.com/ethereum/go-ethereum v1.10.16
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.8.0
......@@ -61,4 +63,3 @@ require (
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
replace github.com/ethereum-optimism/optimism/go/bss-core => ../bss-core
......@@ -7,12 +7,12 @@ import (
"syscall"
"time"
bsscore "github.com/ethereum-optimism/optimism/go/bss-core"
"github.com/ethereum-optimism/optimism/go/bss-core/dial"
"github.com/ethereum-optimism/optimism/go/bss-core/metrics"
"github.com/ethereum-optimism/optimism/go/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/go/teleportr/db"
"github.com/ethereum-optimism/optimism/go/teleportr/drivers/disburser"
bsscore "github.com/ethereum-optimism/optimism/bss-core"
"github.com/ethereum-optimism/optimism/bss-core/dial"
"github.com/ethereum-optimism/optimism/bss-core/metrics"
"github.com/ethereum-optimism/optimism/bss-core/txmgr"
"github.com/ethereum-optimism/optimism/teleportr/db"
"github.com/ethereum-optimism/optimism/teleportr/drivers/disburser"
"github.com/ethereum/go-ethereum/log"
"github.com/urfave/cli"
)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment