initial working version

Signed-off-by: Johan Siebens <johan.siebens@gmail.com>
This commit is contained in:
Johan Siebens
2022-05-09 21:54:06 +02:00
parent 7036fca6e8
commit 5ad89ff02f
71 changed files with 7750 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
.idea
.vagrant
*.log
dist
ionscale.db*
+2
View File
@@ -0,0 +1,2 @@
generate:
buf generate proto
+11
View File
@@ -0,0 +1,11 @@
version: v1beta1
plugins:
- name: go
out: pkg/gen
opt: paths=source_relative
- name: go-grpc
out: pkg/gen
opt: paths=source_relative,require_unimplemented_servers=false
- name: grpc-gateway
out: pkg/gen
opt: paths=source_relative
+12
View File
@@ -0,0 +1,12 @@
package main
import (
"github.com/jsiebens/ionscale/internal/cmd"
"os"
)
func main() {
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}
+78
View File
@@ -0,0 +1,78 @@
module github.com/jsiebens/ionscale
go 1.18
require (
github.com/apparentlymart/go-cidr v1.1.0
github.com/glebarez/sqlite v1.4.3
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.2
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/hashicorp/go-hclog v1.1.0
github.com/klauspost/compress v1.15.3
github.com/labstack/echo-contrib v0.12.0
github.com/labstack/echo/v4 v4.6.3
github.com/mitchellh/go-homedir v1.1.0
github.com/mr-tron/base58 v1.2.0
github.com/muesli/coral v1.0.0
github.com/nleeper/goment v1.4.4
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/rodaine/table v1.0.1
github.com/soheilhy/cmux v0.1.5
github.com/sony/sonyflake v1.0.0
github.com/xhit/go-str2duration/v2 v2.0.0
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
google.golang.org/grpc v1.44.0
google.golang.org/protobuf v1.27.1
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gorm.io/gorm v1.23.5
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6
tailscale.com v1.24.2
)
require (
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/glebarez/go-sqlite v1.16.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-cmp v0.5.7 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/native v1.0.0 // indirect
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b // indirect
github.com/labstack/gommon v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mdlayher/netlink v1.6.0 // indirect
github.com/mdlayher/socket v0.2.3 // indirect
github.com/prometheus/client_golang v1.11.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/tkuchiki/go-timezone v0.2.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
golang.zx2c4.com/wireguard/windows v0.4.10 // indirect
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
modernc.org/libc v1.14.12 // indirect
modernc.org/mathutil v1.4.1 // indirect
modernc.org/memory v1.0.7 // indirect
modernc.org/sqlite v1.16.0 // indirect
)
+815
View File
@@ -0,0 +1,815 @@
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/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
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/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
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/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/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
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=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/mkcert v1.4.3 h1:axpnmtrZMM8u5Hf4N3UXxboGemMOV+Tn+e+pkHM6E3o=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
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/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU=
github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=
github.com/appleboy/gofight/v2 v2.1.2 h1:VOy3jow4vIK8BRQJoC/I9muxyYlJ2yb9ht2hZoS3rf4=
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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/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/cilium/ebpf v0.8.1 h1:bLSSEbBLqGPXxls55pGr5qWZaTqcmfDJHhou7t254ao=
github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=
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/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
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.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/glebarez/go-sqlite v1.16.0 h1:h28rHued+hGof3fNLksBcLwz/a71fiGZ/eIJHK0SsLI=
github.com/glebarez/go-sqlite v1.16.0/go.mod h1:i8/JtqoqzBAFkrUTxbQFkQ05odCOds3j7NlDaXjqiPY=
github.com/glebarez/sqlite v1.4.3 h1:ZABNo+2YIau8F8sZ7Qh/1h/ZnlSUMHFGD4zJKPval7A=
github.com/glebarez/sqlite v1.4.3/go.mod h1:FcJlwP9scnxlQ5zxyl0+bn/qFjYcqG4eRvKYhs39QAQ=
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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
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/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
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.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
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.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/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.4.1/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.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
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/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/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/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.2 h1:1aeRCnE2CkKYqyzBu0+B2lgTcZPc3ea2lGpijeHbI1c=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.0-rc.2/go.mod h1:GhphxcdlaRyAuBSvo6rV71BvQcvB/vuX8ugCyybuS2k=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/go-hclog v1.1.0 h1:QsGcniKx5/LuX2eYoeL+Np3UKYPNaN7YKpTh29h8rbw=
github.com/hashicorp/go-hclog v1.1.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b h1:Yws7RV6kZr2O7PPdT+RkbSmmOponA8i/1DuGHe8BRsM=
github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b/go.mod h1:TzDCVOZKUa79z6iXbbXqhtAflVgUKaFkZ21M5tK5tzY=
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/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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.15.3 h1:wmfu2iqj9q22SyMINp1uQ8C2/V4M1phJdmH9fG4nba0=
github.com/klauspost/compress v1.15.3/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
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/labstack/echo-contrib v0.12.0 h1:NPr1ez+XUa5s/4LujEon+32Bxg5DO6EKSW/va06pmLc=
github.com/labstack/echo-contrib v0.12.0/go.mod h1:kR62TbwsBgmpV2HVab5iQRsQtLuhPyGqCBee88XRc4M=
github.com/labstack/echo/v4 v4.6.3 h1:VhPuIZYxsbPmo4m9KAkMU/el2442eB7EBFFhNTTT9ac=
github.com/labstack/echo/v4 v4.6.3/go.mod h1:Hk5OiHj0kDqmFq7aHe7eDqI7CUhuCrfpupQtLGGLm7A=
github.com/labstack/gommon v0.3.1 h1:OomWaJXm7xR6L1HmEtGyQf26TEn7V6X88mktX9kee9o=
github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ=
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0=
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM=
github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/muesli/coral v1.0.0 h1:odyqkoEg4aJAINOzvnjN4tUsdp+Zleccs7tRIAkkYzU=
github.com/muesli/coral v1.0.0/go.mod h1:bf91M/dkp7iHQw73HOoR9PekdTJMTD6ihJgWoDitde8=
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/nleeper/goment v1.4.4 h1:GlMTpxvhueljArSunzYjN9Ri4SOmpn0Vh2hg2z/IIl8=
github.com/nleeper/goment v1.4.4/go.mod h1:zDl5bAyDhqxwQKAvkSXMRLOdCowrdZz53ofRJc4VhTo=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.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.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/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rodaine/table v1.0.1 h1:U/VwCnUxlVYxw8+NJiLIuCxA/xa6jL38MY3FYysVWWQ=
github.com/rodaine/table v1.0.1/go.mod h1:UVEtfBsflpeEcD56nF4F5AocNFta0ZuolpSVdPtlmP4=
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/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 h1:Ha8xCaq6ln1a+R91Km45Oq6lPXj2Mla6CRJYcuV2h1w=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
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/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=
github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=
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/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/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tkuchiki/go-timezone v0.2.0 h1:yyZVHtQRVZ+wvlte5HXvSpBkR0dPYnPEIgq9qqAqltk=
github.com/tkuchiki/go-timezone v0.2.0/go.mod h1:b1Ean9v2UXtxSq4TZF0i/TU9NuoWa9hOzOKoGCV2zqY=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4=
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/xhit/go-str2duration/v2 v2.0.0 h1:uFtk6FWB375bP7ewQl+/1wBcn840GPhnySOdcz/okPE=
github.com/xhit/go-str2duration/v2 v2.0.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
go4.org/mem v0.0.0-20210711025021-927187094b94 h1:OAAkygi2Js191AJP1Ds42MhJRgeofeKGjuoUqNp1QC4=
go4.org/mem v0.0.0-20210711025021-927187094b94/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 h1:Tx9kY6yUkLge/pFG7IEMwDZy6CS2ajFc9TvQdPCW0uA=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-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-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc=
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb h1:fP6C8Xutcp5AlakmT/SkQot0pMicROAsEX7OfNPuG10=
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/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.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-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-20190628185345-da137c7871d7/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-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-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/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-20200707034311-ab3426394381/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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3 h1:EN5+DfgmRMvRUrMGERW2gQl3Vc+Z7ZMnI/xdEpPSf0c=
golang.org/x/net v0.0.0-20220407224826-aac1ed45d8e3/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
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/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-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-20190222072716-a9d3bda3a223/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-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-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-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/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-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/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-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/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-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
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-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/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-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-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-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-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-20200227222343-706bc42d1f0d/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-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.11-0.20220413170336-afc6aad76eb1 h1:Z3vE1sGlC7qiyFJkkDcZms8Y3+yV8+W7HmDSmuf71tM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
golang.zx2c4.com/wireguard v0.0.0-20210905140043-2ef39d47540c/go.mod h1:laHzsbfMhGSobUmruXWAyMKKHSqvIcrqZJMyHD+/3O8=
golang.zx2c4.com/wireguard/windows v0.4.10 h1:HmjzJnb+G4NCdX+sfjsQlsxGPuYaThxRbZUZFLyR0/s=
golang.zx2c4.com/wireguard/windows v0.4.10/go.mod h1:v7w/8FC48tTBm1IzScDVPEEb0/GjLta+T0ybpP9UWRg=
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.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
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.6/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-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987 h1:PDIOdWxZ8eRizhKa1AAvY53xsvLB1cWorMjslvY3VA8=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
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.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
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.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.5 h1:TnlF26wScKSvknUC/Rn8t0NLLM22fypYBlvj1+aH6dM=
gorm.io/gorm v1.23.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
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=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.4.0-0.dev.0.20220404092545-59d7a2877f83 h1:lZ9GIYaU+o5+X6ST702I/Ntyq9Y2oIMZ42rBQpem64A=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 h1:acCzuUSQ79tGsM/O50VRFySfMm19IoMKL+sZztZkCxw=
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6/go.mod h1:y3MGhcFMlh0KZPMuXXow8mpjxxAk3yoDNsp4cQz54i8=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.11/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.34.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.0/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.4/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.5/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.7/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.8/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.35.24/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
modernc.org/ccgo/v3 v3.11.1/go.mod h1:lWHxfsn13L3f7hgGsGlU28D9eUOf6y3ZYHKoPaKU0ag=
modernc.org/ccgo/v3 v3.11.3/go.mod h1:0oHunRBMBiXOKdaglfMlRPBALQqsfrCKXgw9okQ3GEw=
modernc.org/ccgo/v3 v3.12.4/go.mod h1:Bk+m6m2tsooJchP/Yk5ji56cClmN6R1cqc9o/YtbgBQ=
modernc.org/ccgo/v3 v3.12.6/go.mod h1:0Ji3ruvpFPpz+yu+1m0wk68pdr/LENABhTrDkMDWH6c=
modernc.org/ccgo/v3 v3.12.8/go.mod h1:Hq9keM4ZfjCDuDXxaHptpv9N24JhgBZmUG5q60iLgUo=
modernc.org/ccgo/v3 v3.12.11/go.mod h1:0jVcmyDwDKDGWbcrzQ+xwJjbhZruHtouiBEvDfoIsdg=
modernc.org/ccgo/v3 v3.12.14/go.mod h1:GhTu1k0YCpJSuWwtRAEHAol5W7g1/RRfS4/9hc9vF5I=
modernc.org/ccgo/v3 v3.12.18/go.mod h1:jvg/xVdWWmZACSgOiAhpWpwHWylbJaSzayCqNOJKIhs=
modernc.org/ccgo/v3 v3.12.20/go.mod h1:aKEdssiu7gVgSy/jjMastnv/q6wWGRbszbheXgWRHc8=
modernc.org/ccgo/v3 v3.12.21/go.mod h1:ydgg2tEprnyMn159ZO/N4pLBqpL7NOkJ88GT5zNU2dE=
modernc.org/ccgo/v3 v3.12.22/go.mod h1:nyDVFMmMWhMsgQw+5JH6B6o4MnZ+UQNw1pp52XYFPRk=
modernc.org/ccgo/v3 v3.12.25/go.mod h1:UaLyWI26TwyIT4+ZFNjkyTbsPsY3plAEB6E7L/vZV3w=
modernc.org/ccgo/v3 v3.12.29/go.mod h1:FXVjG7YLf9FetsS2OOYcwNhcdOLGt8S9bQ48+OP75cE=
modernc.org/ccgo/v3 v3.12.36/go.mod h1:uP3/Fiezp/Ga8onfvMLpREq+KUjUmYMxXPO8tETHtA8=
modernc.org/ccgo/v3 v3.12.38/go.mod h1:93O0G7baRST1vNj4wnZ49b1kLxt0xCW5Hsa2qRaZPqc=
modernc.org/ccgo/v3 v3.12.43/go.mod h1:k+DqGXd3o7W+inNujK15S5ZYuPoWYLpF5PYougCmthU=
modernc.org/ccgo/v3 v3.12.46/go.mod h1:UZe6EvMSqOxaJ4sznY7b23/k13R8XNlyWsO5bAmSgOE=
modernc.org/ccgo/v3 v3.12.47/go.mod h1:m8d6p0zNps187fhBwzY/ii6gxfjob1VxWb919Nk1HUk=
modernc.org/ccgo/v3 v3.12.50/go.mod h1:bu9YIwtg+HXQxBhsRDE+cJjQRuINuT9PUK4orOco/JI=
modernc.org/ccgo/v3 v3.12.51/go.mod h1:gaIIlx4YpmGO2bLye04/yeblmvWEmE4BBBls4aJXFiE=
modernc.org/ccgo/v3 v3.12.53/go.mod h1:8xWGGTFkdFEWBEsUmi+DBjwu/WLy3SSOrqEmKUjMeEg=
modernc.org/ccgo/v3 v3.12.54/go.mod h1:yANKFTm9llTFVX1FqNKHE0aMcQb1fuPJx6p8AcUx+74=
modernc.org/ccgo/v3 v3.12.55/go.mod h1:rsXiIyJi9psOwiBkplOaHye5L4MOOaCjHg1Fxkj7IeU=
modernc.org/ccgo/v3 v3.12.56/go.mod h1:ljeFks3faDseCkr60JMpeDb2GSO3TKAmrzm7q9YOcMU=
modernc.org/ccgo/v3 v3.12.57/go.mod h1:hNSF4DNVgBl8wYHpMvPqQWDQx8luqxDnNGCMM4NFNMc=
modernc.org/ccgo/v3 v3.12.60/go.mod h1:k/Nn0zdO1xHVWjPYVshDeWKqbRWIfif5dtsIOCUVMqM=
modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/EhQ=
modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU=
modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko=
modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA=
modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4=
modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8=
modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I=
modernc.org/ccgo/v3 v3.15.14/go.mod h1:144Sz2iBCKogb9OKwsu7hQEub3EVgOlyI8wMUPGKUXQ=
modernc.org/ccgo/v3 v3.15.15/go.mod h1:z5qltXjU4PJl0pE5nhYQCvA9DhPHiWsl5GWl89+NSYE=
modernc.org/ccgo/v3 v3.15.16/go.mod h1:XbKRMeMWMdq712Tr5ECgATYMrzJ+g9zAZEj2ktzBe24=
modernc.org/ccgo/v3 v3.15.17/go.mod h1:bofnFkpRFf5gLY+mBZIyTW6FEcp26xi2lgOFk2Rlvs0=
modernc.org/ccgo/v3 v3.15.18/go.mod h1:/2lv3WjHyanEr2sAPdGKRC38n6f0werut9BRXUjjX+A=
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
modernc.org/libc v1.11.2/go.mod h1:ioIyrl3ETkugDO3SGZ+6EOKvlP3zSOycUETe4XM4n8M=
modernc.org/libc v1.11.5/go.mod h1:k3HDCP95A6U111Q5TmG3nAyUcp3kR5YFZTeDS9v8vSU=
modernc.org/libc v1.11.6/go.mod h1:ddqmzR6p5i4jIGK1d/EiSw97LBcE3dK24QEwCFvgNgE=
modernc.org/libc v1.11.11/go.mod h1:lXEp9QOOk4qAYOtL3BmMve99S5Owz7Qyowzvg6LiZso=
modernc.org/libc v1.11.13/go.mod h1:ZYawJWlXIzXy2Pzghaf7YfM8OKacP3eZQI81PDLFdY8=
modernc.org/libc v1.11.16/go.mod h1:+DJquzYi+DMRUtWI1YNxrlQO6TcA5+dRRiq8HWBWRC8=
modernc.org/libc v1.11.19/go.mod h1:e0dgEame6mkydy19KKaVPBeEnyJB4LGNb0bBH1EtQ3I=
modernc.org/libc v1.11.24/go.mod h1:FOSzE0UwookyT1TtCJrRkvsOrX2k38HoInhw+cSCUGk=
modernc.org/libc v1.11.26/go.mod h1:SFjnYi9OSd2W7f4ct622o/PAYqk7KHv6GS8NZULIjKY=
modernc.org/libc v1.11.27/go.mod h1:zmWm6kcFXt/jpzeCgfvUNswM0qke8qVwxqZrnddlDiE=
modernc.org/libc v1.11.28/go.mod h1:Ii4V0fTFcbq3qrv3CNn+OGHAvzqMBvC7dBNyC4vHZlg=
modernc.org/libc v1.11.31/go.mod h1:FpBncUkEAtopRNJj8aRo29qUiyx5AvAlAxzlx9GNaVM=
modernc.org/libc v1.11.34/go.mod h1:+Tzc4hnb1iaX/SKAutJmfzES6awxfU1BPvrrJO0pYLg=
modernc.org/libc v1.11.37/go.mod h1:dCQebOwoO1046yTrfUE5nX1f3YpGZQKNcITUYWlrAWo=
modernc.org/libc v1.11.39/go.mod h1:mV8lJMo2S5A31uD0k1cMu7vrJbSA3J3waQJxpV4iqx8=
modernc.org/libc v1.11.42/go.mod h1:yzrLDU+sSjLE+D4bIhS7q1L5UwXDOw99PLSX0BlZvSQ=
modernc.org/libc v1.11.44/go.mod h1:KFq33jsma7F5WXiYelU8quMJasCCTnHK0mkri4yPHgA=
modernc.org/libc v1.11.45/go.mod h1:Y192orvfVQQYFzCNsn+Xt0Hxt4DiO4USpLNXBlXg/tM=
modernc.org/libc v1.11.47/go.mod h1:tPkE4PzCTW27E6AIKIR5IwHAQKCAtudEIeAV1/SiyBg=
modernc.org/libc v1.11.49/go.mod h1:9JrJuK5WTtoTWIFQ7QjX2Mb/bagYdZdscI3xrvHbXjE=
modernc.org/libc v1.11.51/go.mod h1:R9I8u9TS+meaWLdbfQhq2kFknTW0O3aw3kEMqDDxMaM=
modernc.org/libc v1.11.53/go.mod h1:5ip5vWYPAoMulkQ5XlSJTy12Sz5U6blOQiYasilVPsU=
modernc.org/libc v1.11.54/go.mod h1:S/FVnskbzVUrjfBqlGFIPA5m7UwB3n9fojHhCNfSsnw=
modernc.org/libc v1.11.55/go.mod h1:j2A5YBRm6HjNkoSs/fzZrSxCuwWqcMYTDPLNx0URn3M=
modernc.org/libc v1.11.56/go.mod h1:pakHkg5JdMLt2OgRadpPOTnyRXm/uzu+Yyg/LSLdi18=
modernc.org/libc v1.11.58/go.mod h1:ns94Rxv0OWyoQrDqMFfWwka2BcaF6/61CqJRK9LP7S8=
modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ=
modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34=
modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ=
modernc.org/libc v1.14.6/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak=
modernc.org/libc v1.14.7/go.mod h1:f8xfWXW8LW41qb4X5+huVQo5dcfPlq7Cbny2TDheMv0=
modernc.org/libc v1.14.8/go.mod h1:9+JCLb1MWSY23smyOpIPbd5ED+rSS/ieiDWUpdyO3mo=
modernc.org/libc v1.14.10/go.mod h1:y1MtIWhwpJFpLYm6grAThtuXJKEsY6xkdZmXbRngIdo=
modernc.org/libc v1.14.11/go.mod h1:l5/Mz/GrZwOqzwRHA3abgSCnSeJzzTl+Ify0bAwKbAw=
modernc.org/libc v1.14.12 h1:pUBZTYoISfbb4pCf4PECENpbvwDBxeKc+/dS9LyOWFM=
modernc.org/libc v1.14.12/go.mod h1:fJdoe23MHu2ruPQkFPPqCpToDi5cckzsbmkI6Ez0LqQ=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
modernc.org/memory v1.0.6/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/memory v1.0.7 h1:UE3cxTRFa5tfUibAV7Jqq8P7zRY0OlJg+yWVIIaluEE=
modernc.org/memory v1.0.7/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.16.0 h1:DdvOGaWN0y+X7t2L7RUD63gcwbVjYZjcBZnA68g44EI=
modernc.org/sqlite v1.16.0/go.mod h1:Jwe13ItpESZ+78K5WS6+AjXsUg+JvirsjN3iIDO4C8k=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.11.2/go.mod h1:BRzgpajcGdS2qTxniOx9c/dcxjlbA7p12eJNmiriQYo=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.3.2/go.mod h1:PEU2oK2OEA1CfzDTd+8E908qEXhC9s0MfyKp5LZsd+k=
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=
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
tailscale.com v1.24.2 h1:xNqEMKLHjqKwKUlggL2QEt1B+oit08w3SwZEIWCmqTg=
tailscale.com v1.24.2/go.mod h1:/z/lF98LSt9CjpwVT6pHh5Vwb1NGsM5/ACI4cLMJfvY=
+55
View File
@@ -0,0 +1,55 @@
package addr
import (
"github.com/apparentlymart/go-cidr/cidr"
"github.com/jsiebens/ionscale/internal/util"
"inet.af/netaddr"
"math/big"
"net"
"tailscale.com/net/tsaddr"
)
var ipv4Range = tsaddr.CGNATRange().IPNet()
type Predicate func(netaddr.IP) (bool, error)
func SelectIP(predicate Predicate) (*netaddr.IP, *netaddr.IP, error) {
ip4, err := selectIP(ipv4Range, predicate)
if err != nil {
return nil, nil, err
}
ip6 := tsaddr.Tailscale4To6(*ip4)
return ip4, &ip6, err
}
func selectIP(c *net.IPNet, predicate Predicate) (*netaddr.IP, error) {
count := cidr.AddressCount(c)
var n = util.RandUint64(count)
for {
stdIP, err := cidr.HostBig(c, big.NewInt(int64(n)))
if err != nil {
return nil, err
}
ip, _ := netaddr.FromStdIP(stdIP)
ok, err := validateIP(ip, predicate)
if err != nil {
return nil, err
}
if ok {
return &ip, nil
}
n = (n + 1) % count
}
}
func validateIP(ip netaddr.IP, p Predicate) (bool, error) {
if tsaddr.IsTailscaleIP(ip) {
if p != nil {
return p(ip)
} else {
return true, nil
}
}
return false, nil
}
+163
View File
@@ -0,0 +1,163 @@
package bind
import (
"encoding/binary"
"encoding/json"
"fmt"
"github.com/jsiebens/ionscale/internal/util"
"github.com/klauspost/compress/zstd"
"github.com/labstack/echo/v4"
"io/ioutil"
"tailscale.com/types/key"
)
type Factory func(c echo.Context) (Binder, error)
type Binder interface {
BindRequest(c echo.Context, v interface{}) error
WriteResponse(c echo.Context, code int, v interface{}) error
Marshal(compress string, v interface{}) ([]byte, error)
Peer() key.MachinePublic
}
func DefaultBinder(machineKey key.MachinePublic) Factory {
return func(c echo.Context) (Binder, error) {
return &defaultBinder{machineKey: machineKey}, nil
}
}
func BoxBinder(controlKey key.MachinePrivate) Factory {
return func(c echo.Context) (Binder, error) {
idParam := c.Param("id")
id, err := util.ParseMachinePublicKey(idParam)
if err != nil {
return nil, err
}
return &boxBinder{
controlKey: controlKey,
machineKey: *id,
}, nil
}
}
type defaultBinder struct {
machineKey key.MachinePublic
}
func (d *defaultBinder) BindRequest(c echo.Context, v interface{}) error {
body, err := ioutil.ReadAll(c.Request().Body)
if err != nil {
return err
}
return json.Unmarshal(body, v)
}
func (d *defaultBinder) WriteResponse(c echo.Context, code int, v interface{}) error {
marshalled, err := json.Marshal(v)
if err != nil {
return err
}
c.Response().WriteHeader(code)
_, err = c.Response().Write(marshalled)
return err
}
func (d *defaultBinder) Marshal(compress string, v interface{}) ([]byte, error) {
var payload []byte
marshalled, err := json.Marshal(v)
if err != nil {
return nil, err
}
if compress == "zstd" {
encoder, err := zstd.NewWriter(nil)
if err != nil {
return nil, err
}
payload = encoder.EncodeAll(marshalled, nil)
} else {
payload = marshalled
}
data := make([]byte, 4)
binary.LittleEndian.PutUint32(data, uint32(len(payload)))
data = append(data, payload...)
return data, nil
}
func (d *defaultBinder) Peer() key.MachinePublic {
return d.machineKey
}
type boxBinder struct {
controlKey key.MachinePrivate
machineKey key.MachinePublic
}
func (b *boxBinder) BindRequest(c echo.Context, v interface{}) error {
body, err := ioutil.ReadAll(c.Request().Body)
if err != nil {
return err
}
decrypted, ok := b.controlKey.OpenFrom(b.machineKey, body)
if !ok {
return fmt.Errorf("unable to decrypt payload")
}
return json.Unmarshal(decrypted, v)
}
func (b *boxBinder) WriteResponse(c echo.Context, code int, v interface{}) error {
marshalled, err := json.Marshal(v)
if err != nil {
return err
}
encrypted := b.controlKey.SealTo(b.machineKey, marshalled)
c.Response().WriteHeader(code)
_, err = c.Response().Write(encrypted)
return err
}
func (b *boxBinder) Marshal(compress string, v interface{}) ([]byte, error) {
var payload []byte
marshalled, err := json.Marshal(v)
if err != nil {
return nil, err
}
if compress == "zstd" {
encoder, err := zstd.NewWriter(nil)
if err != nil {
return nil, err
}
encoded := encoder.EncodeAll(marshalled, nil)
payload = b.controlKey.SealTo(b.machineKey, encoded)
} else {
payload = b.controlKey.SealTo(b.machineKey, marshalled)
}
data := make([]byte, 4)
binary.LittleEndian.PutUint32(data, uint32(len(payload)))
data = append(data, payload...)
return data, nil
}
func (b *boxBinder) Peer() key.MachinePublic {
return b.machineKey
}
+102
View File
@@ -0,0 +1,102 @@
package broker
import (
"sync"
"tailscale.com/types/key"
)
type BrokerPool struct {
lock sync.Mutex
store map[uint64]Broker
}
type Signal struct {
PeerUpdated *uint64
PeersRemoved []uint64
}
type Broker interface {
AddClient(*Client)
RemoveClient(uint64)
SignalPeerUpdated(id uint64)
SignalPeersRemoved([]uint64)
IsConnected(uint64) bool
}
func NewBrokerPool() *BrokerPool {
return &BrokerPool{
store: make(map[uint64]Broker),
}
}
func (m *BrokerPool) Get(tailnetID uint64) Broker {
m.lock.Lock()
defer m.lock.Unlock()
b, ok := m.store[tailnetID]
if !ok {
b = newBroker(tailnetID)
m.store[tailnetID] = b
}
return b
}
func newBroker(tailnetID uint64) Broker {
b := &broker{
tailnetID: tailnetID,
newClients: make(chan *Client),
closingClients: make(chan uint64),
clients: make(map[uint64]*Client),
signalChannel: make(chan *Signal),
}
go b.listen()
return b
}
type broker struct {
tailnetID uint64
privateKey *key.MachinePrivate
newClients chan *Client
closingClients chan uint64
signalChannel chan *Signal
clients map[uint64]*Client
}
func (h *broker) IsConnected(id uint64) (ok bool) {
_, ok = h.clients[id]
return
}
func (h *broker) AddClient(client *Client) {
h.newClients <- client
}
func (h *broker) RemoveClient(id uint64) {
h.closingClients <- id
}
func (h *broker) SignalPeerUpdated(id uint64) {
h.signalChannel <- &Signal{PeerUpdated: &id}
}
func (h *broker) SignalPeersRemoved(ids []uint64) {
h.signalChannel <- &Signal{PeersRemoved: ids}
}
func (h *broker) listen() {
for {
select {
case s := <-h.newClients:
h.clients[s.id] = s
case s := <-h.closingClients:
delete(h.clients, s)
case s := <-h.signalChannel:
for _, c := range h.clients {
c.SignalUpdate(s)
}
}
}
}
+26
View File
@@ -0,0 +1,26 @@
package broker
import (
"github.com/jsiebens/ionscale/internal/bind"
"tailscale.com/tailcfg"
)
func NewClient(id uint64, channel chan *Signal) Client {
return Client{
id: id,
channel: channel,
}
}
type Client struct {
id uint64
binder bind.Binder
node *tailcfg.Node
compress string
channel chan *Signal
}
func (c *Client) SignalUpdate(s *Signal) {
c.channel <- s
}
+181
View File
@@ -0,0 +1,181 @@
package cmd
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/muesli/coral"
"github.com/rodaine/table"
str2dur "github.com/xhit/go-str2duration/v2"
"google.golang.org/protobuf/types/known/durationpb"
"time"
)
func authkeysCommand() *coral.Command {
command := &coral.Command{
Use: "auth-keys",
}
command.AddCommand(createAuthkeysCommand())
command.AddCommand(deleteAuthKeyCommand())
command.AddCommand(listAuthkeysCommand())
return command
}
func createAuthkeysCommand() *coral.Command {
command := &coral.Command{
Use: "create",
SilenceUsage: true,
}
var tailnetID uint64
var tailnetName string
var ephemeral bool
var tags []string
var expiry string
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&tailnetName, "tailnet", "", "")
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "")
command.Flags().BoolVar(&ephemeral, "ephemeral", false, "")
command.Flags().StringSliceVar(&tags, "tag", []string{}, "")
command.Flags().StringVar(&expiry, "expiry", "180d", "")
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
tailnet, err := findTailnet(client, tailnetName, tailnetID)
if err != nil {
return err
}
var expiryDur *durationpb.Duration
if expiry != "" && expiry != "none" {
duration, err := str2dur.ParseDuration(expiry)
if err != nil {
return err
}
expiryDur = durationpb.New(duration)
}
req := &api.CreateAuthKeyRequest{
TailnetId: tailnet.Id,
Ephemeral: ephemeral,
Tags: tags,
Expiry: expiryDur,
}
resp, err := client.CreateAuthKey(context.Background(), req)
if err != nil {
return err
}
fmt.Println("")
fmt.Println("Generated new auth key")
fmt.Println("Be sure to copy your new key below. It won't be shown in full again.")
fmt.Println("")
fmt.Printf(" %s\n", resp.Value)
fmt.Println("")
return nil
}
return command
}
func deleteAuthKeyCommand() *coral.Command {
command := &coral.Command{
Use: "delete",
SilenceUsage: true,
}
var authKeyId uint64
var target = Target{}
target.prepareCommand(command)
command.Flags().Uint64Var(&authKeyId, "id", 0, "")
command.RunE = func(command *coral.Command, args []string) error {
grpcClient, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
req := api.DeleteAuthKeyRequest{AuthKeyId: authKeyId}
if _, err := grpcClient.DeleteAuthKey(context.Background(), &req); err != nil {
return err
}
fmt.Println("Auth key deleted.")
return nil
}
return command
}
func listAuthkeysCommand() *coral.Command {
command := &coral.Command{
Use: "list",
SilenceUsage: true,
}
var tailnetID uint64
var tailnetName string
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&tailnetName, "tailnet", "", "")
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "")
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
tailnet, err := findTailnet(client, tailnetName, tailnetID)
if err != nil {
return err
}
req := &api.ListAuthKeysRequest{TailnetId: tailnet.Id}
resp, err := client.ListAuthKeys(context.Background(), req)
if err != nil {
return err
}
printAuthKeyTable(resp.AuthKeys...)
return nil
}
return command
}
func printAuthKeyTable(authKeys ...*api.AuthKey) {
tbl := table.New("ID", "VALUE", "EPHEMERAL", "EXPIRED", "CREATED_AT", "EXPIRES_AT")
for _, authKey := range authKeys {
addAuthKeyToTable(tbl, authKey)
}
tbl.Print()
}
func addAuthKeyToTable(tbl table.Table, authKey *api.AuthKey) {
var expired = false
var expiresAt = "never"
if authKey.ExpiresAt != nil {
expiresAt = authKey.ExpiresAt.AsTime().Local().Format("2006-01-02 15:04:05")
expired = time.Now().After(authKey.ExpiresAt.AsTime())
}
tbl.AddRow(authKey.Id, fmt.Sprintf("%s...", authKey.Key), authKey.Ephemeral, expired, authKey.CreatedAt.AsTime().Local().Format("2006-01-02 15:04:05"), expiresAt)
}
+33
View File
@@ -0,0 +1,33 @@
package cmd
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/pkg/gen/api"
"io"
)
func findTailnet(client api.IonscaleClient, tailnet string, tailnetID uint64) (*api.Tailnet, error) {
if tailnetID == 0 && tailnet == "" {
return nil, fmt.Errorf("requested tailnet not found or you are not authorized for this tailnet")
}
tailnets, err := client.ListTailnets(context.Background(), &api.ListTailnetRequest{})
if err != nil {
return nil, err
}
for _, t := range tailnets.Tailnet {
if t.Id == tailnetID || t.Name == tailnet {
return t, nil
}
}
return nil, fmt.Errorf("requested tailnet not found or you are not authorized for this tailnet")
}
func safeClose(c io.Closer) {
if c != nil {
_ = c.Close()
}
}
+110
View File
@@ -0,0 +1,110 @@
package cmd
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/muesli/coral"
"github.com/nleeper/goment"
"github.com/rodaine/table"
)
func machineCommands() *coral.Command {
command := &coral.Command{
Use: "machines",
Short: "Manage ionscale machines",
SilenceUsage: true,
}
command.AddCommand(deleteMachineCommand())
command.AddCommand(listMachinesCommand())
return command
}
func deleteMachineCommand() *coral.Command {
command := &coral.Command{
Use: "delete",
Short: "Deletes a machine",
SilenceUsage: true,
}
var machineID uint64
var target = Target{}
target.prepareCommand(command)
command.Flags().Uint64Var(&machineID, "machine-id", 0, "")
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
req := api.DeleteMachineRequest{MachineId: machineID}
if _, err := client.DeleteMachine(context.Background(), &req); err != nil {
return err
}
fmt.Println("Machine deleted.")
return nil
}
return command
}
func listMachinesCommand() *coral.Command {
command := &coral.Command{
Use: "list",
Short: "List machines",
SilenceUsage: true,
}
var tailnetID uint64
var tailnetName string
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVar(&tailnetName, "tailnet", "", "")
command.Flags().Uint64Var(&tailnetID, "tailnet-id", 0, "")
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
tailnet, err := findTailnet(client, tailnetName, tailnetID)
if err != nil {
return err
}
req := api.ListMachinesRequest{TailnetId: tailnet.Id}
resp, err := client.ListMachines(context.Background(), &req)
if err != nil {
return err
}
tbl := table.New("ID", "TAILNET", "NAME", "IPv4", "IPv6", "EPHEMERAL", "LAST_SEEN", "USER")
for _, m := range resp.Machines {
var lastSeen = "N/A"
if m.Connected {
lastSeen = "Connected"
} else if m.LastSeen != nil {
mom, err := goment.New(m.LastSeen.AsTime())
if err == nil {
lastSeen = mom.FromNow()
}
}
tbl.AddRow(m.Id, m.Tailnet.Name, m.Name, m.Ipv4, m.Ipv6, m.Ephemeral, lastSeen, m.User.Name)
}
tbl.Print()
return nil
}
return command
}
+26
View File
@@ -0,0 +1,26 @@
package cmd
import (
"github.com/muesli/coral"
)
func Command() *coral.Command {
rootCmd := rootCommand()
rootCmd.AddCommand(serverCommand())
rootCmd.AddCommand(versionCommand())
rootCmd.AddCommand(tailnetCommand())
rootCmd.AddCommand(authkeysCommand())
rootCmd.AddCommand(machineCommands())
return rootCmd
}
func Execute() error {
return Command().Execute()
}
func rootCommand() *coral.Command {
return &coral.Command{
Use: "ionscale",
}
}
+31
View File
@@ -0,0 +1,31 @@
package cmd
import (
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/internal/server"
"github.com/muesli/coral"
)
func serverCommand() *coral.Command {
command := &coral.Command{
Use: "server",
Short: "Start an ionscale server",
SilenceUsage: true,
}
var configFile string
command.Flags().StringVarP(&configFile, "config", "c", "ionscale.yaml", "Path to the configuration file.")
command.RunE = func(command *coral.Command, args []string) error {
c, err := config.LoadConfig(configFile)
if err != nil {
return err
}
return server.Start(c)
}
return command
}
+97
View File
@@ -0,0 +1,97 @@
package cmd
import (
"context"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/muesli/coral"
"github.com/rodaine/table"
)
func tailnetCommand() *coral.Command {
command := &coral.Command{
Use: "tailnets",
Short: "Manage ionscale tailnets",
Long: "This command allows operations on ionscale tailnet resources.",
}
command.AddCommand(listTailnetsCommand())
command.AddCommand(createTailnetsCommand())
return command
}
func listTailnetsCommand() *coral.Command {
command := &coral.Command{
Use: "list",
Short: "List tailnets",
Long: `List tailnets in this ionscale instance.`,
SilenceUsage: true,
}
var target = Target{}
target.prepareCommand(command)
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
resp, err := client.ListTailnets(context.Background(), &api.ListTailnetRequest{})
if err != nil {
return err
}
tbl := table.New("ID", "NAME")
for _, tailnet := range resp.Tailnet {
tbl.AddRow(tailnet.Id, tailnet.Name)
}
tbl.Print()
return nil
}
return command
}
func createTailnetsCommand() *coral.Command {
command := &coral.Command{
Use: "create",
Short: "Create a new tailnet",
Long: `List tailnets in this ionscale instance.`,
SilenceUsage: true,
}
var name string
var target = Target{}
target.prepareCommand(command)
command.Flags().StringVarP(&name, "name", "n", "", "")
_ = command.MarkFlagRequired("name")
command.RunE = func(command *coral.Command, args []string) error {
client, c, err := target.createGRPCClient()
if err != nil {
return err
}
defer safeClose(c)
resp, err := client.CreateTailnet(context.Background(), &api.CreateTailnetRequest{Name: name})
if err != nil {
return err
}
tbl := table.New("ID", "NAME")
tbl.AddRow(resp.Tailnet.Id, resp.Tailnet.Name)
tbl.Print()
return nil
}
return command
}
+61
View File
@@ -0,0 +1,61 @@
package cmd
import (
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/pkg/client/ionscale"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/muesli/coral"
"io"
)
const (
ionscaleSystemAdminKey = "IONSCALE_ADMIN_KEY"
ionscaleAddr = "IONSCALE_ADDR"
ionscaleInsecureSkipVerify = "IONSCALE_SKIP_VERIFY"
)
type Target struct {
addr string
insecureSkipVerify bool
systemAdminKey string
}
func (t *Target) prepareCommand(cmd *coral.Command) {
cmd.Flags().StringVar(&t.addr, "addr", "", "Addr of the ionscale server, as a complete URL")
cmd.Flags().BoolVar(&t.insecureSkipVerify, "tls-skip-verify", false, "Disable verification of TLS certificates")
cmd.Flags().StringVar(&t.systemAdminKey, "admin-key", "", "If specified, the given value will be used as the key to generate a Bearer token for the call. This can also be specified via the IONSCALE_ADMIN_KEY environment variable.")
}
func (t *Target) createGRPCClient() (api.IonscaleClient, io.Closer, error) {
addr := t.getAddr()
skipVerify := t.getInsecureSkipVerify()
systemAdminKey := t.getSystemAdminKey()
auth, err := ionscale.LoadClientAuth(systemAdminKey)
if err != nil {
return nil, nil, err
}
return ionscale.NewClient(auth, addr, skipVerify)
}
func (t *Target) getAddr() string {
if len(t.addr) != 0 {
return t.addr
}
return config.GetString(ionscaleAddr, "https://localhost:8000")
}
func (t *Target) getInsecureSkipVerify() bool {
if t.insecureSkipVerify {
return true
}
return config.GetBool(ionscaleInsecureSkipVerify, false)
}
func (t *Target) getSystemAdminKey() string {
if len(t.systemAdminKey) != 0 {
return t.systemAdminKey
}
return config.GetString(ionscaleSystemAdminKey, "")
}
+58
View File
@@ -0,0 +1,58 @@
package cmd
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/internal/version"
"github.com/jsiebens/ionscale/pkg/gen/api"
"github.com/muesli/coral"
)
func versionCommand() *coral.Command {
var command = &coral.Command{
Use: "version",
Short: "Display version information",
SilenceUsage: true,
}
var target = Target{}
target.prepareCommand(command)
command.Run = func(cmd *coral.Command, args []string) {
clientVersion, clientRevision := version.GetReleaseInfo()
fmt.Printf(`
Client:
Version: %s
Git Revision: %s
`, clientVersion, clientRevision)
client, c, err := target.createGRPCClient()
if err != nil {
fmt.Printf(`
Server:
Error: %s
`, err)
return
}
defer safeClose(c)
resp, err := client.GetVersion(context.Background(), &api.GetVersionRequest{})
if err != nil {
fmt.Printf(`
Server:
Error: %s
`, err)
return
}
fmt.Printf(`
Server:
Addr: %s
Version: %s
Git Revision: %s
`, target.getAddr(), resp.Version, resp.Revision)
}
return command
}
+144
View File
@@ -0,0 +1,144 @@
package config
import (
"fmt"
"github.com/jsiebens/ionscale/internal/util"
"github.com/mitchellh/go-homedir"
"gopkg.in/yaml.v3"
"io/ioutil"
"strings"
"tailscale.com/types/key"
)
func LoadConfig(path string) (*Config, error) {
config := defaultConfig()
if len(path) != 0 {
expandedPath, err := homedir.Expand(path)
if err != nil {
return nil, err
}
b, err := ioutil.ReadFile(expandedPath)
if err != nil {
return nil, err
}
if err := yaml.Unmarshal(b, config); err != nil {
return nil, err
}
}
return config, nil
}
const (
listenAddrKey = "IONSCALE_LISTEN_ADDR"
serverUrlKey = "IONSCALE_SERVER_URL"
keysSystemAdminKeyKey = "IONSCALE_SYSTEM_ADMIN_KEY"
keysControlKeyKey = "IONSCALE_CONTROL_KEY"
keysLegacyControlKeyKey = "IONSCALE_LEGACY_CONTROL_KEY"
databaseUrlKey = "IONSCALE_DB_URL"
tlsDisableKey = "IONSCALE_TLS_DISABLE"
tlsCertFileKey = "IONSCALE_TLS_CERT_FILE"
tlsKeyFileKey = "IONSCALE_TLS_KEY_FILE"
metricsListenAddrKey = "IONSCALE_METRICS_LISTEN_ADDR"
loggingLevelKey = "IONSCALE_LOGGING_LEVEL"
loggingFormatKey = "IONSCALE_LOGGING_FORMAT"
loggingFileKey = "IONSCALE_LOGGING_FILE"
)
func defaultConfig() *Config {
return &Config{
ListenAddr: GetString(listenAddrKey, ":8000"),
ServerUrl: GetString(serverUrlKey, "https://localhost:8000"),
Keys: Keys{
SystemAdminKey: GetString(keysSystemAdminKeyKey, ""),
ControlKey: GetString(keysControlKeyKey, ""),
LegacyControlKey: GetString(keysLegacyControlKeyKey, ""),
},
Database: Database{
Url: GetString(databaseUrlKey, "ionscale.db"),
},
Tls: Tls{
Disable: GetBool(tlsDisableKey, false),
CertFile: GetString(tlsCertFileKey, ""),
KeyFile: GetString(tlsKeyFileKey, ""),
},
Metrics: Metrics{ListenAddr: GetString(metricsListenAddrKey, ":8001")},
Logging: Logging{
Level: GetString(loggingLevelKey, "info"),
Format: GetString(loggingFormatKey, ""),
File: GetString(loggingFileKey, ""),
},
}
}
type ServerKeys struct {
SystemAdminKey key.MachinePrivate
ControlKey key.MachinePrivate
LegacyControlKey key.MachinePrivate
}
type Config struct {
ListenAddr string `yaml:"listen_addr"`
ServerUrl string `yaml:"server_url"`
Tls Tls `yaml:"tls"`
Metrics Metrics `yaml:"metrics"`
Logging Logging `yaml:"logging"`
Keys Keys `yaml:"keys"`
Database Database `yaml:"database"`
}
type Metrics struct {
ListenAddr string `yaml:"listen_addr"`
}
type Tls struct {
Disable bool `yaml:"disable"`
CertFile string `yaml:"cert_file"`
KeyFile string `yaml:"key_file"`
}
type Logging struct {
Level string `yaml:"level"`
Format string `yaml:"format"`
File string `yaml:"file"`
}
type Database struct {
Url string `yaml:"url"`
}
type Keys struct {
SystemAdminKey string `yaml:"system_admin_key"`
ControlKey string `yaml:"control_key"`
LegacyControlKey string `yaml:"legacy_control_key"`
}
func (c *Config) CreateUrl(format string, a ...interface{}) string {
path := fmt.Sprintf(format, a...)
return strings.TrimSuffix(c.ServerUrl, "/") + "/" + strings.TrimPrefix(path, "/")
}
func (c *Config) ReadServerKeys() (*ServerKeys, error) {
systemAdminKey, err := util.ParseMachinePrivateKey(c.Keys.SystemAdminKey)
if err != nil {
return nil, fmt.Errorf("error reading system admin key: %v", err)
}
controlKey, err := util.ParseMachinePrivateKey(c.Keys.ControlKey)
if err != nil {
return nil, fmt.Errorf("error reading control key: %v", err)
}
legacyControlKey, err := util.ParseMachinePrivateKey(c.Keys.LegacyControlKey)
if err != nil {
return nil, fmt.Errorf("error reading legacy control key: %v", err)
}
return &ServerKeys{
SystemAdminKey: *systemAdminKey,
ControlKey: *controlKey,
LegacyControlKey: *legacyControlKey,
}, nil
}
+22
View File
@@ -0,0 +1,22 @@
package config
import (
"os"
"strings"
)
func GetBool(key string, defaultValue bool) bool {
v := os.Getenv(key)
if len(v) > 0 {
return strings.ToLower(v) == "true"
}
return defaultValue
}
func GetString(key, defaultValue string) string {
v := os.Getenv(key)
if v != "" {
return v
}
return defaultValue
}
+133
View File
@@ -0,0 +1,133 @@
package database
import (
"context"
"encoding/json"
"errors"
"github.com/glebarez/sqlite"
"github.com/hashicorp/go-hclog"
"net/http"
"tailscale.com/tailcfg"
"time"
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/internal/domain"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
func OpenDB(config *config.Database, logger hclog.Logger) (*gorm.DB, domain.Repository, error) {
gormDB, err := createDB(config, logger)
if err != nil {
return nil, nil, err
}
repository := domain.NewRepository(gormDB)
if err := migrate(gormDB, repository); err != nil {
return nil, nil, err
}
return gormDB, repository, nil
}
func createDB(config *config.Database, logger hclog.Logger) (*gorm.DB, error) {
gormConfig := &gorm.Config{
Logger: &GormLoggerAdapter{logger: logger.Named("db")},
}
return gorm.Open(sqlite.Open(config.Url), gormConfig)
}
func migrate(db *gorm.DB, repository domain.Repository) error {
err := db.AutoMigrate(
&domain.ServerConfig{},
&domain.Tailnet{},
&domain.User{},
&domain.AuthKey{},
&domain.Machine{},
)
if err != nil {
return err
}
if err := initializeDERPMap(repository); err != nil {
return err
}
return nil
}
func initializeDERPMap(repository domain.Repository) error {
ctx := context.Background()
derpMap, err := repository.GetDERPMap(ctx)
if err != nil {
return err
}
if derpMap != nil {
return nil
}
getJson := func(url string, target interface{}) error {
c := http.Client{Timeout: 5 * time.Second}
r, err := c.Get(url)
if err != nil {
return err
}
defer r.Body.Close()
return json.NewDecoder(r.Body).Decode(target)
}
m := &tailcfg.DERPMap{}
if err := getJson("https://controlplane.tailscale.com/derpmap/default", m); err != nil {
return err
}
if err := repository.SetDERPMap(ctx, m); err != nil {
return err
}
return nil
}
type GormLoggerAdapter struct {
logger hclog.Logger
}
func (g *GormLoggerAdapter) LogMode(level logger.LogLevel) logger.Interface {
return g
}
func (g *GormLoggerAdapter) Info(ctx context.Context, s string, i ...interface{}) {
g.logger.Info(s, i)
}
func (g *GormLoggerAdapter) Warn(ctx context.Context, s string, i ...interface{}) {
g.logger.Warn(s, i)
}
func (g *GormLoggerAdapter) Error(ctx context.Context, s string, i ...interface{}) {
g.logger.Error(s, i)
}
func (g *GormLoggerAdapter) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
elapsed := time.Since(begin)
switch {
case err != nil && !errors.Is(err, gorm.ErrRecordNotFound):
sql, rows := fc()
if rows == -1 {
g.logger.Error("Error executing query", "sql", sql, "start_time", begin.Format(time.RFC3339), "duration", elapsed, "err", err)
} else {
g.logger.Error("Error executing query", "sql", sql, "start_time", begin.Format(time.RFC3339), "duration", elapsed, "rows", rows, "err", err)
}
case g.logger.IsTrace():
sql, rows := fc()
if rows == -1 {
g.logger.Trace("Statement executed", "sql", sql, "start_time", begin.Format(time.RFC3339), "duration", elapsed)
} else {
g.logger.Trace("Statement executed", "sql", sql, "start_time", begin.Format(time.RFC3339), "duration", elapsed, "rows", rows)
}
}
}
+109
View File
@@ -0,0 +1,109 @@
package domain
import (
"context"
"errors"
"fmt"
"github.com/jsiebens/ionscale/internal/util"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"strings"
"time"
)
func CreateAuthKey(tailnet *Tailnet, user *User, ephemeral bool, tags Tags, expiresAt *time.Time) (string, *AuthKey) {
key := util.RandStringBytes(12)
pwd := util.RandStringBytes(22)
value := fmt.Sprintf("%s_%s", key, pwd)
hash, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
if err != nil {
panic(err)
}
return value, &AuthKey{
ID: util.NextID(),
Key: key,
Hash: string(hash),
Ephemeral: ephemeral,
Tags: tags,
CreatedAt: time.Now().UTC(),
ExpiresAt: expiresAt,
TailnetID: tailnet.ID,
UserID: user.ID,
}
}
type AuthKey struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Key string `gorm:"type:varchar(64);unique_index"`
Hash string
Ephemeral bool
Tags Tags
CreatedAt time.Time
ExpiresAt *time.Time
TailnetID uint64
Tailnet Tailnet
UserID uint64
User User
}
func (r *repository) SaveAuthKey(ctx context.Context, key *AuthKey) error {
tx := r.withContext(ctx).Save(key)
if tx.Error != nil {
return tx.Error
}
return nil
}
func (r *repository) DeleteAuthKey(ctx context.Context, id uint64) (bool, error) {
tx := r.withContext(ctx).Delete(&AuthKey{}, id)
return tx.RowsAffected == 1, tx.Error
}
func (r *repository) ListAuthKeys(ctx context.Context, tailnetID uint64) ([]AuthKey, error) {
var authKeys = []AuthKey{}
tx := (r.withContext(ctx).
Preload("User").
Preload("Tailnet")).
Where("tailnet_id = ?", tailnetID).
Find(&authKeys)
if tx.Error != nil {
return nil, tx.Error
}
return authKeys, nil
}
func (r *repository) LoadAuthKey(ctx context.Context, key string) (*AuthKey, error) {
split := strings.Split(key, "_")
if len(split) != 2 {
return nil, nil
}
var m AuthKey
tx := r.withContext(ctx).Preload("User").Preload("Tailnet").First(&m, "key = ?", split[0])
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
if tx.Error != nil {
return nil, tx.Error
}
if err := bcrypt.CompareHashAndPassword([]byte(m.Hash), []byte(split[1])); err != nil {
return nil, nil
}
if !m.ExpiresAt.IsZero() && m.ExpiresAt.Before(time.Now()) {
return nil, nil
}
return &m, nil
}
+253
View File
@@ -0,0 +1,253 @@
package domain
import (
"context"
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
"gorm.io/gorm"
"gorm.io/gorm/schema"
"tailscale.com/tailcfg"
"time"
)
type Machine struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Name string
NameIdx uint64
MachineKey string
NodeKey string
DiscoKey string
Ephemeral bool
RegisteredTags Tags
Tags Tags
HostInfo HostInfo
Endpoints Endpoints
IPv4 string
IPv6 string
CreatedAt time.Time
ExpiresAt *time.Time
LastSeen *time.Time
UserID uint64
User User
TailnetID uint64
Tailnet Tailnet
}
type Machines []Machine
type HostInfo tailcfg.Hostinfo
func (hi *HostInfo) Scan(destination interface{}) error {
switch value := destination.(type) {
case []byte:
return json.Unmarshal(value, hi)
default:
return fmt.Errorf("unexpected data type %T", destination)
}
}
func (hi HostInfo) Value() (driver.Value, error) {
bytes, err := json.Marshal(hi)
return bytes, err
}
// GormDataType gorm common data type
func (HostInfo) GormDataType() string {
return "json"
}
// GormDBDataType gorm db data type
func (HostInfo) GormDBDataType(db *gorm.DB, field *schema.Field) string {
switch db.Dialector.Name() {
case "sqlite":
return "JSON"
}
return ""
}
type Endpoints []string
func (hi *Endpoints) Scan(destination interface{}) error {
switch value := destination.(type) {
case []byte:
return json.Unmarshal(value, hi)
default:
return fmt.Errorf("unexpected data type %T", destination)
}
}
func (hi Endpoints) Value() (driver.Value, error) {
bytes, err := json.Marshal(hi)
return bytes, err
}
// GormDataType gorm common data type
func (Endpoints) GormDataType() string {
return "json"
}
// GormDBDataType gorm db data type
func (Endpoints) GormDBDataType(db *gorm.DB, field *schema.Field) string {
switch db.Dialector.Name() {
case "sqlite":
return "JSON"
}
return ""
}
func (r *repository) SaveMachine(ctx context.Context, machine *Machine) error {
tx := r.withContext(ctx).Save(machine)
if tx.Error != nil {
return tx.Error
}
return nil
}
func (r *repository) DeleteMachine(ctx context.Context, id uint64) (bool, error) {
tx := r.withContext(ctx).Delete(&Machine{}, id)
return tx.RowsAffected == 1, tx.Error
}
func (r *repository) GetMachine(ctx context.Context, machineID uint64) (*Machine, error) {
var m Machine
tx := r.withContext(ctx).Preload("Tailnet").Preload("User").First(&m, "id = ?", machineID)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
if tx.Error != nil {
return nil, tx.Error
}
return &m, nil
}
func (r *repository) GetNextMachineNameIndex(ctx context.Context, tailnetID uint64, name string) (uint64, error) {
var m Machine
tx := r.withContext(ctx).
Where("name = ? AND tailnet_id = ?", name, tailnetID).
Order("name_idx desc").
First(&m)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return 0, nil
}
if tx.Error != nil {
return 0, tx.Error
}
return m.NameIdx + 1, nil
}
func (r *repository) GetMachineByKey(ctx context.Context, tailnetID uint64, machineKey string) (*Machine, error) {
var m Machine
tx := r.withContext(ctx).Preload("Tailnet").Preload("User").First(&m, "tailnet_id = ? AND machine_key = ?", tailnetID, machineKey)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
if tx.Error != nil {
return nil, tx.Error
}
return &m, nil
}
func (r *repository) GetMachineByKeys(ctx context.Context, machineKey string, nodeKey string) (*Machine, error) {
var m Machine
tx := r.withContext(ctx).Preload("Tailnet").Preload("User").First(&m, "machine_key = ? AND node_key = ?", machineKey, nodeKey)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
if tx.Error != nil {
return nil, tx.Error
}
return &m, nil
}
func (r *repository) CountMachinesWithIPv4(ctx context.Context, ip string) (int64, error) {
var count int64
tx := r.withContext(ctx).Model(&Machine{}).Where("ipv4 = ?", ip).Count(&count)
if tx.Error != nil {
return 0, tx.Error
}
return count, nil
}
func (r *repository) ListMachineByTailnet(ctx context.Context, tailnetID uint64) (Machines, error) {
var machines = []Machine{}
tx := r.withContext(ctx).
Preload("Tailnet").
Preload("User").
Where("tailnet_id = ?", tailnetID).
Order("name asc, name_idx asc").
Find(&machines)
if tx.Error != nil {
return nil, tx.Error
}
return machines, nil
}
func (r *repository) ListMachinePeers(ctx context.Context, tailnetID uint64, key string) (Machines, error) {
var machines = []Machine{}
tx := r.withContext(ctx).
Preload("Tailnet").
Preload("User").
Where("tailnet_id = ? AND machine_key <> ?", tailnetID, key).
Order("id asc").
Find(&machines)
if tx.Error != nil {
return nil, tx.Error
}
return machines, nil
}
func (r *repository) ListInactiveEphemeralMachines(ctx context.Context, t time.Time) (Machines, error) {
var machines = []Machine{}
tx := r.withContext(ctx).
Where("ephemeral = ? AND last_seen < ?", true, t.UTC()).
Find(&machines)
if tx.Error != nil {
return nil, tx.Error
}
return machines, nil
}
func (r *repository) SetMachineLastSeen(ctx context.Context, machineID uint64) error {
now := time.Now().UTC()
tx := r.withContext(ctx).Model(Machine{}).Where("id = ?", machineID).Updates(map[string]interface{}{"last_seen": &now})
if tx.Error != nil {
return tx.Error
}
return nil
}
+51
View File
@@ -0,0 +1,51 @@
package domain
import (
"context"
"gorm.io/gorm"
"tailscale.com/tailcfg"
"time"
)
type Repository interface {
GetDERPMap(ctx context.Context) (*tailcfg.DERPMap, error)
SetDERPMap(ctx context.Context, v *tailcfg.DERPMap) error
GetOrCreateTailnet(ctx context.Context, name string) (*Tailnet, bool, error)
GetTailnet(ctx context.Context, id uint64) (*Tailnet, error)
ListTailnets(ctx context.Context) ([]Tailnet, error)
SaveAuthKey(ctx context.Context, key *AuthKey) error
DeleteAuthKey(ctx context.Context, id uint64) (bool, error)
ListAuthKeys(ctx context.Context, tailnetID uint64) ([]AuthKey, error)
LoadAuthKey(ctx context.Context, key string) (*AuthKey, error)
GetOrCreateServiceUser(ctx context.Context, tailnet *Tailnet) (*User, bool, error)
ListUsers(ctx context.Context, tailnetID uint64) (Users, error)
SaveMachine(ctx context.Context, m *Machine) error
DeleteMachine(ctx context.Context, id uint64) (bool, error)
GetMachine(ctx context.Context, id uint64) (*Machine, error)
GetMachineByKey(ctx context.Context, tailnetID uint64, key string) (*Machine, error)
GetMachineByKeys(ctx context.Context, machineKey string, nodeKey string) (*Machine, error)
CountMachinesWithIPv4(ctx context.Context, ip string) (int64, error)
GetNextMachineNameIndex(ctx context.Context, tailnetID uint64, name string) (uint64, error)
ListMachineByTailnet(ctx context.Context, tailnetID uint64) (Machines, error)
ListMachinePeers(ctx context.Context, tailnetID uint64, key string) (Machines, error)
ListInactiveEphemeralMachines(ctx context.Context, checkpoint time.Time) (Machines, error)
SetMachineLastSeen(ctx context.Context, machineID uint64) error
}
func NewRepository(db *gorm.DB) Repository {
return &repository{
db: db,
}
}
type repository struct {
db *gorm.DB
}
func (r *repository) withContext(ctx context.Context) *gorm.DB {
return r.db.WithContext(ctx)
}
+63
View File
@@ -0,0 +1,63 @@
package domain
import (
"context"
"encoding/json"
"errors"
"gorm.io/gorm"
"tailscale.com/tailcfg"
)
type ServerConfig struct {
Key string `gorm:"primary_key"`
Value []byte
}
func (r *repository) GetDERPMap(ctx context.Context) (*tailcfg.DERPMap, error) {
var m tailcfg.DERPMap
err := r.getServerConfig(ctx, "derp_map", &m)
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
if err != nil {
return nil, err
}
return &m, nil
}
func (r *repository) SetDERPMap(ctx context.Context, v *tailcfg.DERPMap) error {
return r.setServerConfig(ctx, "derp_map", v)
}
func (r *repository) getServerConfig(ctx context.Context, s string, v interface{}) error {
var m ServerConfig
tx := r.withContext(ctx).Take(&m, "key = ?", s)
if tx.Error != nil {
return tx.Error
}
err := json.Unmarshal(m.Value, v)
if err != nil {
return err
}
return nil
}
func (r *repository) setServerConfig(ctx context.Context, s string, v interface{}) error {
marshal, err := json.Marshal(v)
if err != nil {
return err
}
c := &ServerConfig{
Key: s,
Value: marshal,
}
tx := r.withContext(ctx).Save(c)
return tx.Error
}
+48
View File
@@ -0,0 +1,48 @@
package domain
import (
"database/sql/driver"
"fmt"
"strings"
)
type Tags []string
func (i *Tags) Scan(destination interface{}) error {
switch value := destination.(type) {
case string:
t := strings.Trim(value, "|")
if len(t) == 0 {
*i = []string{}
} else {
*i = strings.Split(t, "|")
}
default:
return fmt.Errorf("unexpected data type %T", destination)
}
return nil
}
func (i Tags) Value() (driver.Value, error) {
v := "|" + strings.Join(i, "|") + "|"
return v, nil
}
func SanitizeTags(input []string) Tags {
keys := make(map[string]bool)
var tags []string
for _, v := range input {
var entry string
if strings.HasPrefix(v, "tag:") {
entry = v[4:]
} else {
entry = v
}
if _, value := keys[entry]; !value {
keys[entry] = true
tags = append(tags, entry)
}
}
return tags
}
+50
View File
@@ -0,0 +1,50 @@
package domain
import (
"context"
"errors"
"github.com/jsiebens/ionscale/internal/util"
"gorm.io/gorm"
)
type Tailnet struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Name string `gorm:"type:varchar(64);unique_index"`
}
func (r *repository) GetOrCreateTailnet(ctx context.Context, name string) (*Tailnet, bool, error) {
tailnet := &Tailnet{}
id := util.NextID()
tx := r.withContext(ctx).Where(Tailnet{Name: name}).Attrs(Tailnet{ID: id}).FirstOrCreate(tailnet)
if tx.Error != nil {
return nil, false, tx.Error
}
return tailnet, tailnet.ID == id, nil
}
func (r *repository) GetTailnet(ctx context.Context, id uint64) (*Tailnet, error) {
var t Tailnet
tx := r.withContext(ctx).Take(&t, "id = ?", id)
if errors.Is(tx.Error, gorm.ErrRecordNotFound) {
return nil, nil
}
if tx.Error != nil {
return nil, tx.Error
}
return &t, nil
}
func (r *repository) ListTailnets(ctx context.Context) ([]Tailnet, error) {
var tailnets = []Tailnet{}
tx := r.withContext(ctx).Find(&tailnets)
if tx.Error != nil {
return nil, tx.Error
}
return tailnets, nil
}
+51
View File
@@ -0,0 +1,51 @@
package domain
import (
"context"
"github.com/jsiebens/ionscale/internal/util"
)
type TailnetRole string
const (
TailnetRoleService TailnetRole = "service"
)
type User struct {
ID uint64 `gorm:"primary_key;autoIncrement:false"`
Name string
TailnetRole TailnetRole
TailnetID uint64
Tailnet Tailnet
}
type Users []User
func (r *repository) GetOrCreateServiceUser(ctx context.Context, tailnet *Tailnet) (*User, bool, error) {
user := &User{}
id := util.NextID()
query := User{Name: tailnet.Name, TailnetID: tailnet.ID, TailnetRole: TailnetRoleService}
attrs := User{ID: id, Name: tailnet.Name, TailnetID: tailnet.ID, TailnetRole: TailnetRoleService}
tx := r.withContext(ctx).Where(query).Attrs(attrs).FirstOrCreate(user)
if tx.Error != nil {
return nil, false, tx.Error
}
return user, user.ID == id, nil
}
func (r *repository) ListUsers(ctx context.Context, tailnetID uint64) (Users, error) {
var users = []User{}
tx := r.withContext(ctx).Where("tailnet_id = ?", tailnetID).Find(&users)
if tx.Error != nil {
return nil, tx.Error
}
return users, nil
}
+163
View File
@@ -0,0 +1,163 @@
package handlers
import (
"github.com/jsiebens/ionscale/internal/addr"
"net/http"
"time"
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/util"
"github.com/labstack/echo/v4"
"github.com/patrickmn/go-cache"
"tailscale.com/util/dnsname"
)
func NewAuthenticationHandlers(
config *config.Config,
repository domain.Repository,
pendingMachineRegistrationRequests *cache.Cache) *AuthenticationHandlers {
return &AuthenticationHandlers{
config: config,
repository: repository,
pendingMachineRegistrationRequests: pendingMachineRegistrationRequests,
}
}
type AuthenticationHandlers struct {
repository domain.Repository
config *config.Config
pendingMachineRegistrationRequests *cache.Cache
}
func (h *AuthenticationHandlers) StartAuth(c echo.Context) error {
key := c.Param("key")
authKey := c.FormValue("ak")
if _, ok := h.pendingMachineRegistrationRequests.Get(key); !ok {
return c.Redirect(http.StatusFound, "/a/error")
}
if authKey != "" {
return h.endMachineRegistrationFlow(c, key, authKey)
}
return c.Render(http.StatusOK, "auth.html", nil)
}
func (h *AuthenticationHandlers) Success(c echo.Context) error {
return c.Render(http.StatusOK, "success.html", nil)
}
func (h *AuthenticationHandlers) Error(c echo.Context) error {
e := c.QueryParam("e")
switch e {
case "iak":
return c.Render(http.StatusForbidden, "invalidauthkey.html", nil)
}
return c.Render(http.StatusOK, "error.html", nil)
}
func (h *AuthenticationHandlers) endMachineRegistrationFlow(c echo.Context, registrationKey, authKeyParam string) error {
ctx := c.Request().Context()
defer h.pendingMachineRegistrationRequests.Delete(registrationKey)
preqItem, preqOK := h.pendingMachineRegistrationRequests.Get(registrationKey)
if !preqOK {
return c.Redirect(http.StatusFound, "/a/error")
}
preq := preqItem.(*pendingMachineRegistrationRequest)
req := preq.request
machineKey := preq.machineKey
nodeKey := req.NodeKey.String()
authKey, err := h.repository.LoadAuthKey(ctx, authKeyParam)
if err != nil {
return err
}
if authKey == nil {
return c.Redirect(http.StatusFound, "/a/error?e=iak")
}
tailnet := authKey.Tailnet
user := authKey.User
var m *domain.Machine
m, err = h.repository.GetMachineByKey(ctx, tailnet.ID, machineKey)
if err != nil {
return err
}
if m == nil {
now := time.Now().UTC()
registeredTags := authKey.Tags
advertisedTags := domain.SanitizeTags(req.Hostinfo.RequestTags)
tags := append(registeredTags, advertisedTags...)
sanitizeHostname := dnsname.SanitizeHostname(req.Hostinfo.Hostname)
nameIdx, err := h.repository.GetNextMachineNameIndex(ctx, tailnet.ID, sanitizeHostname)
if err != nil {
return err
}
m = &domain.Machine{
ID: util.NextID(),
Name: sanitizeHostname,
NameIdx: nameIdx,
MachineKey: machineKey,
NodeKey: nodeKey,
Ephemeral: authKey.Ephemeral,
RegisteredTags: registeredTags,
Tags: domain.SanitizeTags(tags),
CreatedAt: now,
User: user,
Tailnet: tailnet,
}
if !req.Expiry.IsZero() {
m.ExpiresAt = &req.Expiry
}
ipv4, ipv6, err := addr.SelectIP(checkIP(ctx, h.repository.CountMachinesWithIPv4))
if err != nil {
return err
}
m.IPv4 = ipv4.String()
m.IPv6 = ipv6.String()
} else {
registeredTags := authKey.Tags
advertisedTags := domain.SanitizeTags(req.Hostinfo.RequestTags)
tags := append(registeredTags, advertisedTags...)
sanitizeHostname := dnsname.SanitizeHostname(req.Hostinfo.Hostname)
if m.Name != sanitizeHostname {
nameIdx, err := h.repository.GetNextMachineNameIndex(ctx, tailnet.ID, sanitizeHostname)
if err != nil {
return err
}
m.Name = sanitizeHostname
m.NameIdx = nameIdx
}
m.NodeKey = nodeKey
m.Ephemeral = authKey.Ephemeral
m.RegisteredTags = registeredTags
m.Tags = domain.SanitizeTags(tags)
m.UserID = user.ID
m.User = user
m.TailnetID = tailnet.ID
m.Tailnet = tailnet
m.ExpiresAt = nil
}
if err := h.repository.SaveMachine(ctx, m); err != nil {
return err
}
return c.Redirect(http.StatusFound, "/a/success")
}
+17
View File
@@ -0,0 +1,17 @@
package handlers
import (
"fmt"
"github.com/jsiebens/ionscale/internal/version"
"github.com/labstack/echo/v4"
)
func IndexHandler(code int) echo.HandlerFunc {
return func(c echo.Context) error {
info, s := version.GetReleaseInfo()
data := map[string]interface{}{
"Version": fmt.Sprintf("%s - %s", info, s),
}
return c.Render(code, "index.html", data)
}
}
+39
View File
@@ -0,0 +1,39 @@
package handlers
import (
"github.com/jsiebens/ionscale/internal/config"
"github.com/labstack/echo/v4"
"net/http"
"strconv"
"tailscale.com/tailcfg"
)
const (
NoiseCapabilityVersion = 28
)
func KeyHandler(keys *config.ServerKeys) echo.HandlerFunc {
legacyPublicKey := keys.LegacyControlKey.Public()
publicKey := keys.ControlKey.Public()
return func(c echo.Context) error {
v := c.QueryParam("v")
if v != "" {
clientCapabilityVersion, err := strconv.Atoi(v)
if err != nil {
return c.String(http.StatusBadRequest, "Invalid version")
}
if clientCapabilityVersion >= NoiseCapabilityVersion {
resp := tailcfg.OverTLSPublicKeyResponse{
LegacyPublicKey: legacyPublicKey,
PublicKey: publicKey,
}
return c.JSON(http.StatusOK, resp)
}
}
return c.String(http.StatusOK, legacyPublicKey.UntypedHexString())
}
}
+39
View File
@@ -0,0 +1,39 @@
package handlers
import (
"context"
"github.com/labstack/echo/v4"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
"net/http"
"tailscale.com/control/controlhttp"
"tailscale.com/net/netutil"
"tailscale.com/types/key"
)
type NoiseHandlers struct {
controlKey key.MachinePrivate
createPeerHandler CreatePeerHandler
}
type CreatePeerHandler func(p key.MachinePublic) http.Handler
func NewNoiseHandlers(controlKey key.MachinePrivate, createPeerHandler CreatePeerHandler) *NoiseHandlers {
return &NoiseHandlers{
controlKey: controlKey,
createPeerHandler: createPeerHandler,
}
}
func (h *NoiseHandlers) Upgrade(c echo.Context) error {
conn, err := controlhttp.AcceptHTTP(context.Background(), c.Response(), c.Request(), h.controlKey)
if err != nil {
return err
}
handler := h.createPeerHandler(conn.Peer())
server := http.Server{}
server.Handler = h2c.NewHandler(handler, &http2.Server{})
return server.Serve(netutil.NewOneConnListener(conn, nil))
}
+352
View File
@@ -0,0 +1,352 @@
package handlers
import (
"context"
"github.com/jsiebens/ionscale/internal/bind"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/mapping"
"github.com/labstack/echo/v4"
"net/http"
"tailscale.com/tailcfg"
"tailscale.com/util/dnsname"
"time"
)
const (
keepAliveInterval = 1 * time.Minute
)
func NewPollNetMapHandler(
createBinder bind.Factory,
brokers *broker.BrokerPool,
repository domain.Repository,
offlineTimers *OfflineTimers) *PollNetMapHandler {
handler := &PollNetMapHandler{
createBinder: createBinder,
brokers: brokers.Get,
repository: repository,
offlineTimers: offlineTimers,
}
return handler
}
type PollNetMapHandler struct {
createBinder bind.Factory
repository domain.Repository
brokers func(uint64) broker.Broker
offlineTimers *OfflineTimers
}
func (h *PollNetMapHandler) PollNetMap(c echo.Context) error {
ctx := c.Request().Context()
binder, err := h.createBinder(c)
if err != nil {
return err
}
req := &tailcfg.MapRequest{}
if err := binder.BindRequest(c, req); err != nil {
return err
}
machineKey := binder.Peer().String()
nodeKey := req.NodeKey.String()
var m *domain.Machine
m, err = h.repository.GetMachineByKeys(ctx, machineKey, nodeKey)
if err != nil {
return err
}
if m == nil {
return echo.NewHTTPError(http.StatusNotFound)
}
if req.ReadOnly {
return h.handleReadOnly(c, binder, m, req)
} else {
return h.handleUpdate(c, binder, m, req)
}
}
func (h *PollNetMapHandler) handleUpdate(c echo.Context, binder bind.Binder, m *domain.Machine, mapRequest *tailcfg.MapRequest) error {
ctx := c.Request().Context()
now := time.Now().UTC()
m.HostInfo = domain.HostInfo(*mapRequest.Hostinfo)
m.DiscoKey = mapRequest.DiscoKey.String()
m.Endpoints = mapRequest.Endpoints
m.LastSeen = &now
if err := h.repository.SaveMachine(ctx, m); err != nil {
return err
}
tailnetID := m.TailnetID
machineID := m.ID
tailnetBroker := h.brokers(tailnetID)
tailnetBroker.SignalPeerUpdated(machineID)
if !mapRequest.Stream {
return c.String(http.StatusOK, "")
}
var syncedPeers = make(map[uint64]bool)
response, syncedPeers, err := h.createMapResponse(m, binder, mapRequest, false, make(map[uint64]bool))
if err != nil {
return err
}
updateChan := make(chan *broker.Signal, 20)
client := broker.NewClient(machineID, updateChan)
tailnetBroker.AddClient(&client)
h.cancelOfflineMessage(machineID)
// Listen to connection close and un-register messageChan
notify := c.Request().Context().Done()
keepAliveResponse, err := h.createKeepAliveResponse(binder, mapRequest)
if err != nil {
return err
}
keepAliveTicker := time.NewTicker(keepAliveInterval)
syncTicker := time.NewTicker(5 * time.Second)
var latestSync = time.Now()
var latestUpdate = latestSync
c.Response().WriteHeader(http.StatusOK)
if _, err := c.Response().Write(response); err != nil {
return err
}
c.Response().Flush()
defer func() {
tailnetBroker.RemoveClient(machineID)
keepAliveTicker.Stop()
syncTicker.Stop()
_ = h.repository.SetMachineLastSeen(ctx, machineID)
h.scheduleOfflineMessage(tailnetID, machineID)
}()
for {
select {
case s := <-updateChan:
if s.PeerUpdated == nil || *s.PeerUpdated != machineID {
latestUpdate = time.Now()
}
case <-keepAliveTicker.C:
if mapRequest.KeepAlive {
if _, err := c.Response().Write(keepAliveResponse); err != nil {
return err
}
_ = h.repository.SetMachineLastSeen(ctx, machineID)
c.Response().Flush()
}
case <-syncTicker.C:
if latestSync.Before(latestUpdate) {
machine, err := h.repository.GetMachine(ctx, machineID)
if err != nil {
return err
}
if machine == nil {
return nil
}
var payload []byte
var payloadErr error
payload, syncedPeers, payloadErr = h.createMapResponse(machine, binder, mapRequest, true, syncedPeers)
if payloadErr != nil {
return payloadErr
}
if _, err := c.Response().Write(payload); err != nil {
return err
}
c.Response().Flush()
latestSync = latestUpdate
}
case <-notify:
return nil
}
}
}
func (h *PollNetMapHandler) handleReadOnly(c echo.Context, binder bind.Binder, m *domain.Machine, request *tailcfg.MapRequest) error {
ctx := c.Request().Context()
m.HostInfo = domain.HostInfo(*request.Hostinfo)
m.DiscoKey = request.DiscoKey.String()
if err := h.repository.SaveMachine(ctx, m); err != nil {
return err
}
response, _, err := h.createMapResponse(m, binder, request, false, map[uint64]bool{})
if err != nil {
return err
}
_, err = c.Response().Write(response)
return err
}
func (h *PollNetMapHandler) scheduleOfflineMessage(tailnetID, machineID uint64) {
h.offlineTimers.startCh <- [2]uint64{tailnetID, machineID}
}
func (h *PollNetMapHandler) cancelOfflineMessage(machineID uint64) {
h.offlineTimers.stopCh <- machineID
}
func (h *PollNetMapHandler) createKeepAliveResponse(binder bind.Binder, request *tailcfg.MapRequest) ([]byte, error) {
mapResponse := &tailcfg.MapResponse{
KeepAlive: true,
}
return binder.Marshal(request.Compress, mapResponse)
}
func (h *PollNetMapHandler) createMapResponse(m *domain.Machine, binder bind.Binder, request *tailcfg.MapRequest, delta bool, prevSyncedPeerIDs map[uint64]bool) ([]byte, map[uint64]bool, error) {
node, err := mapping.ToNode(m, true)
if err != nil {
return nil, nil, err
}
users, err := h.repository.ListUsers(context.TODO(), m.TailnetID)
if err != nil {
return nil, nil, err
}
var changedPeers []*tailcfg.Node
var removedPeers []tailcfg.NodeID
candidatePeers, err := h.repository.ListMachinePeers(context.TODO(), m.TailnetID, m.MachineKey)
if err != nil {
return nil, nil, err
}
syncedPeerIDs := map[uint64]bool{}
for _, peer := range candidatePeers {
n, err := mapping.ToNode(&peer, h.brokers(peer.TailnetID).IsConnected(peer.ID))
if err != nil {
return nil, nil, err
}
changedPeers = append(changedPeers, n)
syncedPeerIDs[peer.ID] = true
delete(prevSyncedPeerIDs, peer.ID)
}
for p, _ := range prevSyncedPeerIDs {
removedPeers = append(removedPeers, tailcfg.NodeID(p))
}
derpMap, err := h.repository.GetDERPMap(context.TODO())
if err != nil {
return nil, nil, err
}
rules := tailcfg.FilterAllowAll
controlTime := time.Now().UTC()
var mapResponse *tailcfg.MapResponse
if !delta {
mapResponse = &tailcfg.MapResponse{
KeepAlive: false,
Node: node,
PacketFilter: rules,
DERPMap: derpMap,
Domain: dnsname.SanitizeHostname(m.Tailnet.Name),
Peers: changedPeers,
UserProfiles: mapping.ToUserProfiles(users),
ControlTime: &controlTime,
}
} else {
mapResponse = &tailcfg.MapResponse{
PacketFilter: rules,
PeersChanged: changedPeers,
PeersRemoved: removedPeers,
UserProfiles: mapping.ToUserProfiles(users),
ControlTime: &controlTime,
}
}
if request.OmitPeers {
mapResponse.PeersChanged = nil
mapResponse.PeersRemoved = nil
mapResponse.Peers = nil
}
payload, err := binder.Marshal(request.Compress, mapResponse)
return payload, syncedPeerIDs, nil
}
func NewOfflineTimers(repository domain.Repository, brokers *broker.BrokerPool) *OfflineTimers {
return &OfflineTimers{
repository: repository,
brokers: brokers.Get,
data: make(map[uint64]*time.Timer),
startCh: make(chan [2]uint64),
stopCh: make(chan uint64),
}
}
type OfflineTimers struct {
repository domain.Repository
brokers func(uint64) broker.Broker
data map[uint64]*time.Timer
stopCh chan uint64
startCh chan [2]uint64
}
func (o *OfflineTimers) Start() {
for {
select {
case i := <-o.startCh:
o.scheduleOfflineMessage(i[0], i[1])
case m := <-o.stopCh:
o.cancelOfflineMessage(m)
}
}
}
func (o *OfflineTimers) scheduleOfflineMessage(tailnetID, machineID uint64) {
t, ok := o.data[machineID]
if ok {
t.Stop()
delete(o.data, machineID)
}
timer := time.NewTimer(10 * time.Second)
go func() {
<-timer.C
if !o.brokers(tailnetID).IsConnected(machineID) {
o.brokers(tailnetID).SignalPeerUpdated(machineID)
o.stopCh <- machineID
}
}()
o.data[machineID] = timer
}
func (o *OfflineTimers) cancelOfflineMessage(machineID uint64) {
t, ok := o.data[machineID]
if ok {
t.Stop()
delete(o.data, machineID)
}
}
+61
View File
@@ -0,0 +1,61 @@
package handlers
import (
"context"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/domain"
"time"
)
const (
ticker = 10 * time.Minute
inactivityTimeout = 30 * time.Minute
)
func NewReaper(brokers *broker.BrokerPool, repository domain.Repository) *Reaper {
return &Reaper{
brokers: brokers,
repository: repository,
}
}
type Reaper struct {
brokers *broker.BrokerPool
repository domain.Repository
}
func (r *Reaper) Start() {
t := time.NewTicker(ticker)
for range t.C {
r.reapInactiveEphemeralNodes()
}
}
func (r *Reaper) reapInactiveEphemeralNodes() {
ctx := context.Background()
now := time.Now().UTC()
checkpoint := now.Add(-inactivityTimeout)
machines, err := r.repository.ListInactiveEphemeralMachines(ctx, checkpoint)
if err != nil {
return
}
var removedNodes = make(map[uint64][]uint64)
for _, m := range machines {
if now.After(m.LastSeen.Add(inactivityTimeout)) {
ok, err := r.repository.DeleteMachine(ctx, m.ID)
if err != nil {
continue
}
if ok {
removedNodes[m.TailnetID] = append(removedNodes[m.TailnetID], m.ID)
}
}
}
if len(removedNodes) != 0 {
for i, p := range removedNodes {
r.brokers.Get(i).SignalPeersRemoved(p)
}
}
}
+235
View File
@@ -0,0 +1,235 @@
package handlers
import (
"context"
"github.com/jsiebens/ionscale/internal/addr"
"github.com/jsiebens/ionscale/internal/bind"
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/util"
"github.com/labstack/echo/v4"
"github.com/patrickmn/go-cache"
"inet.af/netaddr"
"net/http"
"tailscale.com/tailcfg"
"tailscale.com/util/dnsname"
"time"
)
func NewRegistrationHandlers(
createBinder bind.Factory,
config *config.Config,
repository domain.Repository,
pendingMachineRegistrationRequests *cache.Cache) *RegistrationHandlers {
return &RegistrationHandlers{
createBinder: createBinder,
repository: repository,
config: config,
pendingMachineRegistrationRequests: pendingMachineRegistrationRequests,
}
}
type pendingMachineRegistrationRequest struct {
machineKey string
request *tailcfg.RegisterRequest
}
type RegistrationHandlers struct {
createBinder bind.Factory
repository domain.Repository
config *config.Config
pendingMachineRegistrationRequests *cache.Cache
}
func (h *RegistrationHandlers) Register(c echo.Context) error {
ctx := c.Request().Context()
binder, err := h.createBinder(c)
if err != nil {
return err
}
req := &tailcfg.RegisterRequest{}
if err := binder.BindRequest(c, req); err != nil {
return err
}
machineKey := binder.Peer().String()
nodeKey := req.NodeKey.String()
var m *domain.Machine
m, err = h.repository.GetMachineByKeys(ctx, machineKey, nodeKey)
if err != nil {
return err
}
if m != nil {
if m.ExpiresAt != nil && !m.ExpiresAt.IsZero() && m.ExpiresAt.Before(time.Now()) {
response := tailcfg.RegisterResponse{NodeKeyExpired: true}
return binder.WriteResponse(c, http.StatusOK, response)
}
if !req.Expiry.IsZero() && req.Expiry.Before(time.Now()) {
m.ExpiresAt = &req.Expiry
if err := h.repository.SaveMachine(ctx, m); err != nil {
return err
}
response := tailcfg.RegisterResponse{NodeKeyExpired: true}
return binder.WriteResponse(c, http.StatusOK, response)
}
sanitizeHostname := dnsname.SanitizeHostname(req.Hostinfo.Hostname)
if m.Name != sanitizeHostname {
nameIdx, err := h.repository.GetNextMachineNameIndex(ctx, m.TailnetID, sanitizeHostname)
if err != nil {
return err
}
m.Name = sanitizeHostname
m.NameIdx = nameIdx
}
advertisedTags := domain.SanitizeTags(req.Hostinfo.RequestTags)
m.Tags = append(m.RegisteredTags, advertisedTags...)
if err := h.repository.SaveMachine(ctx, m); err != nil {
return err
}
response := tailcfg.RegisterResponse{MachineAuthorized: true}
return binder.WriteResponse(c, http.StatusOK, response)
}
return h.authenticateMachine(c, binder, machineKey, req)
}
func (h *RegistrationHandlers) authenticateMachine(c echo.Context, binder bind.Binder, id string, req *tailcfg.RegisterRequest) error {
if req.Followup != "" {
response := tailcfg.RegisterResponse{AuthURL: req.Followup}
return binder.WriteResponse(c, http.StatusOK, response)
}
if req.Auth.AuthKey == "" {
key := util.RandStringBytes(8)
authUrl := h.config.CreateUrl("/a/%s", key)
h.pendingMachineRegistrationRequests.Set(key, &pendingMachineRegistrationRequest{
machineKey: id,
request: req,
}, cache.DefaultExpiration)
response := tailcfg.RegisterResponse{AuthURL: authUrl}
return binder.WriteResponse(c, http.StatusOK, response)
} else {
return h.authenticateMachineWithAuthKey(c, binder, id, req)
}
}
func (h *RegistrationHandlers) authenticateMachineWithAuthKey(c echo.Context, binder bind.Binder, machineKey string, req *tailcfg.RegisterRequest) error {
ctx := c.Request().Context()
nodeKey := req.NodeKey.String()
authKey, err := h.repository.LoadAuthKey(ctx, req.Auth.AuthKey)
if err != nil {
return err
}
if authKey == nil {
return c.String(http.StatusBadRequest, "invalid auth key")
}
tailnet := authKey.Tailnet
user := authKey.User
var m *domain.Machine
m, err = h.repository.GetMachineByKey(ctx, tailnet.ID, machineKey)
if err != nil {
return err
}
if m == nil {
now := time.Now().UTC()
registeredTags := authKey.Tags
advertisedTags := domain.SanitizeTags(req.Hostinfo.RequestTags)
tags := append(registeredTags, advertisedTags...)
sanitizeHostname := dnsname.SanitizeHostname(req.Hostinfo.Hostname)
nameIdx, err := h.repository.GetNextMachineNameIndex(ctx, tailnet.ID, sanitizeHostname)
if err != nil {
return err
}
m = &domain.Machine{
ID: util.NextID(),
Name: sanitizeHostname,
NameIdx: nameIdx,
MachineKey: machineKey,
NodeKey: nodeKey,
Ephemeral: authKey.Ephemeral,
RegisteredTags: registeredTags,
Tags: domain.SanitizeTags(tags),
CreatedAt: now,
User: user,
Tailnet: tailnet,
}
if !req.Expiry.IsZero() {
m.ExpiresAt = &req.Expiry
}
ipv4, ipv6, err := addr.SelectIP(checkIP(ctx, h.repository.CountMachinesWithIPv4))
if err != nil {
return err
}
m.IPv4 = ipv4.String()
m.IPv6 = ipv6.String()
} else {
registeredTags := authKey.Tags
advertisedTags := domain.SanitizeTags(req.Hostinfo.RequestTags)
tags := append(registeredTags, advertisedTags...)
sanitizeHostname := dnsname.SanitizeHostname(req.Hostinfo.Hostname)
if m.Name != sanitizeHostname {
nameIdx, err := h.repository.GetNextMachineNameIndex(ctx, tailnet.ID, sanitizeHostname)
if err != nil {
return err
}
m.Name = sanitizeHostname
m.NameIdx = nameIdx
}
m.NodeKey = nodeKey
m.Ephemeral = authKey.Ephemeral
m.RegisteredTags = registeredTags
m.Tags = domain.SanitizeTags(tags)
m.UserID = user.ID
m.User = user
m.TailnetID = tailnet.ID
m.Tailnet = tailnet
m.ExpiresAt = nil
}
if err := h.repository.SaveMachine(ctx, m); err != nil {
return err
}
response := tailcfg.RegisterResponse{MachineAuthorized: true}
return binder.WriteResponse(c, http.StatusOK, response)
}
func checkIP(cxt context.Context, s Selector) addr.Predicate {
return func(ip netaddr.IP) (bool, error) {
c, err := s(cxt, ip.String())
if err != nil {
return false, err
}
return c == 0, nil
}
}
type Selector func(ctx context.Context, ip string) (int64, error)
+16
View File
@@ -0,0 +1,16 @@
package handlers
import (
"github.com/jsiebens/ionscale/internal/version"
"github.com/labstack/echo/v4"
"net/http"
)
func Version(c echo.Context) error {
v, r := version.GetReleaseInfo()
resp := map[string]string{
"version": v,
"revision": r,
}
return c.JSON(http.StatusOK, resp)
}
+129
View File
@@ -0,0 +1,129 @@
package mapping
import (
"fmt"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/util"
"inet.af/netaddr"
"strconv"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/util/dnsname"
)
const NetworkMagicDNSSuffix = "ionscale.net"
func ToNode(m *domain.Machine, connected bool) (*tailcfg.Node, error) {
nKey, err := util.ParseNodePublicKey(m.NodeKey)
if err != nil {
return nil, err
}
mKey, err := util.ParseMachinePublicKey(m.MachineKey)
if err != nil {
return nil, err
}
var discoKey key.DiscoPublic
if m.DiscoKey != "" {
dKey, err := util.ParseDiscoPublicKey(m.DiscoKey)
if err != nil {
return nil, err
}
discoKey = *dKey
}
endpoints := m.Endpoints
hostinfo := tailcfg.Hostinfo(m.HostInfo)
var addrs []netaddr.IPPrefix
var allowedIPs []netaddr.IPPrefix
if m.IPv4 != "" {
ipv4, err := netaddr.ParseIPPrefix(fmt.Sprintf("%s/32", m.IPv4))
if err != nil {
return nil, err
}
addrs = append(addrs, ipv4)
allowedIPs = append(allowedIPs, ipv4)
}
if m.IPv6 != "" {
ipv6, err := netaddr.ParseIPPrefix(fmt.Sprintf("%s/128", m.IPv6))
if err != nil {
return nil, err
}
addrs = append(addrs, ipv6)
allowedIPs = append(allowedIPs, ipv6)
}
var derp string
if hostinfo.NetInfo != nil {
derp = fmt.Sprintf("127.3.3.40:%d", hostinfo.NetInfo.PreferredDERP)
} else {
derp = "127.3.3.40:0"
}
var name = m.Name
if m.NameIdx != 0 {
name = fmt.Sprintf("%s-%d", m.Name, m.NameIdx)
}
sanitizedTailnetName := dnsname.SanitizeHostname(m.Tailnet.Name)
hostInfo := tailcfg.Hostinfo{
OS: hostinfo.OS,
Hostname: hostinfo.Hostname,
Services: hostinfo.Services,
}
n := tailcfg.Node{
ID: tailcfg.NodeID(m.ID),
StableID: tailcfg.StableNodeID(strconv.FormatUint(m.ID, 10)),
Name: fmt.Sprintf("%s.%s.%s.", name, sanitizedTailnetName, NetworkMagicDNSSuffix),
Key: *nKey,
Machine: *mKey,
DiscoKey: discoKey,
Addresses: addrs,
AllowedIPs: allowedIPs,
Endpoints: endpoints,
DERP: derp,
Hostinfo: hostInfo.View(),
Created: m.CreatedAt.UTC(),
MachineAuthorized: true,
User: tailcfg.UserID(m.UserID),
}
if m.ExpiresAt != nil {
e := m.ExpiresAt.UTC()
n.KeyExpiry = e
}
n.Online = &connected
if !connected && m.LastSeen != nil {
l := m.LastSeen.UTC()
n.LastSeen = &l
}
return &n, nil
}
func ToUserProfile(u domain.User) tailcfg.UserProfile {
profile := tailcfg.UserProfile{
ID: tailcfg.UserID(u.ID),
LoginName: u.Name,
DisplayName: u.Name,
}
return profile
}
func ToUserProfiles(users domain.Users) []tailcfg.UserProfile {
var profiles []tailcfg.UserProfile
for _, u := range users {
profiles = append(profiles, ToUserProfile(u))
}
return profiles
}
+58
View File
@@ -0,0 +1,58 @@
package mux
import (
"crypto/tls"
"github.com/jsiebens/ionscale/internal/config"
"github.com/soheilhy/cmux"
"golang.org/x/sync/errgroup"
"google.golang.org/grpc"
"net"
"net/http"
)
func Serve(grpcServer *grpc.Server, appHandler http.Handler, metricsHandler http.Handler, config *config.Config) error {
appL, err := appListener(config)
if err != nil {
return err
}
metricsL, err := metricsListener(config)
if err != nil {
return err
}
mux := cmux.New(appL)
grpcL := mux.MatchWithWriters(
cmux.HTTP2MatchHeaderFieldPrefixSendSettings("content-type", "application/grpc"),
cmux.HTTP2MatchHeaderFieldPrefixSendSettings("content-type", "application/grpc+proto"),
)
httpL := mux.Match(cmux.Any())
g := new(errgroup.Group)
g.Go(func() error { return grpcServer.Serve(grpcL) })
g.Go(func() error { return http.Serve(httpL, appHandler) })
g.Go(func() error { return http.Serve(metricsL, metricsHandler) })
g.Go(func() error { return mux.Serve() })
return g.Wait()
}
func metricsListener(config *config.Config) (net.Listener, error) {
return net.Listen("tcp", config.Metrics.ListenAddr)
}
func appListener(config *config.Config) (net.Listener, error) {
if config.Tls.Disable {
return net.Listen("tcp", config.ListenAddr)
} else {
cer, err := tls.LoadX509KeyPair(config.Tls.CertFile, config.Tls.KeyFile)
if err != nil {
return nil, err
}
tlsConfig := &tls.Config{Certificates: []tls.Certificate{cer}}
return tls.Listen("tcp", config.ListenAddr, tlsConfig)
}
}
+57
View File
@@ -0,0 +1,57 @@
package server
import (
"fmt"
"github.com/hashicorp/go-hclog"
"github.com/labstack/echo/v4"
"runtime"
"time"
)
func EchoLogger(logger hclog.Logger) echo.MiddlewareFunc {
httpLogger := logger.Named("http")
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) (err error) {
if !httpLogger.IsTrace() {
return next(c)
}
request := c.Request()
response := c.Response()
start := time.Now()
if err = next(c); err != nil {
c.Error(err)
}
httpLogger.Trace("finished server http call",
"http.code", response.Status,
"http.method", request.Method,
"http.uri", request.RequestURI,
"http.start_time", start.Format(time.RFC3339),
"http.duration", time.Since(start))
return
}
}
}
func EchoRecover(logger hclog.Logger) echo.MiddlewareFunc {
httpLogger := logger.Named("http")
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
defer func() {
if r := recover(); r != nil {
err, ok := r.(error)
if !ok {
err = fmt.Errorf("%v", r)
}
stack := make([]byte, 4<<10) // 4 KB
length := runtime.Stack(stack, false)
httpLogger.Error("panic handling request", "err", err, "stack", string(stack[:length]))
c.Error(err)
}
}()
return next(c)
}
}
}
+54
View File
@@ -0,0 +1,54 @@
package server
import (
"github.com/grpc-ecosystem/go-grpc-middleware/v2"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery"
"github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/hashicorp/go-hclog"
"github.com/jsiebens/ionscale/internal/service"
"google.golang.org/grpc"
"tailscale.com/types/key"
)
func init() {
grpc_prometheus.EnableHandlingTimeHistogram()
}
func NewGrpcServer(logger hclog.Logger, systemAdminKey key.MachinePrivate) *grpc.Server {
return grpc.NewServer(
middleware.WithUnaryServerChain(
logging.UnaryServerInterceptor(
&grpcLogger{logger.Named("grpc")},
logging.WithDurationField(logging.DurationToDurationField),
),
grpc_prometheus.UnaryServerInterceptor,
recovery.UnaryServerInterceptor(),
service.UnaryServerTokenAuth(systemAdminKey),
),
)
}
type grpcLogger struct {
log hclog.Logger
}
func (l *grpcLogger) Log(lvl logging.Level, msg string) {
switch lvl {
case logging.ERROR:
l.log.Error(msg)
default:
l.log.Debug(msg)
}
}
func (l *grpcLogger) With(fields ...string) logging.Logger {
if len(fields) == 0 {
return l
}
vals := make([]interface{}, 0, len(fields))
for i := 0; i < len(fields); i++ {
vals = append(vals, fields[i])
}
return &grpcLogger{log: l.log.With(vals...)}
}
+141
View File
@@ -0,0 +1,141 @@
package server
import (
"github.com/hashicorp/go-hclog"
"github.com/jsiebens/ionscale/internal/bind"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/config"
"github.com/jsiebens/ionscale/internal/database"
"github.com/jsiebens/ionscale/internal/handlers"
"github.com/jsiebens/ionscale/internal/mux"
"github.com/jsiebens/ionscale/internal/service"
"github.com/jsiebens/ionscale/internal/templates"
"github.com/jsiebens/ionscale/pkg/gen/api"
echo_prometheus "github.com/labstack/echo-contrib/prometheus"
"github.com/labstack/echo/v4"
"github.com/patrickmn/go-cache"
"log"
"net/http"
"os"
"strings"
"tailscale.com/types/key"
"time"
)
func Start(config *config.Config) error {
logger, err := setupLogging(config.Logging)
if err != nil {
return err
}
logger.Info("Starting ionscale server")
_, repository, err := database.OpenDB(&config.Database, logger)
if err != nil {
return err
}
serverKey, err := config.ReadServerKeys()
if err != nil {
return err
}
pendingMachineRegistrationRequests := cache.New(5*time.Minute, 10*time.Minute)
brokers := broker.NewBrokerPool()
offlineTimers := handlers.NewOfflineTimers(repository, brokers)
reaper := handlers.NewReaper(brokers, repository)
go offlineTimers.Start()
go reaper.Start()
createPeerHandler := func(p key.MachinePublic) http.Handler {
registrationHandlers := handlers.NewRegistrationHandlers(bind.DefaultBinder(p), config, repository, pendingMachineRegistrationRequests)
pollNetMapHandler := handlers.NewPollNetMapHandler(bind.DefaultBinder(p), brokers, repository, offlineTimers)
e := echo.New()
e.Use(EchoLogger(logger))
e.Use(EchoRecover(logger))
e.POST("/machine/register", registrationHandlers.Register)
e.POST("/machine/map", pollNetMapHandler.PollNetMap)
return e
}
noiseHandlers := handlers.NewNoiseHandlers(serverKey.ControlKey, createPeerHandler)
registrationHandlers := handlers.NewRegistrationHandlers(bind.BoxBinder(serverKey.LegacyControlKey), config, repository, pendingMachineRegistrationRequests)
pollNetMapHandler := handlers.NewPollNetMapHandler(bind.BoxBinder(serverKey.LegacyControlKey), brokers, repository, offlineTimers)
authenticationHandlers := handlers.NewAuthenticationHandlers(
config,
repository,
pendingMachineRegistrationRequests,
)
p := echo_prometheus.NewPrometheus("http", nil)
e := echo.New()
e.Renderer = templates.NewTemplates()
e.Use(EchoRecover(logger))
e.Use(EchoLogger(logger))
e.Use(p.HandlerFunc)
m := echo.New()
p.SetMetricsPath(m)
e.Any("/*", handlers.IndexHandler(http.StatusNotFound))
e.Any("/", handlers.IndexHandler(http.StatusOK))
e.GET("/version", handlers.Version)
e.GET("/key", handlers.KeyHandler(serverKey))
e.POST("/ts2021", noiseHandlers.Upgrade)
e.POST("/machine/:id", registrationHandlers.Register)
e.POST("/machine/:id/map", pollNetMapHandler.PollNetMap)
auth := e.Group("/a")
auth.GET("/:key", authenticationHandlers.StartAuth)
auth.POST("/:key", authenticationHandlers.StartAuth)
auth.GET("/success", authenticationHandlers.Success)
auth.GET("/error", authenticationHandlers.Error)
grpcService := service.NewService(repository, brokers)
grpcServer := NewGrpcServer(logger, serverKey.SystemAdminKey)
api.RegisterIonscaleServer(grpcServer, grpcService)
if config.Tls.Disable {
logger.Warn("TLS is disabled")
} else {
logger.Info("TLS is enabled", "cert", config.Tls.CertFile)
}
logger.Info("Server is running", "addr", config.ListenAddr, "metrics", config.Metrics.ListenAddr)
return mux.Serve(grpcServer, e, m, config)
}
func setupLogging(config config.Logging) (hclog.Logger, error) {
file, err := createLogFile(config)
if err != nil {
return nil, err
}
appLogger := hclog.New(&hclog.LoggerOptions{
Name: "ionscale",
Level: hclog.LevelFromString(config.Level),
JSONFormat: strings.ToLower(config.Format) == "json",
Output: file,
})
log.SetOutput(appLogger.StandardWriter(&hclog.StandardLoggerOptions{InferLevels: true}))
log.SetPrefix("")
log.SetFlags(0)
return appLogger, nil
}
func createLogFile(config config.Logging) (*os.File, error) {
if config.File != "" {
f, err := os.OpenFile(config.File, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
return nil, err
}
return f, nil
}
return os.Stdout, nil
}
+111
View File
@@ -0,0 +1,111 @@
package service
import (
"context"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/pkg/gen/api"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
"time"
)
func (s *Service) ListAuthKeys(ctx context.Context, req *api.ListAuthKeysRequest) (*api.ListAuthKeysResponse, error) {
tailnet, err := s.repository.GetTailnet(ctx, req.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, status.Error(codes.NotFound, "")
}
authKeys, err := s.repository.ListAuthKeys(ctx, req.TailnetId)
if err != nil {
return nil, err
}
response := api.ListAuthKeysResponse{}
for _, key := range authKeys {
var expiresAt *timestamppb.Timestamp
if key.ExpiresAt != nil {
expiresAt = timestamppb.New(*key.ExpiresAt)
}
response.AuthKeys = append(response.AuthKeys, &api.AuthKey{
Id: key.ID,
Key: key.Key,
Ephemeral: key.Ephemeral,
CreatedAt: timestamppb.New(key.CreatedAt),
ExpiresAt: expiresAt,
Tailnet: &api.Ref{
Id: tailnet.ID,
Name: tailnet.Name,
},
})
}
return &response, nil
}
func (s *Service) CreateAuthKey(ctx context.Context, req *api.CreateAuthKeyRequest) (*api.CreateAuthKeyResponse, error) {
if len(req.Tags) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "at least one tag is required when creating an auth key")
}
tailnet, err := s.repository.GetTailnet(ctx, req.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, status.Error(codes.NotFound, "")
}
var expiresAt *time.Time
var expiresAtPb *timestamppb.Timestamp
if req.Expiry != nil {
duration := req.Expiry.AsDuration()
e := time.Now().UTC().Add(duration)
expiresAt = &e
expiresAtPb = timestamppb.New(*expiresAt)
}
user, _, err := s.repository.GetOrCreateServiceUser(ctx, tailnet)
if err != nil {
return nil, err
}
tags := domain.SanitizeTags(req.Tags)
v, authKey := domain.CreateAuthKey(tailnet, user, req.Ephemeral, tags, expiresAt)
if err := s.repository.SaveAuthKey(ctx, authKey); err != nil {
return nil, err
}
response := api.CreateAuthKeyResponse{
Value: v,
AuthKey: &api.AuthKey{
Id: authKey.ID,
Key: authKey.Key,
Ephemeral: authKey.Ephemeral,
CreatedAt: timestamppb.New(authKey.CreatedAt),
ExpiresAt: expiresAtPb,
Tailnet: &api.Ref{
Id: tailnet.ID,
Name: tailnet.Name,
},
}}
return &response, nil
}
func (s *Service) DeleteAuthKey(ctx context.Context, req *api.DeleteAuthKeyRequest) (*api.DeleteAuthKeyResponse, error) {
if _, err := s.repository.DeleteAuthKey(ctx, req.AuthKeyId); err != nil {
return nil, err
}
return &api.DeleteAuthKeyResponse{}, nil
}
+76
View File
@@ -0,0 +1,76 @@
package service
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/pkg/gen/api"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
)
func (s *Service) ListMachines(ctx context.Context, req *api.ListMachinesRequest) (*api.ListMachinesResponse, error) {
tailnet, err := s.repository.GetTailnet(ctx, req.TailnetId)
if err != nil {
return nil, err
}
if tailnet == nil {
return nil, status.Error(codes.NotFound, "tailnet does not exist")
}
machines, err := s.repository.ListMachineByTailnet(ctx, tailnet.ID)
if err != nil {
return nil, err
}
response := &api.ListMachinesResponse{}
for _, m := range machines {
var name = m.Name
if m.NameIdx != 0 {
name = fmt.Sprintf("%s-%d", m.Name, m.NameIdx)
}
online := s.brokers(m.TailnetID).IsConnected(m.ID)
var lastSeen *timestamppb.Timestamp
if m.LastSeen != nil {
lastSeen = timestamppb.New(*m.LastSeen)
}
response.Machines = append(response.Machines, &api.Machine{
Id: m.ID,
Name: name,
Ipv4: m.IPv4,
Ipv6: m.IPv6,
Ephemeral: m.Ephemeral,
LastSeen: lastSeen,
Connected: online,
Tailnet: &api.Ref{
Id: m.Tailnet.ID,
Name: m.Tailnet.Name,
},
User: &api.Ref{
Id: m.User.ID,
Name: m.User.Name,
},
})
}
return response, nil
}
func (s *Service) DeleteMachine(ctx context.Context, req *api.DeleteMachineRequest) (*api.DeleteMachineResponse, error) {
m, err := s.repository.GetMachine(ctx, req.MachineId)
if err != nil {
return nil, err
}
if m == nil {
return nil, status.Error(codes.NotFound, "machine does not exist")
}
if _, err := s.repository.DeleteMachine(ctx, req.MachineId); err != nil {
return nil, err
}
s.brokers(m.TailnetID).SignalPeersRemoved([]uint64{m.ID})
return &api.DeleteMachineResponse{}, nil
}
+84
View File
@@ -0,0 +1,84 @@
package service
import (
"context"
"github.com/jsiebens/ionscale/internal/broker"
"github.com/jsiebens/ionscale/internal/domain"
"github.com/jsiebens/ionscale/internal/token"
"github.com/jsiebens/ionscale/internal/version"
"github.com/jsiebens/ionscale/pkg/gen/api"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"strings"
"tailscale.com/types/key"
)
var (
errMissingMetadata = status.Error(codes.InvalidArgument, "missing metadata")
errInvalidToken = status.Error(codes.Unauthenticated, "invalid token")
)
func NewService(repository domain.Repository, brokerPool *broker.BrokerPool) *Service {
return &Service{
repository: repository,
brokerPool: brokerPool,
}
}
type Service struct {
repository domain.Repository
brokerPool *broker.BrokerPool
}
func (s *Service) brokers(tailnetID uint64) broker.Broker {
return s.brokerPool.Get(tailnetID)
}
func (s *Service) GetVersion(ctx context.Context, req *api.GetVersionRequest) (*api.GetVersionResponse, error) {
v, revision := version.GetReleaseInfo()
return &api.GetVersionResponse{
Version: v,
Revision: revision,
}, nil
}
func UnaryServerTokenAuth(systemAdminKey key.MachinePrivate) func(context.Context, interface{}, *grpc.UnaryServerInfo, grpc.UnaryHandler) (interface{}, error) {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if strings.HasSuffix(info.FullMethod, "/GetVersion") {
return handler(ctx, req)
}
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, errMissingMetadata
}
// The keys within metadata.MD are normalized to lowercase.
// See: https://godoc.org/google.golang.org/grpc/metadata#New
valid := validateAuthorizationToken(systemAdminKey, md["authorization"])
if valid {
return handler(ctx, req)
}
return nil, errInvalidToken
}
}
func validateAuthorizationToken(systemAdminKey key.MachinePrivate, authorization []string) bool {
if len(authorization) != 1 {
return false
}
bearerToken := strings.TrimPrefix(authorization[0], "Bearer ")
if token.IsSystemAdminToken(bearerToken) {
_, err := token.ParseSystemAdminToken(systemAdminKey, bearerToken)
return err == nil
}
return false
}
+39
View File
@@ -0,0 +1,39 @@
package service
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/pkg/gen/api"
)
func (s *Service) CreateTailnet(ctx context.Context, req *api.CreateTailnetRequest) (*api.CreateTailnetResponse, error) {
tailnet, created, err := s.repository.GetOrCreateTailnet(ctx, req.Name)
if err != nil {
return nil, err
}
if !created {
return nil, fmt.Errorf("tailnet already exists")
}
resp := &api.CreateTailnetResponse{Tailnet: &api.Tailnet{
Id: tailnet.ID,
Name: tailnet.Name,
}}
return resp, nil
}
func (s *Service) ListTailnets(ctx context.Context, _ *api.ListTailnetRequest) (*api.ListTailnetResponse, error) {
resp := &api.ListTailnetResponse{}
tailnets, err := s.repository.ListTailnets(ctx)
if err != nil {
return nil, err
}
for _, t := range tailnets {
gt := api.Tailnet{Id: t.ID, Name: t.Name}
resp.Tailnet = append(resp.Tailnet, &gt)
}
return resp, nil
}
+89
View File
@@ -0,0 +1,89 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body {
width: 100%;
height: 100vh;
padding: 10px;
background: #379683;
}
.wrapper {
background: #fff;
max-width: 400px;
width: 100%;
margin: 120px auto;
padding: 25px;
border-radius: 5px;
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
}
.selectionList li {
position: relative;
list-style: none;
height: 45px;
line-height: 45px;
margin-bottom: 8px;
background: #f2f2f2;
border-radius: 3px;
overflow: hidden;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
}
.selectionList {
padding-top: 5px
}
.selectionList li button {
margin: 0;
display: block;
width: 100%;
height: 100%;
border: none;
}
input {
display: block;
width: 100%;
height: 100%;
padding: 10px;
}
button {
padding-top: 10px;
padding-bottom: 10px;
padding-left: 20px;
padding-right: 20px;
height: 45px;
border: none;
}
</style>
<title>ionscale</title>
</head>
<body>
<div class="wrapper">
<div style="text-align: left; padding-bottom: 10px">
<p><b>Authentication required</b></p>
<small>Enter an <label for="ak">auth key</label> here:</small>
</div>
<form method="post" style="text-align: right">
<p><input id="ak" name="ak" type="text"/></p>
<div style="padding-top: 10px">
<button type="submit">submit</button>
</div>
</form>
</div>
</body>
</html>
+62
View File
@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body {
width: 100%;
height: 100vh;
padding: 10px;
background: #379683;
}
.wrapper {
background: #fff;
max-width: 400px;
width: 100%;
margin: 120px auto;
padding: 25px;
border-radius: 5px;
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
}
.selectionList li {
position: relative;
list-style: none;
height: 45px;
line-height: 45px;
margin-bottom: 8px;
background: #f2f2f2;
border-radius: 3px;
overflow: hidden;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
}
.selectionList li button {
margin: 0;
display: block;
width: 100%;
height: 100%;
border: none;
}
</style>
<title>ionscale</title>
</head>
<body>
<div class="wrapper">
<div style="text-align: center">
<p><b>An error occurred</b></p>
</div>
</div>
</body>
</html>
+63
View File
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body {
width: 100%;
height: 100vh;
padding: 10px;
background: #379683;
}
.wrapper {
background: #fff;
max-width: 400px;
width: 100%;
margin: 120px auto;
padding: 25px;
border-radius: 5px;
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
}
.selectionList li {
position: relative;
list-style: none;
height: 45px;
line-height: 45px;
margin-bottom: 8px;
background: #f2f2f2;
border-radius: 3px;
overflow: hidden;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
}
.selectionList li button {
margin: 0;
display: block;
width: 100%;
height: 100%;
border: none;
}
</style>
<title>ionscale</title>
</head>
<body>
<div class="wrapper">
<div style="text-align: center">
<p><b>ionscale</b></p>
<small>{{.Version}}</small>
</div>
</div>
</body>
</html>
+63
View File
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body {
width: 100%;
height: 100vh;
padding: 10px;
background: #379683;
}
.wrapper {
background: #fff;
max-width: 400px;
width: 100%;
margin: 120px auto;
padding: 25px;
border-radius: 5px;
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
}
.selectionList li {
position: relative;
list-style: none;
height: 45px;
line-height: 45px;
margin-bottom: 8px;
background: #f2f2f2;
border-radius: 3px;
overflow: hidden;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
}
.selectionList li button {
margin: 0;
display: block;
width: 100%;
height: 100%;
border: none;
}
</style>
<title>ionscale</title>
</head>
<body>
<div class="wrapper">
<div style="text-align: center">
<p><b>Authorization failed</b></p>
<small>the provided auth key is <b style="color: red">invalid</b></small>
</div>
</div>
</body>
</html>
+63
View File
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400;500;600;700&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body {
width: 100%;
height: 100vh;
padding: 10px;
background: #379683;
}
.wrapper {
background: #fff;
max-width: 400px;
width: 100%;
margin: 120px auto;
padding: 25px;
border-radius: 5px;
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1);
}
.selectionList li {
position: relative;
list-style: none;
height: 45px;
line-height: 45px;
margin-bottom: 8px;
background: #f2f2f2;
border-radius: 3px;
overflow: hidden;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.1);
}
.selectionList li button {
margin: 0;
display: block;
width: 100%;
height: 100%;
border: none;
}
</style>
<title>ionscale</title>
</head>
<body>
<div class="wrapper">
<div style="text-align: center">
<p><b>Authorization successful</b></p>
<small>You can now close this window</small>
</div>
</div>
</body>
</html>
+23
View File
@@ -0,0 +1,23 @@
package templates
import (
"embed"
"github.com/labstack/echo/v4"
"html/template"
"io"
)
//go:embed *.html
var fs embed.FS
func NewTemplates() *Template {
return &Template{templates: template.Must(template.ParseFS(fs, "*.html"))}
}
type Template struct {
templates *template.Template
}
func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
return t.templates.ExecuteTemplate(w, name, data)
}
+124
View File
@@ -0,0 +1,124 @@
package token
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"github.com/jsiebens/ionscale/internal/util"
"github.com/mr-tron/base58"
"strings"
"tailscale.com/types/key"
"time"
)
const (
nonceLength = 16
systemAdminTokenPrefix = "st_"
)
var driftCompensation = time.Minute
type Info struct {
Nonce string `json:"nonce"`
NonceBytes []byte `json:"-"`
CreationTime time.Time `json:"creation_time"`
}
func IsSystemAdminToken(token string) bool {
return strings.HasPrefix(token, systemAdminTokenPrefix)
}
func ParseSystemAdminToken(privKey key.MachinePrivate, versionedToken string) (*Info, error) {
versionedToken = strings.TrimSpace(versionedToken)
if versionedToken == "" {
return nil, errors.New("empty token")
}
if !strings.HasPrefix(versionedToken, systemAdminTokenPrefix) {
return nil, errors.New("token has wrong format")
}
token := strings.TrimPrefix(versionedToken, systemAdminTokenPrefix)
marshaledBlob, err := base58.FastBase58Decoding(token)
if err != nil {
return nil, fmt.Errorf("error base58-decoding token: %w", err)
}
if len(marshaledBlob) == 0 {
return nil, fmt.Errorf("length zero after base58-decoding token")
}
info := new(Info)
if err := unmarshal(marshaledBlob, info, privKey.Public(), privKey); err != nil {
return nil, fmt.Errorf("error unmarshaling token info: %w", err)
}
info.NonceBytes, err = base64.RawStdEncoding.DecodeString(info.Nonce)
if err != nil {
return nil, fmt.Errorf("error decoding nonce bytes: %w", err)
}
if len(info.NonceBytes) != nonceLength {
return nil, errors.New("nonce has incorrect length, must be 32 bytes")
}
if info.CreationTime.IsZero() {
return nil, errors.New("token creation time is zero")
}
if info.CreationTime.After(time.Now().Add(driftCompensation)) {
return nil, errors.New("token creation time is invalid")
}
if info.CreationTime.Before(time.Now().Add(-driftCompensation)) {
return nil, errors.New("token creation time is expired")
}
return info, nil
}
func GenerateSystemAdminToken(privKey key.MachinePrivate) (string, error) {
b, err := util.RandomBytes(nonceLength)
if err != nil {
return "", fmt.Errorf("error generating random bytes for token nonce: %w", err)
}
info := &Info{
Nonce: base64.RawStdEncoding.EncodeToString(b),
CreationTime: time.Now(),
}
return formatToken(privKey.Public(), privKey, systemAdminTokenPrefix, info)
}
func formatToken(pubKey key.MachinePublic, privKey key.MachinePrivate, prefix string, v interface{}) (string, error) {
blobInfo, err := marshal(v, pubKey, privKey)
if err != nil {
return "", fmt.Errorf("error encrypting info: %w", err)
}
encodedMarshaledBlob := base58.FastBase58Encoding(blobInfo)
return fmt.Sprintf("%s%s", prefix, encodedMarshaledBlob), nil
}
func marshal(v interface{}, pubKey key.MachinePublic, privKey key.MachinePrivate) ([]byte, error) {
b, err := json.Marshal(v)
if err != nil {
return nil, err
}
return privKey.SealTo(pubKey, b), nil
}
func unmarshal(msg []byte, v interface{}, publicKey key.MachinePublic, privateKey key.MachinePrivate) error {
decrypted, ok := privateKey.OpenFrom(publicKey, msg)
if !ok {
return fmt.Errorf("unable to decrypt payload")
}
if err := json.Unmarshal(decrypted, v); err != nil {
return err
}
return nil
}
+53
View File
@@ -0,0 +1,53 @@
package util
import (
"fmt"
"github.com/sony/sonyflake"
"net"
"os"
"strconv"
"time"
)
var sf *sonyflake.Sonyflake
func init() {
sf = sonyflake.NewSonyflake(sonyflake.Settings{
MachineID: machineID(),
StartTime: time.Date(2022, 05, 01, 00, 0, 0, 0, time.UTC),
})
if sf == nil {
panic("unable to initialize sonyflake")
}
}
func machineID() func() (uint16, error) {
envMachineID := os.Getenv("IONSCALE_MACHINE_ID")
if len(envMachineID) != 0 {
return func() (uint16, error) {
id, err := strconv.ParseInt(envMachineID, 0, 16)
if err != nil {
return 0, err
}
return uint16(id), nil
}
}
envMachineIP := os.Getenv("IONSCALE_MACHINE_IP")
if len(envMachineIP) != 0 {
return func() (uint16, error) {
ip := net.ParseIP(envMachineIP).To4()
if len(ip) < 4 {
return 0, fmt.Errorf("invalid IP")
}
return uint16(ip[2])<<8 + uint16(ip[3]), nil
}
}
return nil
}
func NextID() uint64 {
id, _ := sf.NextID()
return id
}
+69
View File
@@ -0,0 +1,69 @@
package util
import (
"strings"
"tailscale.com/types/key"
)
const (
discoPublicHexPrefix = "discokey:"
nodePublicHexPrefix = "nodekey:"
machinePublicHexPrefix = "mkey:"
privateHexPrefix = "privkey:"
)
func ParseMachinePrivateKey(machineKey string) (*key.MachinePrivate, error) {
if !strings.HasPrefix(machineKey, privateHexPrefix) {
machineKey = privateHexPrefix + machineKey
}
var mp key.MachinePrivate
if err := mp.UnmarshalText([]byte(machineKey)); err != nil {
return nil, err
}
return &mp, nil
}
func ParseMachinePublicKey(machineKey string) (*key.MachinePublic, error) {
if !strings.HasPrefix(machineKey, machinePublicHexPrefix) {
machineKey = machinePublicHexPrefix + machineKey
}
var mp key.MachinePublic
if err := mp.UnmarshalText([]byte(machineKey)); err != nil {
return nil, err
}
return &mp, nil
}
func ParseNodePublicKey(machineKey string) (*key.NodePublic, error) {
if !strings.HasPrefix(machineKey, nodePublicHexPrefix) {
machineKey = nodePublicHexPrefix + machineKey
}
var mp key.NodePublic
if err := mp.UnmarshalText([]byte(machineKey)); err != nil {
return nil, err
}
return &mp, nil
}
func ParseDiscoPublicKey(machineKey string) (*key.DiscoPublic, error) {
if !strings.HasPrefix(machineKey, discoPublicHexPrefix) {
machineKey = discoPublicHexPrefix + machineKey
}
var mp key.DiscoPublic
if err := mp.UnmarshalText([]byte(machineKey)); err != nil {
return nil, err
}
return &mp, nil
}
+36
View File
@@ -0,0 +1,36 @@
package util
import (
"math/rand"
"time"
)
var entropy *rand.Rand
func init() {
seed := time.Now().UnixNano()
source := rand.NewSource(seed)
entropy = rand.New(source)
}
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
func RandUint64(n uint64) uint64 {
return entropy.Uint64() % n
}
func RandomBytes(size int) ([]byte, error) {
buf := make([]byte, size)
if _, err := entropy.Read(buf); err != nil {
return nil, err
}
return buf, nil
}
+18
View File
@@ -0,0 +1,18 @@
package version
var (
Version string
Revision string
DevVersion = "dev"
)
func BuildVersion() string {
if len(Version) == 0 {
return DevVersion
}
return Version
}
func GetReleaseInfo() (string, string) {
return BuildVersion(), Revision
}
+73
View File
@@ -0,0 +1,73 @@
package ionscale
import (
"context"
"fmt"
"github.com/jsiebens/ionscale/internal/token"
"github.com/jsiebens/ionscale/internal/util"
"google.golang.org/grpc/credentials"
"tailscale.com/types/key"
)
func HasCredentials(systemAdminKey string) bool {
return systemAdminKey != ""
}
func LoadClientAuth(systemAdminKey string) (ClientAuth, error) {
if systemAdminKey != "" {
k, err := util.ParseMachinePrivateKey(systemAdminKey)
if err != nil {
return nil, fmt.Errorf("invalid system admin key")
}
return &systemAdminTokenAuth{key: *k}, nil
}
return &anonymous{}, nil
}
type ClientAuth interface {
credentials.PerRPCCredentials
}
type anonymous struct {
}
func (m *anonymous) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return nil, nil
}
func (m *anonymous) RequireTransportSecurity() bool {
return false
}
type systemAdminTokenAuth struct {
key key.MachinePrivate
}
func (m *systemAdminTokenAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
t, err := token.GenerateSystemAdminToken(m.key)
if err != nil {
return nil, err
}
return map[string]string{
"authorization": "Bearer " + t,
}, nil
}
func (m *systemAdminTokenAuth) RequireTransportSecurity() bool {
return false
}
type tokenAuth struct {
token string
}
func (m *tokenAuth) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
"authorization": "Bearer " + m.token,
}, nil
}
func (m *tokenAuth) RequireTransportSecurity() bool {
return false
}
+36
View File
@@ -0,0 +1,36 @@
package ionscale
import (
"crypto/tls"
"github.com/jsiebens/ionscale/pkg/gen/api"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
"io"
"net/url"
)
func NewClient(clientAuth ClientAuth, serverURL string, insecureSkipVerify bool) (api.IonscaleClient, io.Closer, error) {
parsedUrl, err := url.Parse(serverURL)
if err != nil {
return nil, nil, err
}
var targetAddr = parsedUrl.Host
if parsedUrl.Port() == "" {
targetAddr = targetAddr + ":443"
}
var transportCreds = credentials.NewTLS(&tls.Config{InsecureSkipVerify: insecureSkipVerify})
if parsedUrl.Scheme != "https" {
transportCreds = insecure.NewCredentials()
}
conn, err := grpc.Dial(targetAddr, grpc.WithPerRPCCredentials(clientAuth), grpc.WithTransportCredentials(transportCreds))
if err != nil {
return nil, nil, err
}
return api.NewIonscaleClient(conn), conn, nil
}
+637
View File
@@ -0,0 +1,637 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: api/auth_keys.proto
package api
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
durationpb "google.golang.org/protobuf/types/known/durationpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type CreateAuthKeyRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TailnetId uint64 `protobuf:"varint,1,opt,name=tailnet_id,json=tailnetId,proto3" json:"tailnet_id,omitempty"`
Ephemeral bool `protobuf:"varint,2,opt,name=ephemeral,proto3" json:"ephemeral,omitempty"`
Expiry *durationpb.Duration `protobuf:"bytes,3,opt,name=expiry,proto3,oneof" json:"expiry,omitempty"`
Tags []string `protobuf:"bytes,4,rep,name=tags,proto3" json:"tags,omitempty"`
}
func (x *CreateAuthKeyRequest) Reset() {
*x = CreateAuthKeyRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_keys_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateAuthKeyRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateAuthKeyRequest) ProtoMessage() {}
func (x *CreateAuthKeyRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_keys_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateAuthKeyRequest.ProtoReflect.Descriptor instead.
func (*CreateAuthKeyRequest) Descriptor() ([]byte, []int) {
return file_api_auth_keys_proto_rawDescGZIP(), []int{0}
}
func (x *CreateAuthKeyRequest) GetTailnetId() uint64 {
if x != nil {
return x.TailnetId
}
return 0
}
func (x *CreateAuthKeyRequest) GetEphemeral() bool {
if x != nil {
return x.Ephemeral
}
return false
}
func (x *CreateAuthKeyRequest) GetExpiry() *durationpb.Duration {
if x != nil {
return x.Expiry
}
return nil
}
func (x *CreateAuthKeyRequest) GetTags() []string {
if x != nil {
return x.Tags
}
return nil
}
type CreateAuthKeyResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AuthKey *AuthKey `protobuf:"bytes,1,opt,name=auth_key,json=authKey,proto3" json:"auth_key,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
}
func (x *CreateAuthKeyResponse) Reset() {
*x = CreateAuthKeyResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_keys_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateAuthKeyResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateAuthKeyResponse) ProtoMessage() {}
func (x *CreateAuthKeyResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_keys_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateAuthKeyResponse.ProtoReflect.Descriptor instead.
func (*CreateAuthKeyResponse) Descriptor() ([]byte, []int) {
return file_api_auth_keys_proto_rawDescGZIP(), []int{1}
}
func (x *CreateAuthKeyResponse) GetAuthKey() *AuthKey {
if x != nil {
return x.AuthKey
}
return nil
}
func (x *CreateAuthKeyResponse) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
type DeleteAuthKeyRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AuthKeyId uint64 `protobuf:"varint,1,opt,name=auth_key_id,json=authKeyId,proto3" json:"auth_key_id,omitempty"`
}
func (x *DeleteAuthKeyRequest) Reset() {
*x = DeleteAuthKeyRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_keys_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DeleteAuthKeyRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteAuthKeyRequest) ProtoMessage() {}
func (x *DeleteAuthKeyRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_keys_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteAuthKeyRequest.ProtoReflect.Descriptor instead.
func (*DeleteAuthKeyRequest) Descriptor() ([]byte, []int) {
return file_api_auth_keys_proto_rawDescGZIP(), []int{2}
}
func (x *DeleteAuthKeyRequest) GetAuthKeyId() uint64 {
if x != nil {
return x.AuthKeyId
}
return 0
}
type DeleteAuthKeyResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *DeleteAuthKeyResponse) Reset() {
*x = DeleteAuthKeyResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_keys_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DeleteAuthKeyResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteAuthKeyResponse) ProtoMessage() {}
func (x *DeleteAuthKeyResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_keys_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteAuthKeyResponse.ProtoReflect.Descriptor instead.
func (*DeleteAuthKeyResponse) Descriptor() ([]byte, []int) {
return file_api_auth_keys_proto_rawDescGZIP(), []int{3}
}
type ListAuthKeysRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TailnetId uint64 `protobuf:"varint,1,opt,name=tailnet_id,json=tailnetId,proto3" json:"tailnet_id,omitempty"`
}
func (x *ListAuthKeysRequest) Reset() {
*x = ListAuthKeysRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_keys_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListAuthKeysRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListAuthKeysRequest) ProtoMessage() {}
func (x *ListAuthKeysRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_keys_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListAuthKeysRequest.ProtoReflect.Descriptor instead.
func (*ListAuthKeysRequest) Descriptor() ([]byte, []int) {
return file_api_auth_keys_proto_rawDescGZIP(), []int{4}
}
func (x *ListAuthKeysRequest) GetTailnetId() uint64 {
if x != nil {
return x.TailnetId
}
return 0
}
type ListAuthKeysResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
AuthKeys []*AuthKey `protobuf:"bytes,1,rep,name=auth_keys,json=authKeys,proto3" json:"auth_keys,omitempty"`
}
func (x *ListAuthKeysResponse) Reset() {
*x = ListAuthKeysResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_keys_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListAuthKeysResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListAuthKeysResponse) ProtoMessage() {}
func (x *ListAuthKeysResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_keys_proto_msgTypes[5]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListAuthKeysResponse.ProtoReflect.Descriptor instead.
func (*ListAuthKeysResponse) Descriptor() ([]byte, []int) {
return file_api_auth_keys_proto_rawDescGZIP(), []int{5}
}
func (x *ListAuthKeysResponse) GetAuthKeys() []*AuthKey {
if x != nil {
return x.AuthKeys
}
return nil
}
type AuthKey struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Key string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
Ephemeral bool `protobuf:"varint,3,opt,name=ephemeral,proto3" json:"ephemeral,omitempty"`
Tags []string `protobuf:"bytes,4,rep,name=tags,proto3" json:"tags,omitempty"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=expires_at,json=expiresAt,proto3,oneof" json:"expires_at,omitempty"`
Tailnet *Ref `protobuf:"bytes,7,opt,name=tailnet,proto3" json:"tailnet,omitempty"`
}
func (x *AuthKey) Reset() {
*x = AuthKey{}
if protoimpl.UnsafeEnabled {
mi := &file_api_auth_keys_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *AuthKey) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*AuthKey) ProtoMessage() {}
func (x *AuthKey) ProtoReflect() protoreflect.Message {
mi := &file_api_auth_keys_proto_msgTypes[6]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use AuthKey.ProtoReflect.Descriptor instead.
func (*AuthKey) Descriptor() ([]byte, []int) {
return file_api_auth_keys_proto_rawDescGZIP(), []int{6}
}
func (x *AuthKey) GetId() uint64 {
if x != nil {
return x.Id
}
return 0
}
func (x *AuthKey) GetKey() string {
if x != nil {
return x.Key
}
return ""
}
func (x *AuthKey) GetEphemeral() bool {
if x != nil {
return x.Ephemeral
}
return false
}
func (x *AuthKey) GetTags() []string {
if x != nil {
return x.Tags
}
return nil
}
func (x *AuthKey) GetCreatedAt() *timestamppb.Timestamp {
if x != nil {
return x.CreatedAt
}
return nil
}
func (x *AuthKey) GetExpiresAt() *timestamppb.Timestamp {
if x != nil {
return x.ExpiresAt
}
return nil
}
func (x *AuthKey) GetTailnet() *Ref {
if x != nil {
return x.Tailnet
}
return nil
}
var File_api_auth_keys_proto protoreflect.FileDescriptor
var file_api_auth_keys_proto_rawDesc = []byte{
0x0a, 0x13, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65,
0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0d, 0x61, 0x70, 0x69,
0x2f, 0x72, 0x65, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xaa, 0x01, 0x0a, 0x14, 0x43,
0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x5f, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74,
0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x18,
0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c,
0x12, 0x36, 0x0a, 0x06, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x06, 0x65,
0x78, 0x70, 0x69, 0x72, 0x79, 0x88, 0x01, 0x01, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73,
0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x42, 0x09, 0x0a, 0x07,
0x5f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x79, 0x22, 0x56, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x27, 0x0a, 0x08, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79,
0x52, 0x07, 0x61, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22,
0x36, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x5f,
0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x61, 0x75,
0x74, 0x68, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74,
0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x34, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x69, 0x6c, 0x6e,
0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x61, 0x69,
0x6c, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x22, 0x41, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75,
0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29,
0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52,
0x08, 0x61, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x22, 0x8b, 0x02, 0x0a, 0x07, 0x41, 0x75,
0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x70, 0x68, 0x65, 0x6d,
0x65, 0x72, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x70, 0x68, 0x65,
0x6d, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20,
0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65,
0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x64, 0x41, 0x74, 0x12, 0x3e, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x5f,
0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x48, 0x00, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x41,
0x74, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x07, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x18,
0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x66, 0x52,
0x07, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x65, 0x78, 0x70,
0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75,
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x73, 0x69, 0x65, 0x62, 0x65, 0x6e, 0x73, 0x2f, 0x69,
0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x3b,
0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_api_auth_keys_proto_rawDescOnce sync.Once
file_api_auth_keys_proto_rawDescData = file_api_auth_keys_proto_rawDesc
)
func file_api_auth_keys_proto_rawDescGZIP() []byte {
file_api_auth_keys_proto_rawDescOnce.Do(func() {
file_api_auth_keys_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_auth_keys_proto_rawDescData)
})
return file_api_auth_keys_proto_rawDescData
}
var file_api_auth_keys_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_api_auth_keys_proto_goTypes = []interface{}{
(*CreateAuthKeyRequest)(nil), // 0: api.CreateAuthKeyRequest
(*CreateAuthKeyResponse)(nil), // 1: api.CreateAuthKeyResponse
(*DeleteAuthKeyRequest)(nil), // 2: api.DeleteAuthKeyRequest
(*DeleteAuthKeyResponse)(nil), // 3: api.DeleteAuthKeyResponse
(*ListAuthKeysRequest)(nil), // 4: api.ListAuthKeysRequest
(*ListAuthKeysResponse)(nil), // 5: api.ListAuthKeysResponse
(*AuthKey)(nil), // 6: api.AuthKey
(*durationpb.Duration)(nil), // 7: google.protobuf.Duration
(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
(*Ref)(nil), // 9: api.Ref
}
var file_api_auth_keys_proto_depIdxs = []int32{
7, // 0: api.CreateAuthKeyRequest.expiry:type_name -> google.protobuf.Duration
6, // 1: api.CreateAuthKeyResponse.auth_key:type_name -> api.AuthKey
6, // 2: api.ListAuthKeysResponse.auth_keys:type_name -> api.AuthKey
8, // 3: api.AuthKey.created_at:type_name -> google.protobuf.Timestamp
8, // 4: api.AuthKey.expires_at:type_name -> google.protobuf.Timestamp
9, // 5: api.AuthKey.tailnet:type_name -> api.Ref
6, // [6:6] is the sub-list for method output_type
6, // [6:6] is the sub-list for method input_type
6, // [6:6] is the sub-list for extension type_name
6, // [6:6] is the sub-list for extension extendee
0, // [0:6] is the sub-list for field type_name
}
func init() { file_api_auth_keys_proto_init() }
func file_api_auth_keys_proto_init() {
if File_api_auth_keys_proto != nil {
return
}
file_api_ref_proto_init()
if !protoimpl.UnsafeEnabled {
file_api_auth_keys_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateAuthKeyRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_keys_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateAuthKeyResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_keys_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DeleteAuthKeyRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_keys_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DeleteAuthKeyResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_keys_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListAuthKeysRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_keys_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListAuthKeysResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_auth_keys_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*AuthKey); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_api_auth_keys_proto_msgTypes[0].OneofWrappers = []interface{}{}
file_api_auth_keys_proto_msgTypes[6].OneofWrappers = []interface{}{}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_auth_keys_proto_rawDesc,
NumEnums: 0,
NumMessages: 7,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_api_auth_keys_proto_goTypes,
DependencyIndexes: file_api_auth_keys_proto_depIdxs,
MessageInfos: file_api_auth_keys_proto_msgTypes,
}.Build()
File_api_auth_keys_proto = out.File
file_api_auth_keys_proto_rawDesc = nil
file_api_auth_keys_proto_goTypes = nil
file_api_auth_keys_proto_depIdxs = nil
}
+148
View File
@@ -0,0 +1,148 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: api/ionscale.proto
package api
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
_ "google.golang.org/protobuf/types/known/durationpb"
_ "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
var File_api_ionscale_proto protoreflect.FileDescriptor
var file_api_ionscale_proto_rawDesc = []byte{
0x0a, 0x12, 0x61, 0x70, 0x69, 0x2f, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11, 0x61, 0x70, 0x69, 0x2f,
0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x12, 0x61,
0x70, 0x69, 0x2f, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x1a, 0x13, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6b, 0x65, 0x79, 0x73,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x12, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x61, 0x63, 0x68,
0x69, 0x6e, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xc6, 0x04, 0x0a, 0x08, 0x49,
0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x65,
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x12, 0x43, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65,
0x74, 0x73, 0x12, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x69,
0x6c, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43,
0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x48, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b,
0x65, 0x79, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41,
0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e,
0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65,
0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x45, 0x0a, 0x0c, 0x4c,
0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x18, 0x2e, 0x61, 0x70,
0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74,
0x41, 0x75, 0x74, 0x68, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x22, 0x00, 0x12, 0x45, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
0x65, 0x73, 0x12, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63,
0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0d, 0x44, 0x65, 0x6c,
0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65,
0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x00, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x6a, 0x73, 0x69, 0x65, 0x62, 0x65, 0x6e, 0x73, 0x2f, 0x69, 0x6f, 0x6e, 0x73, 0x63,
0x61, 0x6c, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x3b, 0x61, 0x70, 0x69, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var file_api_ionscale_proto_goTypes = []interface{}{
(*GetVersionRequest)(nil), // 0: api.GetVersionRequest
(*CreateTailnetRequest)(nil), // 1: api.CreateTailnetRequest
(*ListTailnetRequest)(nil), // 2: api.ListTailnetRequest
(*CreateAuthKeyRequest)(nil), // 3: api.CreateAuthKeyRequest
(*DeleteAuthKeyRequest)(nil), // 4: api.DeleteAuthKeyRequest
(*ListAuthKeysRequest)(nil), // 5: api.ListAuthKeysRequest
(*ListMachinesRequest)(nil), // 6: api.ListMachinesRequest
(*DeleteMachineRequest)(nil), // 7: api.DeleteMachineRequest
(*GetVersionResponse)(nil), // 8: api.GetVersionResponse
(*CreateTailnetResponse)(nil), // 9: api.CreateTailnetResponse
(*ListTailnetResponse)(nil), // 10: api.ListTailnetResponse
(*CreateAuthKeyResponse)(nil), // 11: api.CreateAuthKeyResponse
(*DeleteAuthKeyResponse)(nil), // 12: api.DeleteAuthKeyResponse
(*ListAuthKeysResponse)(nil), // 13: api.ListAuthKeysResponse
(*ListMachinesResponse)(nil), // 14: api.ListMachinesResponse
(*DeleteMachineResponse)(nil), // 15: api.DeleteMachineResponse
}
var file_api_ionscale_proto_depIdxs = []int32{
0, // 0: api.Ionscale.GetVersion:input_type -> api.GetVersionRequest
1, // 1: api.Ionscale.CreateTailnet:input_type -> api.CreateTailnetRequest
2, // 2: api.Ionscale.ListTailnets:input_type -> api.ListTailnetRequest
3, // 3: api.Ionscale.CreateAuthKey:input_type -> api.CreateAuthKeyRequest
4, // 4: api.Ionscale.DeleteAuthKey:input_type -> api.DeleteAuthKeyRequest
5, // 5: api.Ionscale.ListAuthKeys:input_type -> api.ListAuthKeysRequest
6, // 6: api.Ionscale.ListMachines:input_type -> api.ListMachinesRequest
7, // 7: api.Ionscale.DeleteMachine:input_type -> api.DeleteMachineRequest
8, // 8: api.Ionscale.GetVersion:output_type -> api.GetVersionResponse
9, // 9: api.Ionscale.CreateTailnet:output_type -> api.CreateTailnetResponse
10, // 10: api.Ionscale.ListTailnets:output_type -> api.ListTailnetResponse
11, // 11: api.Ionscale.CreateAuthKey:output_type -> api.CreateAuthKeyResponse
12, // 12: api.Ionscale.DeleteAuthKey:output_type -> api.DeleteAuthKeyResponse
13, // 13: api.Ionscale.ListAuthKeys:output_type -> api.ListAuthKeysResponse
14, // 14: api.Ionscale.ListMachines:output_type -> api.ListMachinesResponse
15, // 15: api.Ionscale.DeleteMachine:output_type -> api.DeleteMachineResponse
8, // [8:16] is the sub-list for method output_type
0, // [0:8] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_api_ionscale_proto_init() }
func file_api_ionscale_proto_init() {
if File_api_ionscale_proto != nil {
return
}
file_api_version_proto_init()
file_api_tailnets_proto_init()
file_api_auth_keys_proto_init()
file_api_machines_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_ionscale_proto_rawDesc,
NumEnums: 0,
NumMessages: 0,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_api_ionscale_proto_goTypes,
DependencyIndexes: file_api_ionscale_proto_depIdxs,
}.Build()
File_api_ionscale_proto = out.File
file_api_ionscale_proto_rawDesc = nil
file_api_ionscale_proto_goTypes = nil
file_api_ionscale_proto_depIdxs = nil
}
+351
View File
@@ -0,0 +1,351 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package api
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// IonscaleClient is the client API for Ionscale service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type IonscaleClient interface {
GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*GetVersionResponse, error)
CreateTailnet(ctx context.Context, in *CreateTailnetRequest, opts ...grpc.CallOption) (*CreateTailnetResponse, error)
ListTailnets(ctx context.Context, in *ListTailnetRequest, opts ...grpc.CallOption) (*ListTailnetResponse, error)
CreateAuthKey(ctx context.Context, in *CreateAuthKeyRequest, opts ...grpc.CallOption) (*CreateAuthKeyResponse, error)
DeleteAuthKey(ctx context.Context, in *DeleteAuthKeyRequest, opts ...grpc.CallOption) (*DeleteAuthKeyResponse, error)
ListAuthKeys(ctx context.Context, in *ListAuthKeysRequest, opts ...grpc.CallOption) (*ListAuthKeysResponse, error)
ListMachines(ctx context.Context, in *ListMachinesRequest, opts ...grpc.CallOption) (*ListMachinesResponse, error)
DeleteMachine(ctx context.Context, in *DeleteMachineRequest, opts ...grpc.CallOption) (*DeleteMachineResponse, error)
}
type ionscaleClient struct {
cc grpc.ClientConnInterface
}
func NewIonscaleClient(cc grpc.ClientConnInterface) IonscaleClient {
return &ionscaleClient{cc}
}
func (c *ionscaleClient) GetVersion(ctx context.Context, in *GetVersionRequest, opts ...grpc.CallOption) (*GetVersionResponse, error) {
out := new(GetVersionResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/GetVersion", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) CreateTailnet(ctx context.Context, in *CreateTailnetRequest, opts ...grpc.CallOption) (*CreateTailnetResponse, error) {
out := new(CreateTailnetResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/CreateTailnet", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) ListTailnets(ctx context.Context, in *ListTailnetRequest, opts ...grpc.CallOption) (*ListTailnetResponse, error) {
out := new(ListTailnetResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/ListTailnets", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) CreateAuthKey(ctx context.Context, in *CreateAuthKeyRequest, opts ...grpc.CallOption) (*CreateAuthKeyResponse, error) {
out := new(CreateAuthKeyResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/CreateAuthKey", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) DeleteAuthKey(ctx context.Context, in *DeleteAuthKeyRequest, opts ...grpc.CallOption) (*DeleteAuthKeyResponse, error) {
out := new(DeleteAuthKeyResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/DeleteAuthKey", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) ListAuthKeys(ctx context.Context, in *ListAuthKeysRequest, opts ...grpc.CallOption) (*ListAuthKeysResponse, error) {
out := new(ListAuthKeysResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/ListAuthKeys", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) ListMachines(ctx context.Context, in *ListMachinesRequest, opts ...grpc.CallOption) (*ListMachinesResponse, error) {
out := new(ListMachinesResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/ListMachines", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *ionscaleClient) DeleteMachine(ctx context.Context, in *DeleteMachineRequest, opts ...grpc.CallOption) (*DeleteMachineResponse, error) {
out := new(DeleteMachineResponse)
err := c.cc.Invoke(ctx, "/api.Ionscale/DeleteMachine", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// IonscaleServer is the server API for Ionscale service.
// All implementations should embed UnimplementedIonscaleServer
// for forward compatibility
type IonscaleServer interface {
GetVersion(context.Context, *GetVersionRequest) (*GetVersionResponse, error)
CreateTailnet(context.Context, *CreateTailnetRequest) (*CreateTailnetResponse, error)
ListTailnets(context.Context, *ListTailnetRequest) (*ListTailnetResponse, error)
CreateAuthKey(context.Context, *CreateAuthKeyRequest) (*CreateAuthKeyResponse, error)
DeleteAuthKey(context.Context, *DeleteAuthKeyRequest) (*DeleteAuthKeyResponse, error)
ListAuthKeys(context.Context, *ListAuthKeysRequest) (*ListAuthKeysResponse, error)
ListMachines(context.Context, *ListMachinesRequest) (*ListMachinesResponse, error)
DeleteMachine(context.Context, *DeleteMachineRequest) (*DeleteMachineResponse, error)
}
// UnimplementedIonscaleServer should be embedded to have forward compatible implementations.
type UnimplementedIonscaleServer struct {
}
func (UnimplementedIonscaleServer) GetVersion(context.Context, *GetVersionRequest) (*GetVersionResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetVersion not implemented")
}
func (UnimplementedIonscaleServer) CreateTailnet(context.Context, *CreateTailnetRequest) (*CreateTailnetResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateTailnet not implemented")
}
func (UnimplementedIonscaleServer) ListTailnets(context.Context, *ListTailnetRequest) (*ListTailnetResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListTailnets not implemented")
}
func (UnimplementedIonscaleServer) CreateAuthKey(context.Context, *CreateAuthKeyRequest) (*CreateAuthKeyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateAuthKey not implemented")
}
func (UnimplementedIonscaleServer) DeleteAuthKey(context.Context, *DeleteAuthKeyRequest) (*DeleteAuthKeyResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteAuthKey not implemented")
}
func (UnimplementedIonscaleServer) ListAuthKeys(context.Context, *ListAuthKeysRequest) (*ListAuthKeysResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListAuthKeys not implemented")
}
func (UnimplementedIonscaleServer) ListMachines(context.Context, *ListMachinesRequest) (*ListMachinesResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListMachines not implemented")
}
func (UnimplementedIonscaleServer) DeleteMachine(context.Context, *DeleteMachineRequest) (*DeleteMachineResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteMachine not implemented")
}
// UnsafeIonscaleServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to IonscaleServer will
// result in compilation errors.
type UnsafeIonscaleServer interface {
mustEmbedUnimplementedIonscaleServer()
}
func RegisterIonscaleServer(s grpc.ServiceRegistrar, srv IonscaleServer) {
s.RegisterService(&Ionscale_ServiceDesc, srv)
}
func _Ionscale_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetVersionRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).GetVersion(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/GetVersion",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).GetVersion(ctx, req.(*GetVersionRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_CreateTailnet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateTailnetRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).CreateTailnet(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/CreateTailnet",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).CreateTailnet(ctx, req.(*CreateTailnetRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_ListTailnets_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListTailnetRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).ListTailnets(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/ListTailnets",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).ListTailnets(ctx, req.(*ListTailnetRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_CreateAuthKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateAuthKeyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).CreateAuthKey(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/CreateAuthKey",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).CreateAuthKey(ctx, req.(*CreateAuthKeyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_DeleteAuthKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteAuthKeyRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).DeleteAuthKey(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/DeleteAuthKey",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).DeleteAuthKey(ctx, req.(*DeleteAuthKeyRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_ListAuthKeys_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListAuthKeysRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).ListAuthKeys(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/ListAuthKeys",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).ListAuthKeys(ctx, req.(*ListAuthKeysRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_ListMachines_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListMachinesRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).ListMachines(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/ListMachines",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).ListMachines(ctx, req.(*ListMachinesRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Ionscale_DeleteMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DeleteMachineRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IonscaleServer).DeleteMachine(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/api.Ionscale/DeleteMachine",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IonscaleServer).DeleteMachine(ctx, req.(*DeleteMachineRequest))
}
return interceptor(ctx, in, info, handler)
}
// Ionscale_ServiceDesc is the grpc.ServiceDesc for Ionscale service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Ionscale_ServiceDesc = grpc.ServiceDesc{
ServiceName: "api.Ionscale",
HandlerType: (*IonscaleServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetVersion",
Handler: _Ionscale_GetVersion_Handler,
},
{
MethodName: "CreateTailnet",
Handler: _Ionscale_CreateTailnet_Handler,
},
{
MethodName: "ListTailnets",
Handler: _Ionscale_ListTailnets_Handler,
},
{
MethodName: "CreateAuthKey",
Handler: _Ionscale_CreateAuthKey_Handler,
},
{
MethodName: "DeleteAuthKey",
Handler: _Ionscale_DeleteAuthKey_Handler,
},
{
MethodName: "ListAuthKeys",
Handler: _Ionscale_ListAuthKeys_Handler,
},
{
MethodName: "ListMachines",
Handler: _Ionscale_ListMachines_Handler,
},
{
MethodName: "DeleteMachine",
Handler: _Ionscale_DeleteMachine_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "api/ionscale.proto",
}
+480
View File
@@ -0,0 +1,480 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: api/machines.proto
package api
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
_ "google.golang.org/protobuf/types/known/durationpb"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ListMachinesRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
TailnetId uint64 `protobuf:"varint,1,opt,name=tailnet_id,json=tailnetId,proto3" json:"tailnet_id,omitempty"`
}
func (x *ListMachinesRequest) Reset() {
*x = ListMachinesRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_machines_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListMachinesRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListMachinesRequest) ProtoMessage() {}
func (x *ListMachinesRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_machines_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListMachinesRequest.ProtoReflect.Descriptor instead.
func (*ListMachinesRequest) Descriptor() ([]byte, []int) {
return file_api_machines_proto_rawDescGZIP(), []int{0}
}
func (x *ListMachinesRequest) GetTailnetId() uint64 {
if x != nil {
return x.TailnetId
}
return 0
}
type ListMachinesResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Machines []*Machine `protobuf:"bytes,1,rep,name=machines,proto3" json:"machines,omitempty"`
}
func (x *ListMachinesResponse) Reset() {
*x = ListMachinesResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_machines_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListMachinesResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListMachinesResponse) ProtoMessage() {}
func (x *ListMachinesResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_machines_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListMachinesResponse.ProtoReflect.Descriptor instead.
func (*ListMachinesResponse) Descriptor() ([]byte, []int) {
return file_api_machines_proto_rawDescGZIP(), []int{1}
}
func (x *ListMachinesResponse) GetMachines() []*Machine {
if x != nil {
return x.Machines
}
return nil
}
type DeleteMachineRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
MachineId uint64 `protobuf:"varint,1,opt,name=machine_id,json=machineId,proto3" json:"machine_id,omitempty"`
}
func (x *DeleteMachineRequest) Reset() {
*x = DeleteMachineRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_machines_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DeleteMachineRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteMachineRequest) ProtoMessage() {}
func (x *DeleteMachineRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_machines_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteMachineRequest.ProtoReflect.Descriptor instead.
func (*DeleteMachineRequest) Descriptor() ([]byte, []int) {
return file_api_machines_proto_rawDescGZIP(), []int{2}
}
func (x *DeleteMachineRequest) GetMachineId() uint64 {
if x != nil {
return x.MachineId
}
return 0
}
type DeleteMachineResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *DeleteMachineResponse) Reset() {
*x = DeleteMachineResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_machines_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *DeleteMachineResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*DeleteMachineResponse) ProtoMessage() {}
func (x *DeleteMachineResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_machines_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use DeleteMachineResponse.ProtoReflect.Descriptor instead.
func (*DeleteMachineResponse) Descriptor() ([]byte, []int) {
return file_api_machines_proto_rawDescGZIP(), []int{3}
}
type Machine struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Ipv4 string `protobuf:"bytes,3,opt,name=ipv4,proto3" json:"ipv4,omitempty"`
Ipv6 string `protobuf:"bytes,4,opt,name=ipv6,proto3" json:"ipv6,omitempty"`
Ephemeral bool `protobuf:"varint,5,opt,name=ephemeral,proto3" json:"ephemeral,omitempty"`
LastSeen *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"`
Connected bool `protobuf:"varint,7,opt,name=connected,proto3" json:"connected,omitempty"`
Tailnet *Ref `protobuf:"bytes,8,opt,name=tailnet,proto3" json:"tailnet,omitempty"`
User *Ref `protobuf:"bytes,9,opt,name=user,proto3" json:"user,omitempty"`
}
func (x *Machine) Reset() {
*x = Machine{}
if protoimpl.UnsafeEnabled {
mi := &file_api_machines_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Machine) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Machine) ProtoMessage() {}
func (x *Machine) ProtoReflect() protoreflect.Message {
mi := &file_api_machines_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Machine.ProtoReflect.Descriptor instead.
func (*Machine) Descriptor() ([]byte, []int) {
return file_api_machines_proto_rawDescGZIP(), []int{4}
}
func (x *Machine) GetId() uint64 {
if x != nil {
return x.Id
}
return 0
}
func (x *Machine) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *Machine) GetIpv4() string {
if x != nil {
return x.Ipv4
}
return ""
}
func (x *Machine) GetIpv6() string {
if x != nil {
return x.Ipv6
}
return ""
}
func (x *Machine) GetEphemeral() bool {
if x != nil {
return x.Ephemeral
}
return false
}
func (x *Machine) GetLastSeen() *timestamppb.Timestamp {
if x != nil {
return x.LastSeen
}
return nil
}
func (x *Machine) GetConnected() bool {
if x != nil {
return x.Connected
}
return false
}
func (x *Machine) GetTailnet() *Ref {
if x != nil {
return x.Tailnet
}
return nil
}
func (x *Machine) GetUser() *Ref {
if x != nil {
return x.User
}
return nil
}
var File_api_machines_proto protoreflect.FileDescriptor
var file_api_machines_proto_rawDesc = []byte{
0x0a, 0x12, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0d, 0x61, 0x70, 0x69, 0x2f,
0x72, 0x65, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x34, 0x0a, 0x13, 0x4c, 0x69, 0x73,
0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x22,
0x40, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69,
0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e,
0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
0x73, 0x22, 0x35, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69,
0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63,
0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d,
0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65,
0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x22, 0x8c, 0x02, 0x0a, 0x07, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x34, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x69, 0x70, 0x76, 0x34, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x70, 0x76, 0x36, 0x18, 0x04, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x70, 0x76, 0x36, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x70, 0x68,
0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x65, 0x70,
0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x12, 0x37, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f,
0x73, 0x65, 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e,
0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x07, 0x20,
0x01, 0x28, 0x08, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x22,
0x0a, 0x07, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x08, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x66, 0x52, 0x07, 0x74, 0x61, 0x69, 0x6c, 0x6e,
0x65, 0x74, 0x12, 0x1c, 0x0a, 0x04, 0x75, 0x73, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x08, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x66, 0x52, 0x04, 0x75, 0x73, 0x65, 0x72,
0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a,
0x73, 0x69, 0x65, 0x62, 0x65, 0x6e, 0x73, 0x2f, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65,
0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
file_api_machines_proto_rawDescOnce sync.Once
file_api_machines_proto_rawDescData = file_api_machines_proto_rawDesc
)
func file_api_machines_proto_rawDescGZIP() []byte {
file_api_machines_proto_rawDescOnce.Do(func() {
file_api_machines_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_machines_proto_rawDescData)
})
return file_api_machines_proto_rawDescData
}
var file_api_machines_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_api_machines_proto_goTypes = []interface{}{
(*ListMachinesRequest)(nil), // 0: api.ListMachinesRequest
(*ListMachinesResponse)(nil), // 1: api.ListMachinesResponse
(*DeleteMachineRequest)(nil), // 2: api.DeleteMachineRequest
(*DeleteMachineResponse)(nil), // 3: api.DeleteMachineResponse
(*Machine)(nil), // 4: api.Machine
(*timestamppb.Timestamp)(nil), // 5: google.protobuf.Timestamp
(*Ref)(nil), // 6: api.Ref
}
var file_api_machines_proto_depIdxs = []int32{
4, // 0: api.ListMachinesResponse.machines:type_name -> api.Machine
5, // 1: api.Machine.last_seen:type_name -> google.protobuf.Timestamp
6, // 2: api.Machine.tailnet:type_name -> api.Ref
6, // 3: api.Machine.user:type_name -> api.Ref
4, // [4:4] is the sub-list for method output_type
4, // [4:4] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
}
func init() { file_api_machines_proto_init() }
func file_api_machines_proto_init() {
if File_api_machines_proto != nil {
return
}
file_api_ref_proto_init()
if !protoimpl.UnsafeEnabled {
file_api_machines_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListMachinesRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_machines_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListMachinesResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_machines_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DeleteMachineRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_machines_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*DeleteMachineResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_machines_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Machine); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_machines_proto_rawDesc,
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_api_machines_proto_goTypes,
DependencyIndexes: file_api_machines_proto_depIdxs,
MessageInfos: file_api_machines_proto_msgTypes,
}.Build()
File_api_machines_proto = out.File
file_api_machines_proto_rawDesc = nil
file_api_machines_proto_goTypes = nil
file_api_machines_proto_depIdxs = nil
}
+152
View File
@@ -0,0 +1,152 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: api/ref.proto
package api
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Ref struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
}
func (x *Ref) Reset() {
*x = Ref{}
if protoimpl.UnsafeEnabled {
mi := &file_api_ref_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Ref) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Ref) ProtoMessage() {}
func (x *Ref) ProtoReflect() protoreflect.Message {
mi := &file_api_ref_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Ref.ProtoReflect.Descriptor instead.
func (*Ref) Descriptor() ([]byte, []int) {
return file_api_ref_proto_rawDescGZIP(), []int{0}
}
func (x *Ref) GetId() uint64 {
if x != nil {
return x.Id
}
return 0
}
func (x *Ref) GetName() string {
if x != nil {
return x.Name
}
return ""
}
var File_api_ref_proto protoreflect.FileDescriptor
var file_api_ref_proto_rawDesc = []byte{
0x0a, 0x0d, 0x61, 0x70, 0x69, 0x2f, 0x72, 0x65, 0x66, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x03, 0x61, 0x70, 0x69, 0x22, 0x29, 0x0a, 0x03, 0x52, 0x65, 0x66, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42,
0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x73,
0x69, 0x65, 0x62, 0x65, 0x6e, 0x73, 0x2f, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f,
0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
file_api_ref_proto_rawDescOnce sync.Once
file_api_ref_proto_rawDescData = file_api_ref_proto_rawDesc
)
func file_api_ref_proto_rawDescGZIP() []byte {
file_api_ref_proto_rawDescOnce.Do(func() {
file_api_ref_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_ref_proto_rawDescData)
})
return file_api_ref_proto_rawDescData
}
var file_api_ref_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_api_ref_proto_goTypes = []interface{}{
(*Ref)(nil), // 0: api.Ref
}
var file_api_ref_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_api_ref_proto_init() }
func file_api_ref_proto_init() {
if File_api_ref_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_api_ref_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Ref); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_ref_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_api_ref_proto_goTypes,
DependencyIndexes: file_api_ref_proto_depIdxs,
MessageInfos: file_api_ref_proto_msgTypes,
}.Build()
File_api_ref_proto = out.File
file_api_ref_proto_rawDesc = nil
file_api_ref_proto_goTypes = nil
file_api_ref_proto_depIdxs = nil
}
+397
View File
@@ -0,0 +1,397 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: api/tailnets.proto
package api
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Tailnet struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
}
func (x *Tailnet) Reset() {
*x = Tailnet{}
if protoimpl.UnsafeEnabled {
mi := &file_api_tailnets_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Tailnet) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Tailnet) ProtoMessage() {}
func (x *Tailnet) ProtoReflect() protoreflect.Message {
mi := &file_api_tailnets_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Tailnet.ProtoReflect.Descriptor instead.
func (*Tailnet) Descriptor() ([]byte, []int) {
return file_api_tailnets_proto_rawDescGZIP(), []int{0}
}
func (x *Tailnet) GetId() uint64 {
if x != nil {
return x.Id
}
return 0
}
func (x *Tailnet) GetName() string {
if x != nil {
return x.Name
}
return ""
}
type CreateTailnetRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}
func (x *CreateTailnetRequest) Reset() {
*x = CreateTailnetRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_tailnets_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateTailnetRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateTailnetRequest) ProtoMessage() {}
func (x *CreateTailnetRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_tailnets_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateTailnetRequest.ProtoReflect.Descriptor instead.
func (*CreateTailnetRequest) Descriptor() ([]byte, []int) {
return file_api_tailnets_proto_rawDescGZIP(), []int{1}
}
func (x *CreateTailnetRequest) GetName() string {
if x != nil {
return x.Name
}
return ""
}
type CreateTailnetResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tailnet *Tailnet `protobuf:"bytes,1,opt,name=tailnet,proto3" json:"tailnet,omitempty"`
}
func (x *CreateTailnetResponse) Reset() {
*x = CreateTailnetResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_tailnets_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CreateTailnetResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CreateTailnetResponse) ProtoMessage() {}
func (x *CreateTailnetResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_tailnets_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CreateTailnetResponse.ProtoReflect.Descriptor instead.
func (*CreateTailnetResponse) Descriptor() ([]byte, []int) {
return file_api_tailnets_proto_rawDescGZIP(), []int{2}
}
func (x *CreateTailnetResponse) GetTailnet() *Tailnet {
if x != nil {
return x.Tailnet
}
return nil
}
type ListTailnetRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *ListTailnetRequest) Reset() {
*x = ListTailnetRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_tailnets_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListTailnetRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListTailnetRequest) ProtoMessage() {}
func (x *ListTailnetRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_tailnets_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListTailnetRequest.ProtoReflect.Descriptor instead.
func (*ListTailnetRequest) Descriptor() ([]byte, []int) {
return file_api_tailnets_proto_rawDescGZIP(), []int{3}
}
type ListTailnetResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Tailnet []*Tailnet `protobuf:"bytes,1,rep,name=tailnet,proto3" json:"tailnet,omitempty"`
}
func (x *ListTailnetResponse) Reset() {
*x = ListTailnetResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_tailnets_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *ListTailnetResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ListTailnetResponse) ProtoMessage() {}
func (x *ListTailnetResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_tailnets_proto_msgTypes[4]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ListTailnetResponse.ProtoReflect.Descriptor instead.
func (*ListTailnetResponse) Descriptor() ([]byte, []int) {
return file_api_tailnets_proto_rawDescGZIP(), []int{4}
}
func (x *ListTailnetResponse) GetTailnet() []*Tailnet {
if x != nil {
return x.Tailnet
}
return nil
}
var File_api_tailnets_proto protoreflect.FileDescriptor
var file_api_tailnets_proto_rawDesc = []byte{
0x0a, 0x12, 0x61, 0x70, 0x69, 0x2f, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x22, 0x2d, 0x0a, 0x07, 0x54, 0x61, 0x69,
0x6c, 0x6e, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x2a, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61,
0x74, 0x65, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3f, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x61,
0x69, 0x6c, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a,
0x07, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x52, 0x07, 0x74, 0x61,
0x69, 0x6c, 0x6e, 0x65, 0x74, 0x22, 0x14, 0x0a, 0x12, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x61, 0x69,
0x6c, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3d, 0x0a, 0x13, 0x4c,
0x69, 0x73, 0x74, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x26, 0x0a, 0x07, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x61, 0x69, 0x6c, 0x6e, 0x65,
0x74, 0x52, 0x07, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x73, 0x69, 0x65, 0x62, 0x65, 0x6e,
0x73, 0x2f, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67,
0x65, 0x6e, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_api_tailnets_proto_rawDescOnce sync.Once
file_api_tailnets_proto_rawDescData = file_api_tailnets_proto_rawDesc
)
func file_api_tailnets_proto_rawDescGZIP() []byte {
file_api_tailnets_proto_rawDescOnce.Do(func() {
file_api_tailnets_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_tailnets_proto_rawDescData)
})
return file_api_tailnets_proto_rawDescData
}
var file_api_tailnets_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_api_tailnets_proto_goTypes = []interface{}{
(*Tailnet)(nil), // 0: api.Tailnet
(*CreateTailnetRequest)(nil), // 1: api.CreateTailnetRequest
(*CreateTailnetResponse)(nil), // 2: api.CreateTailnetResponse
(*ListTailnetRequest)(nil), // 3: api.ListTailnetRequest
(*ListTailnetResponse)(nil), // 4: api.ListTailnetResponse
}
var file_api_tailnets_proto_depIdxs = []int32{
0, // 0: api.CreateTailnetResponse.tailnet:type_name -> api.Tailnet
0, // 1: api.ListTailnetResponse.tailnet:type_name -> api.Tailnet
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_api_tailnets_proto_init() }
func file_api_tailnets_proto_init() {
if File_api_tailnets_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_api_tailnets_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Tailnet); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_tailnets_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateTailnetRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_tailnets_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CreateTailnetResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_tailnets_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListTailnetRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_tailnets_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*ListTailnetResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_tailnets_proto_rawDesc,
NumEnums: 0,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_api_tailnets_proto_goTypes,
DependencyIndexes: file_api_tailnets_proto_depIdxs,
MessageInfos: file_api_tailnets_proto_msgTypes,
}.Build()
File_api_tailnets_proto = out.File
file_api_tailnets_proto_rawDesc = nil
file_api_tailnets_proto_goTypes = nil
file_api_tailnets_proto_depIdxs = nil
}
+206
View File
@@ -0,0 +1,206 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.27.1
// protoc v3.17.3
// source: api/version.proto
package api
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type GetVersionRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *GetVersionRequest) Reset() {
*x = GetVersionRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_api_version_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetVersionRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetVersionRequest) ProtoMessage() {}
func (x *GetVersionRequest) ProtoReflect() protoreflect.Message {
mi := &file_api_version_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetVersionRequest.ProtoReflect.Descriptor instead.
func (*GetVersionRequest) Descriptor() ([]byte, []int) {
return file_api_version_proto_rawDescGZIP(), []int{0}
}
type GetVersionResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"`
Revision string `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"`
}
func (x *GetVersionResponse) Reset() {
*x = GetVersionResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_api_version_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GetVersionResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GetVersionResponse) ProtoMessage() {}
func (x *GetVersionResponse) ProtoReflect() protoreflect.Message {
mi := &file_api_version_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GetVersionResponse.ProtoReflect.Descriptor instead.
func (*GetVersionResponse) Descriptor() ([]byte, []int) {
return file_api_version_proto_rawDescGZIP(), []int{1}
}
func (x *GetVersionResponse) GetVersion() string {
if x != nil {
return x.Version
}
return ""
}
func (x *GetVersionResponse) GetRevision() string {
if x != nil {
return x.Revision
}
return ""
}
var File_api_version_proto protoreflect.FileDescriptor
var file_api_version_proto_rawDesc = []byte{
0x0a, 0x11, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x22, 0x13, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x4a, 0x0a,
0x12, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a,
0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x72, 0x65, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x73, 0x69, 0x65, 0x62, 0x65, 0x6e, 0x73,
0x2f, 0x69, 0x6f, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x67, 0x65,
0x6e, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_api_version_proto_rawDescOnce sync.Once
file_api_version_proto_rawDescData = file_api_version_proto_rawDesc
)
func file_api_version_proto_rawDescGZIP() []byte {
file_api_version_proto_rawDescOnce.Do(func() {
file_api_version_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_version_proto_rawDescData)
})
return file_api_version_proto_rawDescData
}
var file_api_version_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_api_version_proto_goTypes = []interface{}{
(*GetVersionRequest)(nil), // 0: api.GetVersionRequest
(*GetVersionResponse)(nil), // 1: api.GetVersionResponse
}
var file_api_version_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_api_version_proto_init() }
func file_api_version_proto_init() {
if File_api_version_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_api_version_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetVersionRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_api_version_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GetVersionResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_api_version_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_api_version_proto_goTypes,
DependencyIndexes: file_api_version_proto_depIdxs,
MessageInfos: file_api_version_proto_msgTypes,
}.Build()
File_api_version_proto = out.File
file_api_version_proto_rawDesc = nil
file_api_version_proto_goTypes = nil
file_api_version_proto_depIdxs = nil
}
+44
View File
@@ -0,0 +1,44 @@
syntax = "proto3";
package api;
option go_package = "github.com/jsiebens/ionscale/pkg/gen;api";
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "api/ref.proto";
message CreateAuthKeyRequest {
uint64 tailnet_id = 1;
bool ephemeral = 2;
optional google.protobuf.Duration expiry = 3;
repeated string tags = 4;
}
message CreateAuthKeyResponse {
AuthKey auth_key = 1;
string value = 2;
}
message DeleteAuthKeyRequest {
uint64 auth_key_id = 1;
}
message DeleteAuthKeyResponse {}
message ListAuthKeysRequest {
uint64 tailnet_id = 1;
}
message ListAuthKeysResponse {
repeated AuthKey auth_keys = 1;
}
message AuthKey {
uint64 id = 1;
string key = 2;
bool ephemeral = 3;
repeated string tags = 4;
google.protobuf.Timestamp created_at = 5;
optional google.protobuf.Timestamp expires_at = 6;
Ref tailnet = 7;
}
+25
View File
@@ -0,0 +1,25 @@
syntax = "proto3";
package api;
option go_package = "github.com/jsiebens/ionscale/pkg/gen;api";
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "api/version.proto";
import "api/tailnets.proto";
import "api/auth_keys.proto";
import "api/machines.proto";
service Ionscale {
rpc GetVersion (GetVersionRequest) returns (GetVersionResponse) {}
rpc CreateTailnet (CreateTailnetRequest) returns (CreateTailnetResponse) {}
rpc ListTailnets (ListTailnetRequest) returns (ListTailnetResponse) {}
rpc CreateAuthKey (CreateAuthKeyRequest) returns (CreateAuthKeyResponse) {}
rpc DeleteAuthKey (DeleteAuthKeyRequest) returns (DeleteAuthKeyResponse) {}
rpc ListAuthKeys (ListAuthKeysRequest) returns (ListAuthKeysResponse) {}
rpc ListMachines (ListMachinesRequest) returns (ListMachinesResponse) {}
rpc DeleteMachine(DeleteMachineRequest) returns (DeleteMachineResponse) {}
}
+34
View File
@@ -0,0 +1,34 @@
syntax = "proto3";
package api;
option go_package = "github.com/jsiebens/ionscale/pkg/gen;api";
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "api/ref.proto";
message ListMachinesRequest {
uint64 tailnet_id = 1;
}
message ListMachinesResponse {
repeated Machine machines = 1;
}
message DeleteMachineRequest {
uint64 machine_id = 1;
}
message DeleteMachineResponse {}
message Machine {
uint64 id = 1;
string name = 2;
string ipv4 = 3;
string ipv6 = 4;
bool ephemeral = 5;
google.protobuf.Timestamp last_seen = 6;
bool connected = 7;
Ref tailnet = 8;
Ref user = 9;
}
+8
View File
@@ -0,0 +1,8 @@
syntax = "proto3";
package api;
option go_package = "github.com/jsiebens/ionscale/pkg/gen;api";
message Ref {
uint64 id = 1;
string name = 2;
}
+23
View File
@@ -0,0 +1,23 @@
syntax = "proto3";
package api;
option go_package = "github.com/jsiebens/ionscale/pkg/gen;api";
message Tailnet {
uint64 id = 1;
string name = 2;
}
message CreateTailnetRequest {
string name = 1;
}
message CreateTailnetResponse {
Tailnet tailnet = 1;
}
message ListTailnetRequest {
}
message ListTailnetResponse {
repeated Tailnet tailnet = 1;
}
+11
View File
@@ -0,0 +1,11 @@
syntax = "proto3";
package api;
option go_package = "github.com/jsiebens/ionscale/pkg/gen;api";
message GetVersionRequest {
}
message GetVersionResponse {
string version = 1;
string revision = 2;
}