mirror of
https://github.com/edgelesssys/constellation.git
synced 2024-12-24 23:19:39 -05:00
[node operator] Add GCP client
Signed-off-by: Malte Poll <mp@edgeless.systems>
This commit is contained in:
parent
0618a000a7
commit
717570d00a
@ -3,11 +3,13 @@ module github.com/edgelesssys/constellation/operators/constellation-node-operato
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/googleapis/gax-go/v2 v2.4.0
|
||||
github.com/medik8s/node-maintenance-operator v0.13.0
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/gomega v1.18.1
|
||||
github.com/stretchr/testify v1.7.5
|
||||
go.etcd.io/etcd/api/v3 v3.5.4
|
||||
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f
|
||||
k8s.io/api v0.24.0
|
||||
k8s.io/apimachinery v0.24.0
|
||||
k8s.io/client-go v0.24.0
|
||||
@ -21,14 +23,15 @@ require (
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.4 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 // indirect
|
||||
google.golang.org/grpc v1.40.0 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
google.golang.org/grpc v1.47.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.81.0 // indirect
|
||||
cloud.google.com/go/compute v1.7.0
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.18 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
|
||||
@ -53,9 +56,9 @@ require (
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
||||
github.com/google/go-cmp v0.5.5 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/gofuzz v1.1.0 // indirect
|
||||
github.com/google/uuid v1.1.2 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
@ -74,18 +77,19 @@ require (
|
||||
go.etcd.io/etcd v3.3.27+incompatible
|
||||
go.etcd.io/etcd/client/v3 v3.5.4
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/multierr v1.6.0
|
||||
go.uber.org/zap v1.19.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2 // indirect
|
||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810 // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
|
||||
google.golang.org/api v0.86.0
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
google.golang.org/protobuf v1.28.0
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
@ -17,17 +17,35 @@ cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKP
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
|
||||
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
|
||||
cloud.google.com/go v0.81.0 h1:at8Tk2zUz63cLPR0JPWm5vp77pEZmzxEQBEfRKn1VV8=
|
||||
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
|
||||
cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
|
||||
cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
|
||||
cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
|
||||
cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
|
||||
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
|
||||
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
|
||||
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
|
||||
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
|
||||
cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
|
||||
cloud.google.com/go v0.102.0 h1:DAq3r8y4mDgyB/ZPJ9v/5VJNqjgJAxTn6ZYLlUywOu8=
|
||||
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
|
||||
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/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
|
||||
cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
|
||||
cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
|
||||
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
|
||||
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
|
||||
cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk=
|
||||
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
|
||||
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=
|
||||
@ -37,6 +55,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
||||
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=
|
||||
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
@ -102,7 +121,12 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
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-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
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-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
|
||||
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
|
||||
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
|
||||
@ -147,6 +171,8 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
@ -218,6 +244,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
||||
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/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
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=
|
||||
@ -236,6 +263,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e/go.mod h1:0AA//k/eakGydO4jKRoRL2j92ZKSzTgj9tclaCrvXHk=
|
||||
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=
|
||||
@ -254,14 +282,18 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.2/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 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
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/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
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=
|
||||
@ -274,14 +306,29 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/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/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.1.0 h1:zO8WHNx/MYiAKJ3d5spxZXZE6KHmIQGQcAzwUzV7qQw=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
|
||||
github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
|
||||
github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
|
||||
github.com/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk=
|
||||
github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
|
||||
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
@ -552,6 +599,7 @@ 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.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E=
|
||||
@ -673,12 +721,22 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
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=
|
||||
@ -692,8 +750,17 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2 h1:+jnHzr9VPj32ykQVai5DNahi9+NSp7yYuCsl5eAQtL0=
|
||||
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||
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=
|
||||
@ -705,6 +772,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -763,16 +831,36 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/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-20210616094352-59db8d763f22/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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0=
|
||||
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
@ -849,14 +937,19 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY=
|
||||
gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
@ -881,6 +974,26 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR
|
||||
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
|
||||
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
|
||||
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
|
||||
google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
|
||||
google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
|
||||
google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
|
||||
google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
|
||||
google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
|
||||
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
|
||||
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
|
||||
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
|
||||
google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
|
||||
google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
|
||||
google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
|
||||
google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
|
||||
google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
|
||||
google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
|
||||
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
|
||||
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
|
||||
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
|
||||
google.golang.org/api v0.86.0 h1:ZAnyOHQFIuWso1BodVfSaRyffD74T9ERGFa3k1fNk/U=
|
||||
google.golang.org/api v0.86.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
|
||||
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=
|
||||
@ -931,11 +1044,49 @@ google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
|
||||
google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
|
||||
google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368 h1:Et6SkiuvnBn+SgrSYXs/BrUpGB4mbdwt4R3vaPIlicA=
|
||||
google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
|
||||
google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
|
||||
google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
|
||||
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
|
||||
google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f h1:hJ/Y5SqPXbarffmAsApliUlcvMU+wScNGfyop4bZm8o=
|
||||
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
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=
|
||||
@ -956,9 +1107,19 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
|
||||
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
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=
|
||||
@ -971,8 +1132,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||
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=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
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=
|
||||
|
@ -0,0 +1,53 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/googleapis/gax-go/v2"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
)
|
||||
|
||||
type instanceAPI interface {
|
||||
Close() error
|
||||
Get(ctx context.Context, req *computepb.GetInstanceRequest,
|
||||
opts ...gax.CallOption) (*computepb.Instance, error)
|
||||
}
|
||||
|
||||
type instanceTemplateAPI interface {
|
||||
Close() error
|
||||
Get(ctx context.Context, req *computepb.GetInstanceTemplateRequest,
|
||||
opts ...gax.CallOption) (*computepb.InstanceTemplate, error)
|
||||
Delete(ctx context.Context, req *computepb.DeleteInstanceTemplateRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
Insert(ctx context.Context, req *computepb.InsertInstanceTemplateRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
}
|
||||
|
||||
type instanceGroupManagersAPI interface {
|
||||
Close() error
|
||||
Get(ctx context.Context, req *computepb.GetInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption) (*computepb.InstanceGroupManager, error)
|
||||
SetInstanceTemplate(ctx context.Context, req *computepb.SetInstanceTemplateInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
CreateInstances(ctx context.Context, req *computepb.CreateInstancesInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
DeleteInstances(ctx context.Context, req *computepb.DeleteInstancesInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption) (Operation, error)
|
||||
}
|
||||
|
||||
type diskAPI interface {
|
||||
Close() error
|
||||
Get(ctx context.Context, req *computepb.GetDiskRequest,
|
||||
opts ...gax.CallOption) (*computepb.Disk, error)
|
||||
}
|
||||
|
||||
type Operation interface {
|
||||
Proto() *computepb.Operation
|
||||
Done() bool
|
||||
Wait(ctx context.Context, opts ...gax.CallOption) error
|
||||
}
|
||||
|
||||
type prng interface {
|
||||
// Intn returns, as an int, a non-negative pseudo-random number in the half-open interval [0,n). It panics if n <= 0.
|
||||
Intn(n int) int
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
compute "cloud.google.com/go/compute/apiv1"
|
||||
"go.uber.org/multierr"
|
||||
)
|
||||
|
||||
// Client is a client for the Google Compute Engine.
|
||||
type Client struct {
|
||||
instanceAPI
|
||||
instanceTemplateAPI
|
||||
instanceGroupManagersAPI
|
||||
diskAPI
|
||||
// prng is a pseudo-random number generator seeded with time. Not used for security.
|
||||
prng
|
||||
}
|
||||
|
||||
// New creates a new client for the Google Compute Engine.
|
||||
func New(ctx context.Context) (*Client, error) {
|
||||
var closers []closer
|
||||
insAPI, err := compute.NewInstancesRESTClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
closers = append(closers, insAPI)
|
||||
templAPI, err := compute.NewInstanceTemplatesRESTClient(ctx)
|
||||
if err != nil {
|
||||
_ = closeAll(closers)
|
||||
return nil, err
|
||||
}
|
||||
closers = append(closers, templAPI)
|
||||
groupAPI, err := compute.NewInstanceGroupManagersRESTClient(ctx)
|
||||
if err != nil {
|
||||
_ = closeAll(closers)
|
||||
return nil, err
|
||||
}
|
||||
closers = append(closers, groupAPI)
|
||||
diskAPI, err := compute.NewDisksRESTClient(ctx)
|
||||
if err != nil {
|
||||
_ = closeAll(closers)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Client{
|
||||
instanceAPI: insAPI,
|
||||
instanceTemplateAPI: &instanceTemplateClient{templAPI},
|
||||
instanceGroupManagersAPI: &instanceGroupManagersClient{groupAPI},
|
||||
diskAPI: diskAPI,
|
||||
prng: rand.New(rand.NewSource(int64(time.Now().Nanosecond()))),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close closes the client's connection.
|
||||
func (c *Client) Close() error {
|
||||
closers := []closer{
|
||||
c.instanceAPI,
|
||||
c.instanceTemplateAPI,
|
||||
c.instanceGroupManagersAPI,
|
||||
c.diskAPI,
|
||||
}
|
||||
return closeAll(closers)
|
||||
}
|
||||
|
||||
type closer interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
// closeAll closes all closers, even if an error occurs.
|
||||
//
|
||||
// Errors are collected and a composed error is returned.
|
||||
func closeAll(closers []closer) error {
|
||||
// Since this function is intended to be deferred, it will always call all
|
||||
// close operations, even if a previous operation failed.
|
||||
var errs error
|
||||
for _, closer := range closers {
|
||||
errs = multierr.Append(errs, closer.Close())
|
||||
}
|
||||
return errs
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/googleapis/gax-go/v2"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type stubInstanceAPI struct {
|
||||
instance *computepb.Instance
|
||||
getErr error
|
||||
}
|
||||
|
||||
func (a stubInstanceAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubInstanceAPI) Get(ctx context.Context, req *computepb.GetInstanceRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*computepb.Instance, error) {
|
||||
return a.instance, a.getErr
|
||||
}
|
||||
|
||||
type stubInstanceTemplateAPI struct {
|
||||
template *computepb.InstanceTemplate
|
||||
getErr error
|
||||
deleteErr error
|
||||
insertErr error
|
||||
}
|
||||
|
||||
func (a stubInstanceTemplateAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubInstanceTemplateAPI) Get(ctx context.Context, req *computepb.GetInstanceTemplateRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*computepb.InstanceTemplate, error) {
|
||||
return a.template, a.getErr
|
||||
}
|
||||
|
||||
func (a stubInstanceTemplateAPI) Delete(ctx context.Context, req *computepb.DeleteInstanceTemplateRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, a.deleteErr
|
||||
}
|
||||
|
||||
func (a stubInstanceTemplateAPI) Insert(ctx context.Context, req *computepb.InsertInstanceTemplateRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, a.insertErr
|
||||
}
|
||||
|
||||
type stubInstanceGroupManagersAPI struct {
|
||||
instanceGroupManager *computepb.InstanceGroupManager
|
||||
getErr error
|
||||
setInstanceTemplateErr error
|
||||
createInstancesErr error
|
||||
deleteInstancesErr error
|
||||
}
|
||||
|
||||
func (a stubInstanceGroupManagersAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubInstanceGroupManagersAPI) Get(ctx context.Context, req *computepb.GetInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*computepb.InstanceGroupManager, error) {
|
||||
return a.instanceGroupManager, a.getErr
|
||||
}
|
||||
|
||||
func (a stubInstanceGroupManagersAPI) SetInstanceTemplate(ctx context.Context, req *computepb.SetInstanceTemplateInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, a.setInstanceTemplateErr
|
||||
}
|
||||
|
||||
func (a stubInstanceGroupManagersAPI) CreateInstances(ctx context.Context, req *computepb.CreateInstancesInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, a.createInstancesErr
|
||||
}
|
||||
|
||||
func (a stubInstanceGroupManagersAPI) DeleteInstances(ctx context.Context, req *computepb.DeleteInstancesInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
if a.deleteInstancesErr != nil {
|
||||
return nil, a.deleteInstancesErr
|
||||
}
|
||||
return &stubOperation{
|
||||
&computepb.Operation{
|
||||
Name: proto.String("name"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type stubDiskAPI struct {
|
||||
disk *computepb.Disk
|
||||
getErr error
|
||||
}
|
||||
|
||||
func (a stubDiskAPI) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a stubDiskAPI) Get(ctx context.Context, req *computepb.GetDiskRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*computepb.Disk, error) {
|
||||
return a.disk, a.getErr
|
||||
}
|
||||
|
||||
type stubOperation struct {
|
||||
*computepb.Operation
|
||||
}
|
||||
|
||||
func (o *stubOperation) Proto() *computepb.Operation {
|
||||
return o.Operation
|
||||
}
|
||||
|
||||
func (o *stubOperation) Done() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *stubOperation) Wait(ctx context.Context, opts ...gax.CallOption) error {
|
||||
return nil
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
diskSourceRegex = regexp.MustCompile(`^https://www.googleapis.com/compute/v1/projects/([^/]+)/zones/([^/]+)/disks/([^/]+)$`)
|
||||
computeAPIBase = regexp.MustCompile(`^https://www.googleapis.com/compute/v1/(.+)$`)
|
||||
)
|
||||
|
||||
// diskSourceToDiskReq converts a disk source URI to a disk request.
|
||||
func diskSourceToDiskReq(diskSource string) (*compute.GetDiskRequest, error) {
|
||||
matches := diskSourceRegex.FindStringSubmatch(diskSource)
|
||||
if len(matches) != 4 {
|
||||
return nil, fmt.Errorf("error splitting diskSource: %v", diskSource)
|
||||
}
|
||||
return &compute.GetDiskRequest{
|
||||
Disk: matches[3],
|
||||
Project: matches[1],
|
||||
Zone: matches[2],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// uriNormalize normalizes a compute API URI by removing the optional URI prefix.
|
||||
// for normalization, the prefix 'https://www.googleapis.com/compute/v1/' is removed.
|
||||
func uriNormalize(imageURI string) string {
|
||||
matches := computeAPIBase.FindStringSubmatch(imageURI)
|
||||
if len(matches) != 2 {
|
||||
return imageURI
|
||||
}
|
||||
return matches[1]
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
)
|
||||
|
||||
func TestDiskSourceToDiskReq(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
diskSource string
|
||||
wantRequest *compute.GetDiskRequest
|
||||
wantErr bool
|
||||
}{
|
||||
"valid request": {
|
||||
diskSource: "https://www.googleapis.com/compute/v1/projects/project/zones/zone/disks/disk",
|
||||
wantRequest: &compute.GetDiskRequest{
|
||||
Disk: "disk",
|
||||
Project: "project",
|
||||
Zone: "zone",
|
||||
},
|
||||
},
|
||||
"invalid host": {
|
||||
diskSource: "https://hostname/compute/v1/projects/project/zones/zone/disks/disk",
|
||||
wantErr: true,
|
||||
},
|
||||
"invalid scheme": {
|
||||
diskSource: "invalid://www.googleapis.com/compute/v1/projects/project/zones/zone/disks/disk",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
request, err := diskSourceToDiskReq(tc.diskSource)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantRequest, request)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestURINormalize(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
imageURI string
|
||||
wantNormalized string
|
||||
}{
|
||||
"URI with scheme and host": {
|
||||
imageURI: "https://www.googleapis.com/compute/v1/projects/project/global/images/image",
|
||||
wantNormalized: "projects/project/global/images/image",
|
||||
},
|
||||
"normalized": {
|
||||
imageURI: "projects/project/global/images/image",
|
||||
wantNormalized: "projects/project/global/images/image",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
normalized := uriNormalize(tc.imageURI)
|
||||
assert.Equal(tc.wantNormalized, normalized)
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
compute "cloud.google.com/go/compute/apiv1"
|
||||
"github.com/googleapis/gax-go/v2"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
)
|
||||
|
||||
type instanceTemplateClient struct {
|
||||
*compute.InstanceTemplatesClient
|
||||
}
|
||||
|
||||
func (c *instanceTemplateClient) Close() error {
|
||||
return c.InstanceTemplatesClient.Close()
|
||||
}
|
||||
|
||||
func (c *instanceTemplateClient) Delete(ctx context.Context, req *computepb.DeleteInstanceTemplateRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.InstanceTemplatesClient.Delete(ctx, req, opts...)
|
||||
}
|
||||
|
||||
func (c *instanceTemplateClient) Insert(ctx context.Context, req *computepb.InsertInstanceTemplateRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.InstanceTemplatesClient.Insert(ctx, req, opts...)
|
||||
}
|
||||
|
||||
type instanceGroupManagersClient struct {
|
||||
*compute.InstanceGroupManagersClient
|
||||
}
|
||||
|
||||
func (c *instanceGroupManagersClient) Close() error {
|
||||
return c.InstanceGroupManagersClient.Close()
|
||||
}
|
||||
|
||||
func (c *instanceGroupManagersClient) Get(ctx context.Context, req *computepb.GetInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (*computepb.InstanceGroupManager, error) {
|
||||
return c.InstanceGroupManagersClient.Get(ctx, req, opts...)
|
||||
}
|
||||
|
||||
func (c *instanceGroupManagersClient) SetInstanceTemplate(ctx context.Context, req *computepb.SetInstanceTemplateInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.InstanceGroupManagersClient.SetInstanceTemplate(ctx, req, opts...)
|
||||
}
|
||||
|
||||
func (c *instanceGroupManagersClient) CreateInstances(ctx context.Context, req *computepb.CreateInstancesInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.InstanceGroupManagersClient.CreateInstances(ctx, req, opts...)
|
||||
}
|
||||
|
||||
func (c *instanceGroupManagersClient) DeleteInstances(ctx context.Context, req *computepb.DeleteInstancesInstanceGroupManagerRequest,
|
||||
opts ...gax.CallOption,
|
||||
) (Operation, error) {
|
||||
return c.InstanceGroupManagersClient.DeleteInstances(ctx, req, opts...)
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var instanceGroupIDRegex = regexp.MustCompile(`^projects/([^/]+)/zones/([^/]+)/instanceGroupManagers/([^/]+)$`)
|
||||
|
||||
// splitInstanceGroupID splits an instance group ID into core components.
|
||||
func splitInstanceGroupID(instanceGroupID string) (project, zone, instanceGroup string, err error) {
|
||||
matches := instanceGroupIDRegex.FindStringSubmatch(instanceGroupID)
|
||||
if len(matches) != 4 {
|
||||
return "", "", "", fmt.Errorf("error splitting instanceGroupID: %v", instanceGroupID)
|
||||
}
|
||||
return matches[1], matches[2], matches[3], nil
|
||||
}
|
||||
|
||||
// generateInstanceName generates a random instance name.
|
||||
func generateInstanceName(baseInstanceName string, random prng) (string, error) {
|
||||
letters := []byte("abcdefghijklmnopqrstuvwxyz0123456789")
|
||||
const uidLen = 4
|
||||
uid := make([]byte, 0, uidLen)
|
||||
for i := 0; i < uidLen; i++ {
|
||||
n := random.Intn(len(letters))
|
||||
uid = append(uid, letters[n])
|
||||
}
|
||||
return baseInstanceName + "-" + string(uid), nil
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSplitInstanceGroupID(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
instanceGroupID string
|
||||
|
||||
wantProject string
|
||||
wantZone string
|
||||
wantInstanceGroup string
|
||||
wantErr bool
|
||||
}{
|
||||
"valid request": {
|
||||
instanceGroupID: "projects/project/zones/zone/instanceGroupManagers/instanceGroup",
|
||||
wantProject: "project",
|
||||
wantZone: "zone",
|
||||
wantInstanceGroup: "instanceGroup",
|
||||
},
|
||||
"wrong format": {
|
||||
instanceGroupID: "wrong-format",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
gotProject, gotZone, gotInstanceGroup, err := splitInstanceGroupID(tc.instanceGroupID)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantProject, gotProject)
|
||||
assert.Equal(tc.wantZone, gotZone)
|
||||
assert.Equal(tc.wantInstanceGroup, gotInstanceGroup)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateInstanceName(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
baseInstanceName := "base"
|
||||
gotInstanceName, err := generateInstanceName(baseInstanceName, &stubRng{result: 0})
|
||||
require.NoError(err)
|
||||
assert.Equal("base-aaaa", gotInstanceName)
|
||||
}
|
||||
|
||||
func TestGenerateInstanceNameRandomTest(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
instanceNameRegexp := regexp.MustCompile(`^base-[0-9a-z]{4}$`)
|
||||
baseInstanceName := "base"
|
||||
random := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
||||
gotInstanceName, err := generateInstanceName(baseInstanceName, random)
|
||||
require.NoError(err)
|
||||
assert.Regexp(instanceNameRegexp, gotInstanceName)
|
||||
}
|
||||
|
||||
type stubRng struct {
|
||||
result int
|
||||
}
|
||||
|
||||
func (r *stubRng) Intn(n int) int {
|
||||
return r.result
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
numberedNameRegex = regexp.MustCompile(`^(.+)-(\d+)$`)
|
||||
instanceTemplateIDRegex = regexp.MustCompile(`projects/([^/]+)/global/instanceTemplates/([^/]+)`)
|
||||
)
|
||||
|
||||
// generateInstanceTemplateName generates a unique name for an instance template by incrementing a counter.
|
||||
// The name is in the format <prefix>-<counter>.
|
||||
func generateInstanceTemplateName(last string) (string, error) {
|
||||
if len(last) > 0 && last[len(last)-1] == '-' {
|
||||
return last + "1", nil
|
||||
}
|
||||
matches := numberedNameRegex.FindStringSubmatch(last)
|
||||
if len(matches) != 3 {
|
||||
return last + "-1", nil
|
||||
}
|
||||
n, err := strconv.Atoi(matches[2])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n < 1 || n == math.MaxInt {
|
||||
return "", fmt.Errorf("invalid counter: %v", n)
|
||||
}
|
||||
return matches[1] + "-" + strconv.Itoa(n+1), nil
|
||||
}
|
||||
|
||||
// splitInstanceTemplateID splits an instance template ID into its project and name components.
|
||||
func splitInstanceTemplateID(instanceTemplateID string) (project, templateName string, err error) {
|
||||
matches := instanceTemplateIDRegex.FindStringSubmatch(instanceTemplateID)
|
||||
if len(matches) != 3 {
|
||||
return "", "", fmt.Errorf("error splitting instanceTemplateID: %v", instanceTemplateID)
|
||||
}
|
||||
return matches[1], matches[2], nil
|
||||
}
|
||||
|
||||
// joinInstanceTemplateURI joins a project and template name into an instance template URI.
|
||||
func joinInstanceTemplateURI(project, templateName string) string {
|
||||
return fmt.Sprintf("https://www.googleapis.com/compute/v1/projects/%v/global/instanceTemplates/%v", project, templateName)
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGenerateInstanceTemplateName(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
last string
|
||||
wantNext string
|
||||
wantErr bool
|
||||
}{
|
||||
"no numbering yet": {
|
||||
last: "prefix",
|
||||
wantNext: "prefix-1",
|
||||
},
|
||||
"ends in -": {
|
||||
last: "prefix-",
|
||||
wantNext: "prefix-1",
|
||||
},
|
||||
"has number": {
|
||||
last: "prefix-1",
|
||||
wantNext: "prefix-2",
|
||||
},
|
||||
"last number too small": {
|
||||
last: "prefix-0",
|
||||
wantErr: true,
|
||||
},
|
||||
"last number would overflow": {
|
||||
last: fmt.Sprintf("prefix-%d", math.MaxInt),
|
||||
wantErr: true,
|
||||
},
|
||||
"integer out of range": {
|
||||
last: "prefix-999999999999999999999999999999999999999999",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
gotNext, err := generateInstanceTemplateName(tc.last)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantNext, gotNext)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitInstanceTemplateID(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
instanceTemplateID string
|
||||
|
||||
wantProject string
|
||||
wantTemplateName string
|
||||
wantErr bool
|
||||
}{
|
||||
"valid request": {
|
||||
instanceTemplateID: "projects/project/global/instanceTemplates/template",
|
||||
wantProject: "project",
|
||||
wantTemplateName: "template",
|
||||
},
|
||||
"wrong format": {
|
||||
instanceTemplateID: "wrong-format",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
gotProject, gotTemplateName, err := splitInstanceTemplateID(tc.instanceTemplateID)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantProject, gotProject)
|
||||
assert.Equal(tc.wantTemplateName, gotTemplateName)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinInstanceTemplateID(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
project := "project"
|
||||
templateName := "template"
|
||||
wantInstanceTemplateURI := "https://www.googleapis.com/compute/v1/projects/project/global/instanceTemplates/template"
|
||||
gotInstancetemplateURI := joinInstanceTemplateURI(project, templateName)
|
||||
assert.Equal(wantInstanceTemplateURI, gotInstancetemplateURI)
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
)
|
||||
|
||||
// getMetadataByKey returns the value of the metadata key in the given metadata.
|
||||
func getMetadataByKey(metadata *computepb.Metadata, key string) string {
|
||||
if metadata == nil {
|
||||
return ""
|
||||
}
|
||||
for _, item := range metadata.Items {
|
||||
if item.Key == nil || item.Value == nil {
|
||||
continue
|
||||
}
|
||||
if *item.Key == key {
|
||||
return *item.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestGetMetadataByKey(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
metadata *compute.Metadata
|
||||
key string
|
||||
wantValue string
|
||||
}{
|
||||
"metadata has key": {
|
||||
metadata: &compute.Metadata{
|
||||
Items: []*compute.Items{
|
||||
{Key: proto.String("key"), Value: proto.String("value")},
|
||||
},
|
||||
},
|
||||
key: "key",
|
||||
wantValue: "value",
|
||||
},
|
||||
"metadata does not have key": {
|
||||
metadata: &compute.Metadata{
|
||||
Items: []*compute.Items{
|
||||
{Key: proto.String("otherkey"), Value: proto.String("value")},
|
||||
},
|
||||
},
|
||||
key: "key",
|
||||
wantValue: "",
|
||||
},
|
||||
"metadata contains invalid item": {
|
||||
metadata: &compute.Metadata{
|
||||
Items: []*compute.Items{
|
||||
{},
|
||||
{Key: proto.String("key"), Value: proto.String("value")},
|
||||
},
|
||||
},
|
||||
key: "key",
|
||||
wantValue: "value",
|
||||
},
|
||||
"metadata is nil": {
|
||||
key: "key",
|
||||
wantValue: "",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
assert.Equal(tc.wantValue, getMetadataByKey(tc.metadata, tc.key))
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// GetNodeImage returns the image name of the node.
|
||||
func (c *Client) GetNodeImage(ctx context.Context, providerID string) (string, error) {
|
||||
project, zone, instanceName, err := splitProviderID(providerID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
instance, err := c.instanceAPI.Get(ctx, &computepb.GetInstanceRequest{
|
||||
Instance: instanceName,
|
||||
Project: project,
|
||||
Zone: zone,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// first disk is always the boot disk
|
||||
if len(instance.Disks) < 1 {
|
||||
return "", fmt.Errorf("instance %v has no disks", instanceName)
|
||||
}
|
||||
if instance.Disks[0] == nil || instance.Disks[0].Source == nil {
|
||||
return "", fmt.Errorf("instance %q has invalid disk", instanceName)
|
||||
}
|
||||
diskReq, err := diskSourceToDiskReq(*instance.Disks[0].Source)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
disk, err := c.diskAPI.Get(ctx, diskReq)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if disk.SourceImage == nil {
|
||||
return "", fmt.Errorf("disk %q has no source image", diskReq.Disk)
|
||||
}
|
||||
return uriNormalize(*disk.SourceImage), nil
|
||||
}
|
||||
|
||||
// GetScalingGroupID returns the scaling group ID of the node.
|
||||
func (c *Client) GetScalingGroupID(ctx context.Context, providerID string) (string, error) {
|
||||
project, zone, instanceName, err := splitProviderID(providerID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
instance, err := c.instanceAPI.Get(ctx, &computepb.GetInstanceRequest{
|
||||
Instance: instanceName,
|
||||
Project: project,
|
||||
Zone: zone,
|
||||
})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("getting instance %q: %w", instanceName, err)
|
||||
}
|
||||
scalingGroupID := getMetadataByKey(instance.Metadata, "created-by")
|
||||
if scalingGroupID == "" {
|
||||
return "", fmt.Errorf("instance %q has no created-by metadata", instanceName)
|
||||
}
|
||||
return scalingGroupID, nil
|
||||
}
|
||||
|
||||
// CreateNode creates a node in the specified scaling group.
|
||||
func (c *Client) CreateNode(ctx context.Context, scalingGroupID string) (nodeName, providerID string, err error) {
|
||||
project, zone, instanceGroupName, err := splitInstanceGroupID(scalingGroupID)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
instanceGroupManager, err := c.instanceGroupManagersAPI.Get(ctx, &computepb.GetInstanceGroupManagerRequest{
|
||||
InstanceGroupManager: instanceGroupName,
|
||||
Project: project,
|
||||
Zone: zone,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if instanceGroupManager.BaseInstanceName == nil {
|
||||
return "", "", fmt.Errorf("instance group manager %q has no base instance name", instanceGroupName)
|
||||
}
|
||||
instanceName, err := generateInstanceName(*instanceGroupManager.BaseInstanceName, c.prng)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
op, err := c.instanceGroupManagersAPI.CreateInstances(ctx, &computepb.CreateInstancesInstanceGroupManagerRequest{
|
||||
InstanceGroupManager: instanceGroupName,
|
||||
Project: project,
|
||||
Zone: zone,
|
||||
InstanceGroupManagersCreateInstancesRequestResource: &computepb.InstanceGroupManagersCreateInstancesRequest{
|
||||
Instances: []*computepb.PerInstanceConfig{
|
||||
{Name: proto.String(instanceName)},
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if err := op.Wait(ctx); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return instanceName, joinProviderID(project, zone, instanceName), nil
|
||||
}
|
||||
|
||||
// DeleteNode deletes a node specified by its provider ID.
|
||||
func (c *Client) DeleteNode(ctx context.Context, providerID string) error {
|
||||
_, zone, instanceName, err := splitProviderID(providerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scalingGroupID, err := c.GetScalingGroupID(ctx, providerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
instanceGroupProject, instanceGroupZone, instanceGroupName, err := splitInstanceGroupID(scalingGroupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
instanceID := joinInstanceID(zone, instanceName)
|
||||
op, err := c.instanceGroupManagersAPI.DeleteInstances(ctx, &computepb.DeleteInstancesInstanceGroupManagerRequest{
|
||||
InstanceGroupManager: instanceGroupName,
|
||||
Project: instanceGroupProject,
|
||||
Zone: instanceGroupZone,
|
||||
InstanceGroupManagersDeleteInstancesRequestResource: &computepb.InstanceGroupManagersDeleteInstancesRequest{
|
||||
Instances: []string{instanceID},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("deleting instance %q from instance group manager %q: %w", instanceID, scalingGroupID, err)
|
||||
}
|
||||
return op.Wait(ctx)
|
||||
}
|
@ -0,0 +1,292 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestGetNodeImage(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
providerID string
|
||||
attachedDisks []*computepb.AttachedDisk
|
||||
disk *computepb.Disk
|
||||
getInstanceErr error
|
||||
getDiskErr error
|
||||
wantImage string
|
||||
wantErr bool
|
||||
}{
|
||||
"boot disk is found": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
attachedDisks: []*computepb.AttachedDisk{
|
||||
{
|
||||
Source: proto.String("https://www.googleapis.com/compute/v1/projects/project/zones/zone/disks/disk"),
|
||||
},
|
||||
},
|
||||
disk: &computepb.Disk{
|
||||
SourceImage: proto.String("https://www.googleapis.com/compute/v1/projects/project/global/images/image"),
|
||||
},
|
||||
wantImage: "projects/project/global/images/image",
|
||||
},
|
||||
"splitting providerID fails": {
|
||||
providerID: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
"get instance fails": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
getInstanceErr: errors.New("get instance error"),
|
||||
wantErr: true,
|
||||
},
|
||||
"instance has no disks": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
wantErr: true,
|
||||
},
|
||||
"attached disk is invalid": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
attachedDisks: []*computepb.AttachedDisk{{}},
|
||||
wantErr: true,
|
||||
},
|
||||
"boot disk reference is invalid": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
attachedDisks: []*computepb.AttachedDisk{{
|
||||
Source: proto.String("invalid"),
|
||||
}},
|
||||
wantErr: true,
|
||||
},
|
||||
"get disk fails": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
attachedDisks: []*computepb.AttachedDisk{{
|
||||
Source: proto.String("https://www.googleapis.com/compute/v1/projects/project/zones/zone/disks/disk"),
|
||||
}},
|
||||
getDiskErr: errors.New("get disk error"),
|
||||
wantErr: true,
|
||||
},
|
||||
"disk has no source image": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
attachedDisks: []*computepb.AttachedDisk{{
|
||||
Source: proto.String("https://www.googleapis.com/compute/v1/projects/project/zones/zone/disks/disk"),
|
||||
}},
|
||||
disk: &computepb.Disk{},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
client := Client{
|
||||
instanceAPI: &stubInstanceAPI{
|
||||
getErr: tc.getInstanceErr,
|
||||
instance: &computepb.Instance{
|
||||
Disks: tc.attachedDisks,
|
||||
},
|
||||
},
|
||||
diskAPI: &stubDiskAPI{
|
||||
getErr: tc.getDiskErr,
|
||||
disk: tc.disk,
|
||||
},
|
||||
}
|
||||
gotImage, err := client.GetNodeImage(context.Background(), tc.providerID)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantImage, gotImage)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetScalingGroupID(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
providerID string
|
||||
createdBy string
|
||||
getInstanceErr error
|
||||
wantScalingGroupID string
|
||||
wantErr bool
|
||||
}{
|
||||
"scaling group is found": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
createdBy: "projects/project/zones/zone/instanceGroups/instance-group",
|
||||
wantScalingGroupID: "projects/project/zones/zone/instanceGroups/instance-group",
|
||||
},
|
||||
"splitting providerID fails": {
|
||||
providerID: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
"get instance fails": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
getInstanceErr: errors.New("get instance error"),
|
||||
wantErr: true,
|
||||
},
|
||||
"instance has no created-by": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
instance := computepb.Instance{}
|
||||
if tc.createdBy != "" {
|
||||
instance.Metadata = &computepb.Metadata{
|
||||
Items: []*computepb.Items{
|
||||
{
|
||||
Key: proto.String("created-by"),
|
||||
Value: proto.String(tc.createdBy),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
client := Client{
|
||||
instanceAPI: &stubInstanceAPI{
|
||||
getErr: tc.getInstanceErr,
|
||||
instance: &instance,
|
||||
},
|
||||
}
|
||||
gotScalingGroupID, err := client.GetScalingGroupID(context.Background(), tc.providerID)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantScalingGroupID, gotScalingGroupID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateNode(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
scalingGroupID string
|
||||
baseInstanceName *string
|
||||
getInstanceGroupManagerErr error
|
||||
createInstanceErr error
|
||||
wantErr bool
|
||||
}{
|
||||
"scaling group is found": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
baseInstanceName: proto.String("base-name"),
|
||||
},
|
||||
"splitting scalingGroupID fails": {
|
||||
scalingGroupID: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
"get instance group manager fails": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
getInstanceGroupManagerErr: errors.New("get instance group manager error"),
|
||||
wantErr: true,
|
||||
},
|
||||
"instance group manager has no base instance name": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
wantErr: true,
|
||||
},
|
||||
"create instance fails": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
baseInstanceName: proto.String("base-name"),
|
||||
createInstanceErr: errors.New("create instance error"),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
client := Client{
|
||||
instanceGroupManagersAPI: &stubInstanceGroupManagersAPI{
|
||||
getErr: tc.getInstanceGroupManagerErr,
|
||||
createInstancesErr: tc.createInstanceErr,
|
||||
instanceGroupManager: &computepb.InstanceGroupManager{
|
||||
BaseInstanceName: tc.baseInstanceName,
|
||||
},
|
||||
},
|
||||
prng: rand.New(rand.NewSource(int64(time.Now().Nanosecond()))),
|
||||
}
|
||||
instanceName, providerID, err := client.CreateNode(context.Background(), tc.scalingGroupID)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Contains(instanceName, "base-name")
|
||||
assert.Contains(providerID, "base-name")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteNode(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
providerID string
|
||||
scalingGroupID string
|
||||
getInstanceErr error
|
||||
deleteInstanceErr error
|
||||
wantErr bool
|
||||
}{
|
||||
"node is deleted": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
},
|
||||
"splitting providerID fails": {
|
||||
providerID: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
"get instance fails": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
getInstanceErr: errors.New("get instance error"),
|
||||
wantErr: true,
|
||||
},
|
||||
"splitting scalingGroupID fails": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
scalingGroupID: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
"delete instance fails": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
deleteInstanceErr: errors.New("delete instance error"),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
client := Client{
|
||||
instanceGroupManagersAPI: &stubInstanceGroupManagersAPI{
|
||||
deleteInstancesErr: tc.deleteInstanceErr,
|
||||
},
|
||||
instanceAPI: &stubInstanceAPI{
|
||||
getErr: tc.getInstanceErr,
|
||||
instance: &computepb.Instance{
|
||||
Metadata: &computepb.Metadata{
|
||||
Items: []*computepb.Items{
|
||||
{Key: proto.String("created-by"), Value: &tc.scalingGroupID},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
err := client.DeleteNode(context.Background(), tc.providerID)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/edgelesssys/constellation/operators/constellation-node-operator/api/v1alpha1"
|
||||
updatev1alpha1 "github.com/edgelesssys/constellation/operators/constellation-node-operator/api/v1alpha1"
|
||||
"google.golang.org/api/googleapi"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
)
|
||||
|
||||
// GetNodeState returns the state of the node.
|
||||
func (c *Client) GetNodeState(ctx context.Context, providerID string) (updatev1alpha1.CSPNodeState, error) {
|
||||
project, zone, instanceName, err := splitProviderID(providerID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
instance, err := c.instanceAPI.Get(ctx, &computepb.GetInstanceRequest{
|
||||
Instance: instanceName,
|
||||
Project: project,
|
||||
Zone: zone,
|
||||
})
|
||||
if err != nil {
|
||||
var apiErr *googleapi.Error
|
||||
if errors.As(err, &apiErr) {
|
||||
if apiErr.Code == http.StatusNotFound {
|
||||
return v1alpha1.NodeStateTerminated, nil
|
||||
}
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
if instance.Status == nil {
|
||||
return v1alpha1.NodeStateUnknown, nil
|
||||
}
|
||||
|
||||
// reference: https://cloud.google.com/compute/docs/instances/instance-life-cycle
|
||||
switch *instance.Status {
|
||||
case computepb.Instance_PROVISIONING.String():
|
||||
fallthrough
|
||||
case computepb.Instance_STAGING.String():
|
||||
return v1alpha1.NodeStateCreating, nil
|
||||
case computepb.Instance_RUNNING.String():
|
||||
return v1alpha1.NodeStateReady, nil
|
||||
case computepb.Instance_STOPPING.String():
|
||||
fallthrough
|
||||
case computepb.Instance_SUSPENDING.String():
|
||||
fallthrough
|
||||
case computepb.Instance_SUSPENDED.String():
|
||||
fallthrough
|
||||
case computepb.Instance_REPAIRING.String():
|
||||
fallthrough
|
||||
case computepb.Instance_TERMINATED.String(): // this is stopped in GCP terms
|
||||
return v1alpha1.NodeStateStopped, nil
|
||||
}
|
||||
return v1alpha1.NodeStateUnknown, nil
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
updatev1alpha1 "github.com/edgelesssys/constellation/operators/constellation-node-operator/api/v1alpha1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/api/googleapi"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestGetNodeState(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
providerID string
|
||||
getInstanceErr error
|
||||
instanceStatus *string
|
||||
wantNodeState updatev1alpha1.CSPNodeState
|
||||
wantErr bool
|
||||
}{
|
||||
"node is deleted and API returns 404": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
getInstanceErr: &googleapi.Error{
|
||||
Code: http.StatusNotFound,
|
||||
},
|
||||
wantNodeState: updatev1alpha1.NodeStateTerminated,
|
||||
},
|
||||
"splitting providerID fails": {
|
||||
providerID: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
"node is deleted and API returns other error": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
getInstanceErr: errors.New("get instance error"),
|
||||
wantErr: true,
|
||||
},
|
||||
"instance has no status": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
wantNodeState: updatev1alpha1.NodeStateUnknown,
|
||||
},
|
||||
"instance is provisioning": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
instanceStatus: proto.String("PROVISIONING"),
|
||||
wantNodeState: updatev1alpha1.NodeStateCreating,
|
||||
},
|
||||
"instance is staging": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
instanceStatus: proto.String("STAGING"),
|
||||
wantNodeState: updatev1alpha1.NodeStateCreating,
|
||||
},
|
||||
"instance is running": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
instanceStatus: proto.String("RUNNING"),
|
||||
wantNodeState: updatev1alpha1.NodeStateReady,
|
||||
},
|
||||
"instance is stopping": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
instanceStatus: proto.String("STOPPING"),
|
||||
wantNodeState: updatev1alpha1.NodeStateStopped,
|
||||
},
|
||||
"instance is suspending": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
instanceStatus: proto.String("SUSPENDING"),
|
||||
wantNodeState: updatev1alpha1.NodeStateStopped,
|
||||
},
|
||||
"instance is suspended": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
instanceStatus: proto.String("SUSPENDED"),
|
||||
wantNodeState: updatev1alpha1.NodeStateStopped,
|
||||
},
|
||||
"instance is repairing": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
instanceStatus: proto.String("REPAIRING"),
|
||||
wantNodeState: updatev1alpha1.NodeStateStopped,
|
||||
},
|
||||
"instance terminated": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
instanceStatus: proto.String("TERMINATED"),
|
||||
wantNodeState: updatev1alpha1.NodeStateStopped,
|
||||
},
|
||||
"instance state unknown": {
|
||||
providerID: "gce://project/zone/instance-name",
|
||||
instanceStatus: proto.String("unknown"),
|
||||
wantNodeState: updatev1alpha1.NodeStateUnknown,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
client := Client{
|
||||
instanceAPI: &stubInstanceAPI{
|
||||
getErr: tc.getInstanceErr,
|
||||
instance: &computepb.Instance{
|
||||
Status: tc.instanceStatus,
|
||||
},
|
||||
},
|
||||
}
|
||||
nodeState, err := client.GetNodeState(context.Background(), tc.providerID)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantNodeState, nodeState)
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var providerIDRegex = regexp.MustCompile(`^gce://([^/]+)/([^/]+)/([^/]+)$`)
|
||||
|
||||
// splitProviderID splits a provider's id into core components.
|
||||
// A providerID is build after the schema 'gce://<project-id>/<zone>/<instance-name>'
|
||||
func splitProviderID(providerID string) (project, zone, instance string, err error) {
|
||||
matches := providerIDRegex.FindStringSubmatch(providerID)
|
||||
|
||||
if len(matches) != 4 {
|
||||
return "", "", "", fmt.Errorf("splitting providerID: %q. matches: %v", providerID, matches)
|
||||
}
|
||||
return matches[1], matches[2], matches[3], nil
|
||||
}
|
||||
|
||||
// joinProviderID builds a k8s provider ID for GCP instances.
|
||||
// A providerID is build after the schema 'gce://<project-id>/<zone>/<instance-name>'
|
||||
func joinProviderID(project, zone, instanceName string) string {
|
||||
return fmt.Sprintf("gce://%v/%v/%v", project, zone, instanceName)
|
||||
}
|
||||
|
||||
// joinInstanceID builds a gcp instance ID from the zone and instance name.
|
||||
func joinInstanceID(zone, instanceName string) string {
|
||||
return fmt.Sprintf("zones/%v/instances/%v", zone, instanceName)
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSplitProviderID(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
providerID string
|
||||
wantProjectID string
|
||||
wantZone string
|
||||
wantInstance string
|
||||
wantErr bool
|
||||
}{
|
||||
"simple id": {
|
||||
providerID: "gce://someProject/someZone/someInstance",
|
||||
wantProjectID: "someProject",
|
||||
wantZone: "someZone",
|
||||
wantInstance: "someInstance",
|
||||
},
|
||||
"incomplete id": {
|
||||
providerID: "gce://someProject/someZone",
|
||||
wantErr: true,
|
||||
},
|
||||
"wrong provider": {
|
||||
providerID: "azure://someProject/someZone/someInstance",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
projectID, zone, instance, err := splitProviderID(tc.providerID)
|
||||
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
assert.NoError(err)
|
||||
assert.Equal(tc.wantProjectID, projectID)
|
||||
assert.Equal(tc.wantZone, zone)
|
||||
assert.Equal(tc.wantInstance, instance)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinProviderID(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
projectID string
|
||||
zone string
|
||||
instance string
|
||||
wantProviderID string
|
||||
}{
|
||||
"simple id": {
|
||||
projectID: "someProject",
|
||||
zone: "someZone",
|
||||
instance: "someInstance",
|
||||
wantProviderID: "gce://someProject/someZone/someInstance",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
providerID := joinProviderID(tc.projectID, tc.zone, tc.instance)
|
||||
|
||||
assert.Equal(tc.wantProviderID, providerID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoinnstanceID(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
zone string
|
||||
instanceName string
|
||||
wantInstanceID string
|
||||
}{
|
||||
"simple id": {
|
||||
zone: "someZone",
|
||||
instanceName: "someInstance",
|
||||
wantInstanceID: "zones/someZone/instances/someInstance",
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
providerID := joinInstanceID(tc.zone, tc.instanceName)
|
||||
|
||||
assert.Equal(tc.wantInstanceID, providerID)
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
)
|
||||
|
||||
// GetScalingGroupImage returns the image URI of the scaling group.
|
||||
func (c *Client) GetScalingGroupImage(ctx context.Context, scalingGroupID string) (string, error) {
|
||||
instanceTemplate, err := c.getScalingGroupTemplate(ctx, scalingGroupID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return instanceTemplateSourceImage(instanceTemplate)
|
||||
}
|
||||
|
||||
// SetScalingGroupImage sets the image URI of the scaling group.
|
||||
func (c *Client) SetScalingGroupImage(ctx context.Context, scalingGroupID, imageURI string) error {
|
||||
project, zone, instanceGroupName, err := splitInstanceGroupID(scalingGroupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// get current template
|
||||
instanceTemplate, err := c.getScalingGroupTemplate(ctx, scalingGroupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// check if template already uses the same image
|
||||
oldImageURI, err := instanceTemplateSourceImage(instanceTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if oldImageURI == imageURI {
|
||||
return nil
|
||||
}
|
||||
|
||||
// clone template with desired image
|
||||
if instanceTemplate.Name == nil {
|
||||
return fmt.Errorf("instance template of scaling group %q has no name", scalingGroupID)
|
||||
}
|
||||
instanceTemplate.Properties.Disks[0].InitializeParams.SourceImage = &imageURI
|
||||
newTemplateName, err := generateInstanceTemplateName(*instanceTemplate.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
instanceTemplate.Name = &newTemplateName
|
||||
op, err := c.instanceTemplateAPI.Insert(ctx, &computepb.InsertInstanceTemplateRequest{
|
||||
Project: project,
|
||||
InstanceTemplateResource: instanceTemplate,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("cloning instance template: %w", err)
|
||||
}
|
||||
if err := op.Wait(ctx); err != nil {
|
||||
return fmt.Errorf("waiting for cloned instance template: %w", err)
|
||||
}
|
||||
|
||||
newTemplateURI := joinInstanceTemplateURI(project, newTemplateName)
|
||||
// update instance group manager to use new template
|
||||
op, err = c.instanceGroupManagersAPI.SetInstanceTemplate(ctx, &computepb.SetInstanceTemplateInstanceGroupManagerRequest{
|
||||
InstanceGroupManager: instanceGroupName,
|
||||
Project: project,
|
||||
Zone: zone,
|
||||
InstanceGroupManagersSetInstanceTemplateRequestResource: &computepb.InstanceGroupManagersSetInstanceTemplateRequest{
|
||||
InstanceTemplate: &newTemplateURI,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting instance template: %w", err)
|
||||
}
|
||||
if err := op.Wait(ctx); err != nil {
|
||||
return fmt.Errorf("waiting for setting instance template: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) getScalingGroupTemplate(ctx context.Context, scalingGroupID string) (*computepb.InstanceTemplate, error) {
|
||||
project, zone, instanceGroupName, err := splitInstanceGroupID(scalingGroupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instanceGroupManager, err := c.instanceGroupManagersAPI.Get(ctx, &computepb.GetInstanceGroupManagerRequest{
|
||||
InstanceGroupManager: instanceGroupName,
|
||||
Project: project,
|
||||
Zone: zone,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting instance group manager %q: %w", instanceGroupName, err)
|
||||
}
|
||||
if instanceGroupManager.InstanceTemplate == nil {
|
||||
return nil, fmt.Errorf("instance group manager %q has no instance template", instanceGroupName)
|
||||
}
|
||||
instanceTemplateProject, instanceTemplateName, err := splitInstanceTemplateID(uriNormalize(*instanceGroupManager.InstanceTemplate))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("splitting instance template name: %w", err)
|
||||
}
|
||||
instanceTemplate, err := c.instanceTemplateAPI.Get(ctx, &computepb.GetInstanceTemplateRequest{
|
||||
InstanceTemplate: instanceTemplateName,
|
||||
Project: instanceTemplateProject,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getting instance template %q: %w", instanceTemplateName, err)
|
||||
}
|
||||
return instanceTemplate, nil
|
||||
}
|
||||
|
||||
func instanceTemplateSourceImage(instanceTemplate *computepb.InstanceTemplate) (string, error) {
|
||||
if instanceTemplate.Properties == nil ||
|
||||
len(instanceTemplate.Properties.Disks) == 0 ||
|
||||
instanceTemplate.Properties.Disks[0].InitializeParams == nil ||
|
||||
instanceTemplate.Properties.Disks[0].InitializeParams.SourceImage == nil {
|
||||
return "", errors.New("instance template has no source image")
|
||||
}
|
||||
return uriNormalize(*instanceTemplate.Properties.Disks[0].InitializeParams.SourceImage), nil
|
||||
}
|
@ -0,0 +1,284 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
computepb "google.golang.org/genproto/googleapis/cloud/compute/v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestGetScalingGroupImage(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
scalingGroupID string
|
||||
instanceGroupManagerTemplateID *string
|
||||
instanceTemplate *computepb.InstanceTemplate
|
||||
getInstanceGroupManagerErr error
|
||||
getInstanceTemplateErr error
|
||||
wantImage string
|
||||
wantErr bool
|
||||
}{
|
||||
"getting image works": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
instanceGroupManagerTemplateID: proto.String("projects/project/global/instanceTemplates/instance-template"),
|
||||
instanceTemplate: &computepb.InstanceTemplate{
|
||||
Properties: &computepb.InstanceProperties{
|
||||
Disks: []*computepb.AttachedDisk{
|
||||
{
|
||||
InitializeParams: &computepb.AttachedDiskInitializeParams{
|
||||
SourceImage: proto.String("https://www.googleapis.com/compute/v1/projects/project/global/images/image"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantImage: "projects/project/global/images/image",
|
||||
},
|
||||
"splitting scalingGroupID fails": {
|
||||
scalingGroupID: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
"get instance fails": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
getInstanceGroupManagerErr: errors.New("get instance error"),
|
||||
wantErr: true,
|
||||
},
|
||||
"instance group manager has no template": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
wantErr: true,
|
||||
},
|
||||
"instance group manager template id is invalid": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
instanceGroupManagerTemplateID: proto.String("invalid"),
|
||||
wantErr: true,
|
||||
},
|
||||
"get instance template fails": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
instanceGroupManagerTemplateID: proto.String("projects/project/global/instanceTemplates/instance-template"),
|
||||
getInstanceTemplateErr: errors.New("get instance template error"),
|
||||
wantErr: true,
|
||||
},
|
||||
"instance template has no disks": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
instanceGroupManagerTemplateID: proto.String("projects/project/global/instanceTemplates/instance-template"),
|
||||
instanceTemplate: &computepb.InstanceTemplate{
|
||||
Properties: &computepb.InstanceProperties{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
client := Client{
|
||||
instanceGroupManagersAPI: &stubInstanceGroupManagersAPI{
|
||||
getErr: tc.getInstanceGroupManagerErr,
|
||||
instanceGroupManager: &computepb.InstanceGroupManager{
|
||||
InstanceTemplate: tc.instanceGroupManagerTemplateID,
|
||||
},
|
||||
},
|
||||
instanceTemplateAPI: &stubInstanceTemplateAPI{
|
||||
getErr: tc.getInstanceTemplateErr,
|
||||
template: tc.instanceTemplate,
|
||||
},
|
||||
}
|
||||
gotImage, err := client.GetScalingGroupImage(context.Background(), tc.scalingGroupID)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
assert.Equal(tc.wantImage, gotImage)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetScalingGroupImage(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
scalingGroupID string
|
||||
imageURI string
|
||||
instanceGroupManagerTemplateID *string
|
||||
instanceTemplate *computepb.InstanceTemplate
|
||||
getInstanceGroupManagerErr error
|
||||
getInstanceTemplateErr error
|
||||
setInstanceTemplateErr error
|
||||
insertInstanceTemplateErr error
|
||||
wantErr bool
|
||||
}{
|
||||
"setting image works": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
imageURI: "projects/project/global/images/image-2",
|
||||
instanceGroupManagerTemplateID: proto.String("projects/project/global/instanceTemplates/instance-template"),
|
||||
instanceTemplate: &computepb.InstanceTemplate{
|
||||
Name: proto.String("instance-template"),
|
||||
Properties: &computepb.InstanceProperties{
|
||||
Disks: []*computepb.AttachedDisk{
|
||||
{
|
||||
InitializeParams: &computepb.AttachedDiskInitializeParams{
|
||||
SourceImage: proto.String("https://www.googleapis.com/compute/v1/projects/project/global/images/image-1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"same image already in use": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
imageURI: "projects/project/global/images/image",
|
||||
instanceGroupManagerTemplateID: proto.String("projects/project/global/instanceTemplates/instance-template"),
|
||||
instanceTemplate: &computepb.InstanceTemplate{
|
||||
Name: proto.String("instance-template"),
|
||||
Properties: &computepb.InstanceProperties{
|
||||
Disks: []*computepb.AttachedDisk{
|
||||
{
|
||||
InitializeParams: &computepb.AttachedDiskInitializeParams{
|
||||
SourceImage: proto.String("https://www.googleapis.com/compute/v1/projects/project/global/images/image"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// will not be triggered
|
||||
insertInstanceTemplateErr: errors.New("insert instance template error"),
|
||||
},
|
||||
"splitting scalingGroupID fails": {
|
||||
scalingGroupID: "invalid",
|
||||
wantErr: true,
|
||||
},
|
||||
"get instance fails": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
getInstanceGroupManagerErr: errors.New("get instance error"),
|
||||
wantErr: true,
|
||||
},
|
||||
"instance group manager has no template": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
wantErr: true,
|
||||
},
|
||||
"instance group manager template id is invalid": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
instanceGroupManagerTemplateID: proto.String("invalid"),
|
||||
wantErr: true,
|
||||
},
|
||||
"get instance template fails": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
instanceGroupManagerTemplateID: proto.String("projects/project/global/instanceTemplates/instance-template"),
|
||||
getInstanceTemplateErr: errors.New("get instance template error"),
|
||||
wantErr: true,
|
||||
},
|
||||
"instance template has no disks": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
instanceGroupManagerTemplateID: proto.String("projects/project/global/instanceTemplates/instance-template"),
|
||||
instanceTemplate: &computepb.InstanceTemplate{
|
||||
Properties: &computepb.InstanceProperties{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
"instance template has no name": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
imageURI: "projects/project/global/images/image-2",
|
||||
instanceGroupManagerTemplateID: proto.String("projects/project/global/instanceTemplates/instance-template"),
|
||||
instanceTemplate: &computepb.InstanceTemplate{
|
||||
Properties: &computepb.InstanceProperties{
|
||||
Disks: []*computepb.AttachedDisk{
|
||||
{
|
||||
InitializeParams: &computepb.AttachedDiskInitializeParams{
|
||||
SourceImage: proto.String("https://www.googleapis.com/compute/v1/projects/project/global/images/image-1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
"instance template name generation fails": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
imageURI: "projects/project/global/images/image-2",
|
||||
instanceGroupManagerTemplateID: proto.String("projects/project/global/instanceTemplates/instance-template"),
|
||||
instanceTemplate: &computepb.InstanceTemplate{
|
||||
Name: proto.String("instance-template-999999999999999999999"),
|
||||
Properties: &computepb.InstanceProperties{
|
||||
Disks: []*computepb.AttachedDisk{
|
||||
{
|
||||
InitializeParams: &computepb.AttachedDiskInitializeParams{
|
||||
SourceImage: proto.String("https://www.googleapis.com/compute/v1/projects/project/global/images/image-1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
"instance template insert fails": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
imageURI: "projects/project/global/images/image-2",
|
||||
instanceGroupManagerTemplateID: proto.String("projects/project/global/instanceTemplates/instance-template"),
|
||||
instanceTemplate: &computepb.InstanceTemplate{
|
||||
Name: proto.String("instance-template"),
|
||||
Properties: &computepb.InstanceProperties{
|
||||
Disks: []*computepb.AttachedDisk{
|
||||
{
|
||||
InitializeParams: &computepb.AttachedDiskInitializeParams{
|
||||
SourceImage: proto.String("https://www.googleapis.com/compute/v1/projects/project/global/images/image-1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
insertInstanceTemplateErr: errors.New("insert instance template error"),
|
||||
wantErr: true,
|
||||
},
|
||||
"setting instance template fails": {
|
||||
scalingGroupID: "projects/project/zones/zone/instanceGroupManagers/instance-group",
|
||||
imageURI: "projects/project/global/images/image-2",
|
||||
instanceGroupManagerTemplateID: proto.String("projects/project/global/instanceTemplates/instance-template"),
|
||||
instanceTemplate: &computepb.InstanceTemplate{
|
||||
Name: proto.String("instance-template"),
|
||||
Properties: &computepb.InstanceProperties{
|
||||
Disks: []*computepb.AttachedDisk{
|
||||
{
|
||||
InitializeParams: &computepb.AttachedDiskInitializeParams{
|
||||
SourceImage: proto.String("https://www.googleapis.com/compute/v1/projects/project/global/images/image-1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
setInstanceTemplateErr: errors.New("setting instance template error"),
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
require := require.New(t)
|
||||
|
||||
client := Client{
|
||||
instanceGroupManagersAPI: &stubInstanceGroupManagersAPI{
|
||||
getErr: tc.getInstanceGroupManagerErr,
|
||||
setInstanceTemplateErr: tc.setInstanceTemplateErr,
|
||||
instanceGroupManager: &computepb.InstanceGroupManager{
|
||||
InstanceTemplate: tc.instanceGroupManagerTemplateID,
|
||||
},
|
||||
},
|
||||
instanceTemplateAPI: &stubInstanceTemplateAPI{
|
||||
getErr: tc.getInstanceTemplateErr,
|
||||
insertErr: tc.insertInstanceTemplateErr,
|
||||
template: tc.instanceTemplate,
|
||||
},
|
||||
}
|
||||
err := client.SetScalingGroupImage(context.Background(), tc.scalingGroupID, tc.imageURI)
|
||||
if tc.wantErr {
|
||||
assert.Error(err)
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
@ -19,6 +19,8 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
gcpclient "github.com/edgelesssys/constellation/operators/constellation-node-operator/internal/gcp/client"
|
||||
|
||||
updatev1alpha1 "github.com/edgelesssys/constellation/operators/constellation-node-operator/api/v1alpha1"
|
||||
"github.com/edgelesssys/constellation/operators/constellation-node-operator/controllers"
|
||||
"github.com/edgelesssys/constellation/operators/constellation-node-operator/internal/etcd"
|
||||
@ -58,11 +60,16 @@ func main() {
|
||||
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
|
||||
|
||||
var cspClient cspAPI
|
||||
var clientErr error
|
||||
switch strings.ToLower(csp) {
|
||||
case "azure":
|
||||
panic("Azure is not supported yet")
|
||||
case "gcp":
|
||||
panic("GCP is not supported yet")
|
||||
cspClient, clientErr = gcpclient.New(context.Background())
|
||||
if clientErr != nil {
|
||||
setupLog.Error(clientErr, "unable to create GCP client")
|
||||
os.Exit(1)
|
||||
}
|
||||
default:
|
||||
setupLog.Info("Unknown CSP", "csp", csp)
|
||||
os.Exit(1)
|
||||
|
Loading…
Reference in New Issue
Block a user