feat(kubernetes): add provider.kubernetes.delimiter config property

the delimiter configuration property for kubernetes allows you to change the default "_" delimiter.

By specifying "/" or "." as a delimiter you ensure that you won't collide with objects names that contains the default delimiter "_".

Fixes #207
This commit is contained in:
Alexis Couvreur
2024-02-05 16:23:45 +00:00
parent 99478819f8
commit 60b270472d
13 changed files with 126 additions and 33 deletions

View File

@@ -5,7 +5,7 @@
"ghcr.io/devcontainers/features/node:1": {
"version": "lts"
},
"ghcr.io/devcontainers/features/docker-in-docker:1": {},
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/go:1": {}
},

View File

@@ -22,9 +22,6 @@ import (
"k8s.io/client-go/tools/cache"
)
// Delimiter is used to split name into kind,namespace,name,replicacount
const Delimiter = "_"
type Config struct {
OriginalName string
Kind string // deployment or statefulset
@@ -38,11 +35,10 @@ type Workload interface {
UpdateScale(ctx context.Context, workloadName string, scale *autoscalingv1.Scale, opts metav1.UpdateOptions) (*autoscalingv1.Scale, error)
}
func convertName(name string) (*Config, error) {
// name format kind_namespace_name_replicas
s := strings.Split(name, Delimiter)
func (provider *KubernetesProvider) convertName(name string) (*Config, error) {
s := strings.Split(name, provider.delimiter)
if len(s) < 4 {
return nil, errors.New("invalid name should be: kind" + Delimiter + "namespace" + Delimiter + "name" + Delimiter + "replicas")
return nil, errors.New("invalid name should be: kind" + provider.delimiter + "namespace" + provider.delimiter + "name" + provider.delimiter + "replicas")
}
replicas, err := strconv.Atoi(s[3])
if err != nil {
@@ -58,8 +54,18 @@ func convertName(name string) (*Config, error) {
}, nil
}
func (provider *KubernetesProvider) convertStatefulset(ss *appsv1.StatefulSet, replicas int32) string {
return fmt.Sprintf("statefulset%s%s%s%s%s%d", provider.delimiter, ss.Namespace, provider.delimiter, ss.Name, provider.delimiter, replicas)
}
func (provider *KubernetesProvider) convertDeployment(d *appsv1.Deployment, replicas int32) string {
return fmt.Sprintf("statefulset%s%s%s%s%s%d", provider.delimiter, d.Namespace, provider.delimiter, d.Name, provider.delimiter, replicas)
}
type KubernetesProvider struct {
Client kubernetes.Interface
Client kubernetes.Interface
delimiter string
}
func NewKubernetesProvider(providerConfig providerConfig.Kubernetes) (*KubernetesProvider, error) {
@@ -79,13 +85,14 @@ func NewKubernetesProvider(providerConfig providerConfig.Kubernetes) (*Kubernete
}
return &KubernetesProvider{
Client: client,
Client: client,
delimiter: providerConfig.Delimiter,
}, nil
}
func (provider *KubernetesProvider) Start(ctx context.Context, name string) (instance.State, error) {
config, err := convertName(name)
config, err := provider.convertName(name)
if err != nil {
return instance.UnrecoverableInstanceState(name, err.Error(), int(config.Replicas))
}
@@ -94,7 +101,7 @@ func (provider *KubernetesProvider) Start(ctx context.Context, name string) (ins
}
func (provider *KubernetesProvider) Stop(ctx context.Context, name string) (instance.State, error) {
config, err := convertName(name)
config, err := provider.convertName(name)
if err != nil {
return instance.UnrecoverableInstanceState(name, err.Error(), int(config.Replicas))
}
@@ -121,12 +128,11 @@ func (provider *KubernetesProvider) GetGroups(ctx context.Context) (map[string][
group := groups[groupName]
// TOOD: Use annotation for scale
name := fmt.Sprintf("%s_%s_%s_%d", "deployment", deployment.Namespace, deployment.Name, 1)
name := provider.convertDeployment(&deployment, 1)
group = append(group, name)
groups[groupName] = group
}
statefulSets, err := provider.Client.AppsV1().StatefulSets(core_v1.NamespaceAll).List(ctx, metav1.ListOptions{
LabelSelector: enableLabel,
})
@@ -143,7 +149,7 @@ func (provider *KubernetesProvider) GetGroups(ctx context.Context) (map[string][
group := groups[groupName]
// TOOD: Use annotation for scale
name := fmt.Sprintf("%s_%s_%s_%d", "statefulset", statefulSet.Namespace, statefulSet.Name, 1)
name := provider.convertStatefulset(&statefulSet, 1)
group = append(group, name)
groups[groupName] = group
}
@@ -179,7 +185,7 @@ func (provider *KubernetesProvider) scale(ctx context.Context, config *Config, r
}
func (provider *KubernetesProvider) GetState(ctx context.Context, name string) (instance.State, error) {
config, err := convertName(name)
config, err := provider.convertName(name)
if err != nil {
return instance.UnrecoverableInstanceState(name, err.Error(), int(config.Replicas))
}
@@ -243,12 +249,12 @@ func (provider *KubernetesProvider) watchDeployents(instance chan<- string) cach
}
if *newDeployment.Spec.Replicas == 0 {
instance <- fmt.Sprintf("deployment_%s_%s_%d", newDeployment.Namespace, newDeployment.Name, *oldDeployment.Spec.Replicas)
instance <- provider.convertDeployment(newDeployment, *oldDeployment.Spec.Replicas)
}
},
DeleteFunc: func(obj interface{}) {
deletedDeployment := obj.(*appsv1.Deployment)
instance <- fmt.Sprintf("deployment_%s_%s_%d", deletedDeployment.Namespace, deletedDeployment.Name, *deletedDeployment.Spec.Replicas)
instance <- provider.convertDeployment(deletedDeployment, *deletedDeployment.Spec.Replicas)
},
}
factory := informers.NewSharedInformerFactoryWithOptions(provider.Client, 2*time.Second, informers.WithNamespace(core_v1.NamespaceAll))
@@ -269,12 +275,12 @@ func (provider *KubernetesProvider) watchStatefulSets(instance chan<- string) ca
}
if *newStatefulSet.Spec.Replicas == 0 {
instance <- fmt.Sprintf("statefulset_%s_%s_%d", newStatefulSet.Namespace, newStatefulSet.Name, *oldStatefulSet.Spec.Replicas)
instance <- provider.convertStatefulset(newStatefulSet, *oldStatefulSet.Spec.Replicas)
}
},
DeleteFunc: func(obj interface{}) {
deletedStatefulSet := obj.(*appsv1.StatefulSet)
instance <- fmt.Sprintf("statefulset__%s_%s_%d", deletedStatefulSet.Namespace, deletedStatefulSet.Name, *deletedStatefulSet.Spec.Replicas)
instance <- provider.convertStatefulset(deletedStatefulSet, *deletedStatefulSet.Spec.Replicas)
},
}
factory := informers.NewSharedInformerFactoryWithOptions(provider.Client, 2*time.Second, informers.WithNamespace(core_v1.NamespaceAll))

View File

@@ -90,7 +90,8 @@ func TestKubernetesProvider_Start(t *testing.T) {
deploymentAPI := mocks.DeploymentMock{}
statefulsetAPI := mocks.StatefulSetsMock{}
provider := KubernetesProvider{
Client: mocks.NewKubernetesAPIClientMock(&deploymentAPI, &statefulsetAPI),
Client: mocks.NewKubernetesAPIClientMock(&deploymentAPI, &statefulsetAPI),
delimiter: "_",
}
deploymentAPI.On("GetScale", mock.Anything, tt.data.name, metav1.GetOptions{}).Return(tt.data.get, nil)
@@ -188,7 +189,8 @@ func TestKubernetesProvider_Stop(t *testing.T) {
deploymentAPI := mocks.DeploymentMock{}
statefulsetAPI := mocks.StatefulSetsMock{}
provider := KubernetesProvider{
Client: mocks.NewKubernetesAPIClientMock(&deploymentAPI, &statefulsetAPI),
Client: mocks.NewKubernetesAPIClientMock(&deploymentAPI, &statefulsetAPI),
delimiter: "_",
}
deploymentAPI.On("GetScale", mock.Anything, tt.data.name, metav1.GetOptions{}).Return(tt.data.get, nil)
@@ -316,7 +318,8 @@ func TestKubernetesProvider_GetState(t *testing.T) {
deploymentAPI := mocks.DeploymentMock{}
statefulsetAPI := mocks.StatefulSetsMock{}
provider := KubernetesProvider{
Client: mocks.NewKubernetesAPIClientMock(&deploymentAPI, &statefulsetAPI),
Client: mocks.NewKubernetesAPIClientMock(&deploymentAPI, &statefulsetAPI),
delimiter: "_",
}
deploymentAPI.On("Get", mock.Anything, tt.data.name, metav1.GetOptions{}).Return(tt.data.getDeployment, nil)

View File

@@ -46,10 +46,12 @@ It provides an integrations with multiple reverse proxies and different loading
// Provider flags
startCmd.Flags().StringVar(&conf.Provider.Name, "provider.name", "docker", fmt.Sprintf("Provider to use to manage containers %v", config.GetProviders()))
viper.BindPFlag("provider.name", startCmd.Flags().Lookup("provider.name"))
startCmd.Flags().Float32Var(&conf.Provider.Kubernetes.QPS, "provider.kubernetes.qps", 5, fmt.Sprintf("QPS limit for K8S API access client-side throttling"))
startCmd.Flags().Float32Var(&conf.Provider.Kubernetes.QPS, "provider.kubernetes.qps", 5, "QPS limit for K8S API access client-side throttling")
viper.BindPFlag("provider.kubernetes.qps", startCmd.Flags().Lookup("provider.kubernetes.qps"))
startCmd.Flags().IntVar(&conf.Provider.Kubernetes.Burst, "provider.kubernetes.burst", 10, fmt.Sprintf("Maximum burst for K8S API acees client-side throttling"))
startCmd.Flags().IntVar(&conf.Provider.Kubernetes.Burst, "provider.kubernetes.burst", 10, "Maximum burst for K8S API acees client-side throttling")
viper.BindPFlag("provider.kubernetes.burst", startCmd.Flags().Lookup("provider.kubernetes.burst"))
startCmd.Flags().StringVar(&conf.Provider.Kubernetes.Delimiter, "provider.kubernetes.delimiter", "_", "Delimiter used for namespace/resource type/name resolution. Defaults to \"_\" for backward compatibility. But you should use \"/\" or \".\"")
viper.BindPFlag("provider.kubernetes.delimiter", startCmd.Flags().Lookup("provider.kubernetes.delimiter"))
// Server flags
startCmd.Flags().IntVar(&conf.Server.Port, "server.port", 10000, "The server port to use")
viper.BindPFlag("server.port", startCmd.Flags().Lookup("server.port"))

View File

@@ -17,6 +17,32 @@ import (
"gotest.tools/v3/assert"
)
func TestDefault(t *testing.T) {
testDir, err := os.Getwd()
require.NoError(t, err, "error getting the current working directory")
wantConfig, err := ioutil.ReadFile(filepath.Join(testDir, "testdata", "config_default.json"))
require.NoError(t, err, "error reading test config file")
// CHANGE `startCmd` behavior to only print the config, this is for testing purposes only
newStartCommand = mockStartCommand
t.Run("config file", func(t *testing.T) {
conf = config.NewConfig()
cmd := NewRootCommand()
output := &bytes.Buffer{}
cmd.SetOut(output)
cmd.SetArgs([]string{
"start",
})
cmd.Execute()
gotOutput := output.String()
assert.Equal(t, string(wantConfig), gotOutput)
})
}
func TestPrecedence(t *testing.T) {
testDir, err := os.Getwd()
require.NoError(t, err, "error getting the current working directory")
@@ -82,6 +108,7 @@ func TestPrecedence(t *testing.T) {
"--provider.name", "cli",
"--provider.kubernetes.qps", "256",
"--provider.kubernetes.burst", "512",
"--provider.kubernetes.delimiter", "_",
"--server.port", "3333",
"--server.base-path", "/cli/",
"--storage.file", "/tmp/cli.json",

View File

@@ -1,6 +1,7 @@
PROVIDER_NAME=envvar
PROVIDER_KUBERNETES_QPS=16
PROVIDER_KUBERNETES_BURST=32
PROVIDER_KUBERNETES_DELIMITER=/
SERVER_PORT=2222
SERVER_BASE_PATH=/envvar/
STORAGE_FILE=/tmp/envvar.json

View File

@@ -3,6 +3,7 @@ provider:
kubernetes:
qps: 64
burst: 128
delimiter: .
server:
port: 1111
base-path: /configfile/

View File

@@ -10,7 +10,8 @@
"Name": "cli",
"Kubernetes": {
"QPS": 256,
"Burst": 512
"Burst": 512,
"Delimiter": "_"
}
},
"Sessions": {

35
cmd/testdata/config_default.json vendored Normal file
View File

@@ -0,0 +1,35 @@
{
"Server": {
"Port": 10000,
"BasePath": "/"
},
"Storage": {
"File": ""
},
"Provider": {
"Name": "docker",
"Kubernetes": {
"QPS": 5,
"Burst": 10,
"Delimiter": "_"
}
},
"Sessions": {
"DefaultDuration": 300000000000,
"ExpirationInterval": 20000000000
},
"Logging": {
"Level": "info"
},
"Strategy": {
"Dynamic": {
"CustomThemesPath": "",
"ShowDetailsByDefault": true,
"DefaultTheme": "hacker-terminal",
"DefaultRefreshFrequency": 5000000000
},
"Blocking": {
"DefaultTimeout": 60000000000
}
}
}

View File

@@ -10,7 +10,8 @@
"Name": "envvar",
"Kubernetes": {
"QPS": 16,
"Burst": 32
"Burst": 32,
"Delimiter": "/"
}
},
"Sessions": {

View File

@@ -10,7 +10,8 @@
"Name": "configfile",
"Kubernetes": {
"QPS": 64,
"Burst": 128
"Burst": 128,
"Delimiter": "."
}
},
"Sessions": {

View File

@@ -4,10 +4,11 @@ import (
"fmt"
)
// Provider holds the provider description
// It can be either docker, swarm or kubernetes
// Provider holds the provider configurations
type Provider struct {
Name string `mapstructure:"NAME" yaml:"provider,omitempty"`
// The provider name to use
// It can be either docker, swarm or kubernetes. Defaults to "docker"
Name string `mapstructure:"NAME" yaml:"provider,omitempty" default:"docker"`
Kubernetes Kubernetes
}
@@ -16,16 +17,20 @@ type Kubernetes struct {
QPS float32 `mapstructure:"QPS" yaml:"QPS" default:"5"`
//Maximum burst for client-side throttle
Burst int `mapstructure:"BURST" yaml:"Burst" default:"10"`
//Delimiter used for namespace/resource type/name resolution. Defaults to "_" for backward compatibility. But you should use "/" or ".".
Delimiter string `mapstructure:"DELIMITER" yaml:"Delimiter" default:"_"`
}
var providers = []string{"docker", "swarm", "kubernetes"}
func NewProviderConfig() Provider {
return Provider{
Name: "docker",
Kubernetes: Kubernetes{
QPS: 5,
Burst: 10,
QPS: 5,
Burst: 10,
Delimiter: "_", //Delimiter used for namespace/resource type/name resolution. Defaults to "_" for backward compatibility. But you should use "/" or ".".
},
}
}

View File

@@ -320,6 +320,7 @@ cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542
cloud.google.com/go/speech v1.20.1/go.mod h1:wwolycgONvfz2EDU8rKuHRW3+wc9ILPsAWoikBEWavY=
cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI=
cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4=
cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8=
cloud.google.com/go/storagetransfer v1.8.0 h1:5T+PM+3ECU3EY2y9Brv0Sf3oka8pKmsCfpQ07+91G9o=
cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw=
cloud.google.com/go/storagetransfer v1.10.3/go.mod h1:Up8LY2p6X68SZ+WToswpQbQHnJpOty/ACcMafuey8gc=
@@ -436,6 +437,7 @@ github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec h1:EdRZT3IeKQmfCSrgo8SZ8V3MEnskuJP0wCYNpe+aiXo=
@@ -448,6 +450,7 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195 h1:58f1tJ1ra+zFINPlwLWvQsR9CzAKt2e+EWV2yX9oXQ4=
github.com/cncf/xds/go v0.0.0-20230310173818-32f1caf87195/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04=
@@ -477,8 +480,10 @@ github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/go-control-plane v0.11.0 h1:jtLewhRR2vMRNnq2ZZUoCjUlgut+Y0+sDDWPOfwOi1o=
github.com/envoyproxy/go-control-plane v0.11.0/go.mod h1:VnHyVMpzcLvCFt9yUz1UnCwHLhwx1WguiVDV7pTG/tI=
github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g=
github.com/envoyproxy/protoc-gen-validate v0.10.0 h1:oIfnZFdC0YhpNNEX+SuIqko4cqqVZeN9IGTrhZje83Y=
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE=
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fasthttp/websocket v1.4.3-rc.6 h1:omHqsl8j+KXpmzRjF8bmzOSYJ8GnS0E3efi1wYT+niY=
@@ -522,11 +527,14 @@ github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/s2a-go v0.1.3 h1:FAgZmpLl/SXurPEZyCMPBIiiYeTbqfjlbdnCNTAkbGE=
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/gax-go/v2 v2.8.0 h1:UBtEZqx1bjXtOQ5BVTkuYghXrr3N4V123VKJK67vJZc=
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
@@ -764,6 +772,7 @@ go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
@@ -799,6 +808,7 @@ google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
google.golang.org/api v0.122.0 h1:zDobeejm3E7pEG1mNHvdxvjs5XJoCMzyNH+CmwL94Es=
google.golang.org/api v0.122.0/go.mod h1:gcitW0lvnyWjSp9nKxAbdHKIZ6vF4aajGueeslZOyms=
google.golang.org/api v0.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA=
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=