rpcapi: Emit top-level tracing spans for requests

rpcapi is a normal gRPC server once it's running, so if the overall
Terraform CLI process has been configured to export telemetry then this
will emit a span for each incoming gRPC request.

In later commits we'll hopefully add further telemetry downstream to cover
interesting child spans such as running Terraform Core operations, but
this is at least enough to observe which requests a client is making
and what their outcomes were.
pull/34738/head
Martin Atkins 3 years ago
parent a246e05036
commit e8e20a2b0a

@ -1,7 +1,7 @@
module github.com/hashicorp/terraform
require (
cloud.google.com/go/kms v1.12.1
cloud.google.com/go/kms v1.15.0
cloud.google.com/go/storage v1.30.1
github.com/Azure/azure-sdk-for-go v59.2.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.27
@ -32,7 +32,7 @@ require (
github.com/go-test/deep v1.0.3
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.3.0
github.com/google/uuid v1.3.1
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.40
github.com/hashicorp/consul/api v1.13.0
github.com/hashicorp/consul/sdk v0.8.0
@ -46,6 +46,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-plugin v1.4.3
github.com/hashicorp/go-retryablehttp v0.7.4
github.com/hashicorp/go-slug v0.12.2
github.com/hashicorp/go-tfe v1.34.0
github.com/hashicorp/go-uuid v1.0.3
github.com/hashicorp/go-version v1.6.0
@ -87,6 +88,7 @@ require (
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b
github.com/zclconf/go-cty-yaml v1.0.3
go.opentelemetry.io/contrib/exporters/autoexport v0.45.0
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0
go.opentelemetry.io/otel v1.20.0
go.opentelemetry.io/otel/sdk v1.20.0
go.opentelemetry.io/otel/trace v1.20.0
@ -94,15 +96,15 @@ require (
golang.org/x/exp v0.0.0-20230905200255-921286631fa9
golang.org/x/mod v0.12.0
golang.org/x/net v0.17.0
golang.org/x/oauth2 v0.10.0
golang.org/x/oauth2 v0.11.0
golang.org/x/sys v0.14.0
golang.org/x/term v0.13.0
golang.org/x/text v0.14.0
golang.org/x/tools v0.13.0
golang.org/x/tools/cmd/cover v0.1.0-deprecated
google.golang.org/api v0.126.0
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98
google.golang.org/grpc v1.58.3
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d
google.golang.org/grpc v1.59.0
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0
google.golang.org/protobuf v1.31.0
honnef.co/go/tools v0.5.0-0.dev.0.20230826160118-ad5ca31ff221
@ -113,8 +115,8 @@ require (
)
require (
cloud.google.com/go v0.110.4 // indirect
cloud.google.com/go/compute v1.21.0 // indirect
cloud.google.com/go v0.110.7 // indirect
cloud.google.com/go/compute v1.23.0 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.1.1 // indirect
github.com/AlecAivazis/survey/v2 v2.3.6 // indirect
@ -200,7 +202,6 @@ require (
github.com/hashicorp/go-msgpack v0.5.4 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-slug v0.12.2 // indirect
github.com/hashicorp/golang-lru v0.5.1 // indirect
github.com/hashicorp/serf v0.9.6 // indirect
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
@ -261,8 +262,8 @@ require (
golang.org/x/time v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

@ -32,8 +32,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9
cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk=
cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
cloud.google.com/go v0.110.7 h1:rJyC7nWRg2jWGZ4wSJ5nY65GTdYJkg0cd/uXb+ACI6o=
cloud.google.com/go v0.110.7/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=
cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=
cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=
@ -70,8 +70,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU=
cloud.google.com/go/compute v1.21.0 h1:JNBsyXVoOoNJtTQcnEY5uYpZIbeCTYIeDe0Xh1bySMk=
cloud.google.com/go/compute v1.21.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I=
@ -113,8 +113,8 @@ cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp
cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=
cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
cloud.google.com/go/kms v1.12.1 h1:xZmZuwy2cwzsocmKDOPu4BL7umg8QXagQx6fKVmf45U=
cloud.google.com/go/kms v1.12.1/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM=
cloud.google.com/go/kms v1.15.0 h1:xYl5WEaSekKYN5gGRyhjvZKM22GVBBCzegGNVPy+aIs=
cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM=
cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=
@ -425,6 +425,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH
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/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d h1:t5Wuyh53qYyg9eqn4BbnlIT+vmhyww0TatL+zT3uWgI=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
@ -461,6 +463,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m
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/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
@ -522,8 +526,8 @@ github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -619,8 +623,9 @@ github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/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/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
@ -1082,6 +1087,8 @@ go.opentelemetry.io/contrib/exporters/autoexport v0.45.0 h1:KU3hwb3O+fc2F15lltmD
go.opentelemetry.io/contrib/exporters/autoexport v0.45.0/go.mod h1:9hFI4YY6Ehe9enzw9qGlKAjJGQAtEo75Ysrb3byOZtI=
go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws v0.46.0 h1:qmQcwJOEOfNvVOD8H7bVAEipp+6UtnDK3qHGCcjwB9o=
go.opentelemetry.io/contrib/instrumentation/github.com/aws/aws-sdk-go-v2/otelaws v0.46.0/go.mod h1:d1FGIeeryqx0a2Oa5oQrK1Ug85AGfFUx+nMtoAwJ4VI=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M=
go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc=
go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
@ -1262,8 +1269,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
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=
@ -1644,12 +1651,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw
google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY=
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q=
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@ -1688,8 +1695,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ=
google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE=
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=

@ -5,6 +5,7 @@ import (
"net"
"testing"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/test/bufconn"
)
@ -19,7 +20,10 @@ import (
// server end of this fake connection.
func grpcClientForTesting(ctx context.Context, t *testing.T, registerServices func(srv *grpc.Server)) (conn grpc.ClientConnInterface, close func()) {
fakeListener := bufconn.Listen(1024 /* buffer size */)
srv := grpc.NewServer()
srv := grpc.NewServer(
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor()),
)
// Caller gets an opportunity to register specific services before
// we actually start "serving".
@ -40,6 +44,8 @@ func grpcClientForTesting(ctx context.Context, t *testing.T, registerServices fu
ctx, "testfake",
grpc.WithContextDialer(fakeDialer),
grpc.WithInsecure(),
grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),
grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor()),
)
if err != nil {
t.Fatalf("failed to connect to the fake server: %s", err)

@ -6,6 +6,7 @@ import (
"os"
"github.com/hashicorp/go-plugin"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"google.golang.org/grpc"
)
@ -34,7 +35,12 @@ func ServePlugin(ctx context.Context, opts ServerOpts) error {
},
},
GRPCServer: func(opts []grpc.ServerOption) *grpc.Server {
server := grpc.NewServer(opts...)
fullOpts := []grpc.ServerOption{
grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor()),
}
fullOpts = append(fullOpts, opts...)
server := grpc.NewServer(fullOpts...)
// We'll also monitor the given context for cancellation
// and terminate the server gracefully if we get cancelled.
go func() {

@ -5,6 +5,7 @@ import (
"sync"
"github.com/hashicorp/terraform/internal/rpcapi/terraform1"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
@ -39,7 +40,13 @@ func (s *setupServer) Handshake(ctx context.Context, req *terraform1.Handshake_R
return nil, status.Error(codes.FailedPrecondition, "handshake already completed")
}
serverCaps, err := s.initOthers(ctx, req.Capabilities)
var serverCaps *terraform1.ServerCapabilities
var err error
{
ctx, span := tracer.Start(ctx, "initialize RPC services")
serverCaps, err = s.initOthers(ctx, req.Capabilities)
span.End()
}
s.initOthers = nil // cannot handshake again
if err != nil {
return nil, err

@ -0,0 +1,20 @@
package rpcapi
import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/trace"
)
// tracer is the OpenTelemetry tracer to use for tracing for code in this
// package.
//
// When creating tracing spans in gRPC service functions, always use the
// a [context.Context] descended from the one passed in to the service
// function so that the spans can attach to the automatically-generated
// server request span and, if the client is also using OpenTelemetry,
// to the client's request span.
var tracer trace.Tracer
func init() {
tracer = otel.Tracer("github.com/hashicorp/terraform/internal/rpcapi")
}

@ -0,0 +1,297 @@
package rpcapi
import (
"context"
"testing"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform/internal/rpcapi/terraform1"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/instrumentation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
)
// initTelemetryForTest configures OpenTelemetry to collect spans into a
// local in-memory buffer and returns an object that provides access to that
// buffer.
//
// The OpenTelemetry tracer provider is a global cross-cutting concern shared
// throughout the program, so it isn't valid to use this function in any test
// that calls t.Parallel, or in subtests of a parent test that has already
// used this function.
func initTelemetryForTest(t *testing.T, providerOptions ...sdktrace.TracerProviderOption) *tracetest.InMemoryExporter {
t.Helper()
exp := tracetest.NewInMemoryExporter()
sp := sdktrace.NewSimpleSpanProcessor(exp)
providerOptions = append(
[]sdktrace.TracerProviderOption{
sdktrace.WithSpanProcessor(sp),
},
providerOptions...,
)
provider := sdktrace.NewTracerProvider(providerOptions...)
otel.SetTracerProvider(provider)
pgtr := propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})
otel.SetTextMapPropagator(pgtr)
// We'll automatically shut down the provider at the end of the test run,
// because otherwise a subsequent test which runs something that generates
// telemetry _without_ calling initTelemetryForTest (which is optional)
// could end up appending irrelevant spans to an earlier test's exporter.
t.Cleanup(func() {
provider.Shutdown(context.Background())
otel.SetTracerProvider(nil)
otel.SetTextMapPropagator(nil)
})
t.Log("OpenTelemetry initialized")
return exp
}
// findTestTelemetrySpan tests each of the spans that have been reported to the
// given [tracetest.InMemoryExporter] with the given predicate function and
// returns the first one for which the predicate matches.
//
// If the predicate returns false for all spans then this function will fail
// the test using the given [testing.T].
func findTestTelemetrySpan(t *testing.T, exp *tracetest.InMemoryExporter, predicate func(tracetest.SpanStub) bool) tracetest.SpanStub {
for _, span := range exp.GetSpans() {
if predicate(span) {
return span
}
}
t.Fatal("no spans matched the predicate")
return tracetest.SpanStub{}
}
// findTestTelemetrySpans tests each of the spans that have been reported to the
// given [tracetest.InMemoryExporter] with the given predicate function and
// returns only those for which the predicate matches.
//
// If no spans match at all then the result is a zero-length slice. If you are
// expecting to find exactly one matching span then [findTestTelemetrySpan]
// (singular) might be more convenient.
func findTestTelemetrySpans(t *testing.T, exp *tracetest.InMemoryExporter, predicate func(tracetest.SpanStub) bool) tracetest.SpanStubs {
var ret tracetest.SpanStubs
for _, span := range exp.GetSpans() {
if predicate(span) {
ret = append(ret, span)
}
}
return ret
}
// overwriteTestSpanTimestamps overwrites the timestamps in all of the given
// spans to be exactly the given fakeTime, as a way to avoid considering exact
// timestamps when comparing actual spans with desired spans.
//
// This function overwrites both the start and end times of the spans themselves
// and also the timestamps of any events associated with the spans.
func overwriteTestSpanTimestamps(spans tracetest.SpanStubs, fakeTime time.Time) {
for i := range spans {
spans[i].StartTime = fakeTime
spans[i].EndTime = fakeTime
for j := range spans[i].Events {
spans[i].Events[j].Time = fakeTime
}
}
}
func fixedTraceID(n uint32) trace.TraceID {
return trace.TraceID{
0xfe, 0xed, 0xfa, 0xce,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
uint8(n >> 24), uint8(n >> 16), uint8(n >> 8), uint8(n >> 0),
}
}
func fixedSpanID(n uint32) trace.SpanID {
return trace.SpanID{
0xfa, 0xce, 0xfe, 0xed,
uint8(n >> 24), uint8(n >> 16), uint8(n >> 8), uint8(n >> 0),
}
}
func TestTelemetryInTests(t *testing.T) {
ctx := context.Background()
testResource := resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("telemetry test"),
semconv.ServiceVersionKey.String("1.2.3"),
)
telemetry := initTelemetryForTest(t,
sdktrace.WithResource(testResource),
)
var parentSpanContext, childSpanContext trace.SpanContext
tracer := otel.Tracer("test thingy")
{
ctx, parentSpan := tracer.Start(ctx, "parent span")
parentSpanContext = parentSpan.SpanContext()
{
_, childSpan := tracer.Start(ctx, "child span")
childSpanContext = childSpan.SpanContext()
childSpan.AddEvent("did something totally hilarious")
childSpan.SetStatus(codes.Error, "it went wrong")
childSpan.End()
}
parentSpan.End()
}
gotSpans := telemetry.GetSpans()
// The spans contain real timestamps that make them annoying to compare,
// so we'll just replace those with fixed timestamps so we can easily
// compare everything else.
fakeTime := time.Now()
overwriteTestSpanTimestamps(gotSpans, fakeTime)
wantSpans := tracetest.SpanStubs{
// These are ordered by the calls to Span.End above, so child should
// always appear first. (That's a detail of this in-memory-only
// exporter, not a general guarantee about OpenTracing.)
{
Name: "child span",
SpanContext: childSpanContext,
Parent: parentSpanContext,
SpanKind: trace.SpanKindInternal,
StartTime: fakeTime,
EndTime: fakeTime,
Events: []sdktrace.Event{
{
Name: "did something totally hilarious",
Time: fakeTime,
},
},
Status: sdktrace.Status{
Code: codes.Error,
Description: "it went wrong",
},
Resource: testResource,
InstrumentationLibrary: instrumentation.Scope{
Name: "test thingy",
},
},
{
Name: "parent span",
SpanContext: parentSpanContext,
SpanKind: trace.SpanKindInternal,
StartTime: fakeTime,
EndTime: fakeTime,
ChildSpanCount: 1,
Resource: testResource,
InstrumentationLibrary: instrumentation.Scope{
Name: "test thingy",
},
},
}
if diff := cmp.Diff(wantSpans, gotSpans); diff != "" {
t.Errorf("wrong spans\n%s", diff)
}
}
func TestTelemetryInTestsGRPC(t *testing.T) {
ctx := context.Background()
testResource := resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceNameKey.String("TestTelemetryInTestsGRPC"),
)
telemetry := initTelemetryForTest(t,
sdktrace.WithResource(testResource),
)
client, close := grpcClientForTesting(ctx, t, func(srv *grpc.Server) {
setup := &setupServer{
initOthers: func(ctx context.Context, cc *terraform1.ClientCapabilities) (*terraform1.ServerCapabilities, error) {
return &terraform1.ServerCapabilities{}, nil
},
}
terraform1.RegisterSetupServer(srv, setup)
})
defer close()
setupClient := terraform1.NewSetupClient(client)
{
ctx, span := otel.Tracer("TestTelemetryInTestsGRPC").Start(ctx, "root")
_, err := setupClient.Handshake(ctx, &terraform1.Handshake_Request{
Capabilities: &terraform1.ClientCapabilities{},
})
if err != nil {
t.Fatal(err)
}
span.End()
}
clientSpan := findTestTelemetrySpan(t, telemetry, func(ss tracetest.SpanStub) bool {
return ss.SpanKind == trace.SpanKindClient
})
serverSpan := findTestTelemetrySpan(t, telemetry, func(ss tracetest.SpanStub) bool {
return ss.SpanKind == trace.SpanKindServer
})
t.Run("client span", func(t *testing.T) {
span := clientSpan
t.Logf("client span: %s", spew.Sdump(span))
if got, want := span.Name, "terraform1.Setup/Handshake"; got != want {
t.Errorf("wrong name\ngot: %s\nwant: %s", got, want)
}
attrs := otelAttributesMap(span.Attributes)
if got, want := attrs["rpc.system"], "grpc"; got != want {
t.Errorf("wrong rpc.system\ngot: %s\nwant: %s", got, want)
}
if got, want := attrs["rpc.service"], "terraform1.Setup"; got != want {
t.Errorf("wrong rpc.service\ngot: %s\nwant: %s", got, want)
}
if got, want := attrs["rpc.method"], "Handshake"; got != want {
t.Errorf("wrong rpc.method\ngot: %s\nwant: %s", got, want)
}
})
t.Run("server span", func(t *testing.T) {
span := serverSpan
t.Logf("server span: %s", spew.Sdump(span))
if got, want := span.Name, "terraform1.Setup/Handshake"; got != want {
t.Errorf("wrong name\ngot: %s\nwant: %s", got, want)
}
if got, want := span.Parent.SpanID(), clientSpan.SpanContext.SpanID(); got != want {
t.Errorf("server span is not a child of the client span\nclient span ID: %s\nserver span parent ID: %s", want, got)
}
if got, want := serverSpan.SpanContext.TraceID(), clientSpan.SpanContext.TraceID(); got != want {
t.Errorf("server span belongs to different trace than client span\nclient trace ID: %s\nserver trace ID: %s", want, got)
}
attrs := otelAttributesMap(span.Attributes)
if got, want := attrs["rpc.system"], "grpc"; got != want {
t.Errorf("wrong rpc.system\ngot: %s\nwant: %s", got, want)
}
if got, want := attrs["rpc.service"], "terraform1.Setup"; got != want {
t.Errorf("wrong rpc.service\ngot: %s\nwant: %s", got, want)
}
if got, want := attrs["rpc.method"], "Handshake"; got != want {
t.Errorf("wrong rpc.method\ngot: %s\nwant: %s", got, want)
}
})
}
func otelAttributesMap(kvs []attribute.KeyValue) map[string]any {
ret := make(map[string]any, len(kvs))
for _, kv := range kvs {
ret[string(kv.Key)] = kv.Value.AsInterface()
}
return ret
}
Loading…
Cancel
Save