Skip to content

Commit

Permalink
GODRIVER-2906 Add container Env to Handshake. (#1382)
Browse files Browse the repository at this point in the history
(cherry picked from commit c3d390f)
  • Loading branch information
qingyang-hu authored and blink1073 committed Sep 20, 2023
1 parent 4b0c121 commit 12064f9
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 23 deletions.
95 changes: 79 additions & 16 deletions x/mongo/driver/operation/hello.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,23 @@ const (
envNameVercel = "vercel"
)

const dockerEnvPath = "/.dockerenv"
const envVarK8s = "KUBERNETES_SERVICE_HOST"

const (
// Runtime names
runtimeNameDocker = "docker"

// Orchestrator names
orchestratorNameK8s = "kubernetes"
)

// getFaasEnvName parses the FaaS environment variable name and returns the
// corresponding name used by the client. If none of the variables or variables
// for multiple names are populated the client.env value MUST be entirely
// omitted. When variables for multiple "client.env.name" values are present,
// "vercel" takes precedence over "aws.lambda"; any other combination MUST cause
// "client.env" to be entirely omitted.
// for multiple names are populated the FaaS values MUST be entirely omitted.
// When variables for multiple "client.env.name" values are present, "vercel"
// takes precedence over "aws.lambda"; any other combination MUST cause FaaS
// values to be entirely omitted.
func getFaasEnvName() string {
envVars := []string{
envVarAWSExecutionEnv,
Expand Down Expand Up @@ -218,6 +229,31 @@ func getFaasEnvName() string {
return ""
}

type containerInfo struct {
runtime string
orchestrator string
}

// getContainerEnvInfo returns runtime and orchestrator of a container.
// If no fields is populated, the client.env.container value MUST be entirely
// omitted.
func getContainerEnvInfo() *containerInfo {
var runtime, orchestrator string
if _, err := os.Stat(dockerEnvPath); !os.IsNotExist(err) {
runtime = runtimeNameDocker
}
if v := os.Getenv(envVarK8s); v != "" {
orchestrator = orchestratorNameK8s
}
if runtime != "" || orchestrator != "" {
return &containerInfo{
runtime: runtime,
orchestrator: orchestrator,
}
}
return nil
}

// appendClientAppName appends the application metadata to the dst. It is the
// responsibility of the caller to check that this appending does not cause dst
// to exceed any size limitations.
Expand Down Expand Up @@ -256,14 +292,20 @@ func appendClientEnv(dst []byte, omitNonName, omitDoc bool) ([]byte, error) {
}

name := getFaasEnvName()
if name == "" {
container := getContainerEnvInfo()
// Omit the entire 'env' if both name and container are empty because other
// fields depend on either of them.
if name == "" && container == nil {
return dst, nil
}

var idx int32

idx, dst = bsoncore.AppendDocumentElementStart(dst, "env")
dst = bsoncore.AppendStringElement(dst, "name", name)

if name != "" {
dst = bsoncore.AppendStringElement(dst, "name", name)
}

addMem := func(envVar string) []byte {
mem := os.Getenv(envVar)
Expand Down Expand Up @@ -306,6 +348,7 @@ func appendClientEnv(dst []byte, omitNonName, omitDoc bool) ([]byte, error) {
}

if !omitNonName {
// No other FaaS fields will be populated if the name is empty.
switch name {
case envNameAWSLambda:
dst = addMem(envVarAWSLambdaFunctionMemorySize)
Expand All @@ -319,6 +362,22 @@ func appendClientEnv(dst []byte, omitNonName, omitDoc bool) ([]byte, error) {
}
}

if container != nil {
var idxCntnr int32
idxCntnr, dst = bsoncore.AppendDocumentElementStart(dst, "container")
if container.runtime != "" {
dst = bsoncore.AppendStringElement(dst, "runtime", container.runtime)
}
if container.orchestrator != "" {
dst = bsoncore.AppendStringElement(dst, "orchestrator", container.orchestrator)
}
var err error
dst, err = bsoncore.AppendDocumentEnd(dst, idxCntnr)
if err != nil {
return dst, err
}
}

return bsoncore.AppendDocumentEnd(dst, idx)
}

Expand Down Expand Up @@ -358,21 +417,25 @@ func appendClientPlatform(dst []byte) []byte {
// name: "<string>"
// },
// driver: {
// name: "<string>",
// version: "<string>"
// name: "<string>",
// version: "<string>"
// },
// platform: "<string>",
// os: {
// type: "<string>",
// name: "<string>",
// architecture: "<string>",
// version: "<string>"
// type: "<string>",
// name: "<string>",
// architecture: "<string>",
// version: "<string>"
// },
// env: {
// name: "<string>",
// timeout_sec: 42,
// memory_mb: 1024,
// region: "<string>",
// name: "<string>",
// timeout_sec: 42,
// memory_mb: 1024,
// region: "<string>",
// container: {
// runtime: "<string>",
// orchestrator: "<string>"
// }
// }
// }
func encodeClientMetadata(appname string, maxLen int) ([]byte, error) {
Expand Down
50 changes: 43 additions & 7 deletions x/mongo/driver/operation/hello_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,14 @@ func TestAppendClientEnv(t *testing.T) {
},
want: []byte(`{"env":{"name":"azure.func"}}`),
},
{
name: "k8s",
env: map[string]string{
envVarK8s: "0.0.0.0",
},
want: []byte(`{"env":{"container":{"orchestrator":"kubernetes"}}}`),
},
// client.env.container.runtime is untested.
}

for _, test := range tests {
Expand Down Expand Up @@ -382,11 +390,17 @@ func TestEncodeClientMetadata(t *testing.T) {
Architecture string `bson:"architecture,omitempty"`
}

type container struct {
Runtime string `bson:"runtime,omitempty"`
Orchestrator string `bson:"orchestrator,omitempty"`
}

type env struct {
Name string `bson:"name,omitempty"`
TimeoutSec int64 `bson:"timeout_sec,omitempty"`
MemoryMB int32 `bson:"memory_mb,omitempty"`
Region string `bson:"region,omitempty"`
Name string `bson:"name,omitempty"`
TimeoutSec int64 `bson:"timeout_sec,omitempty"`
MemoryMB int32 `bson:"memory_mb,omitempty"`
Region string `bson:"region,omitempty"`
Container *container `bson:"container,omitempty"`
}

type clientMetadata struct {
Expand All @@ -408,6 +422,7 @@ func TestEncodeClientMetadata(t *testing.T) {
t.Setenv(envVarAWSLambdaRuntimeAPI, "lambda")
t.Setenv(envVarAWSLambdaFunctionMemorySize, "123")
t.Setenv(envVarAWSRegion, "us-east-2")
t.Setenv(envVarK8s, "0.0.0.0")

t.Run("nothing is omitted", func(t *testing.T) {
got, err := encodeClientMetadata("foo", maxClientMetadataSize)
Expand All @@ -418,7 +433,14 @@ func TestEncodeClientMetadata(t *testing.T) {
Driver: &driver{Name: driverName, Version: version.Driver},
OS: &dist{Type: runtime.GOOS, Architecture: runtime.GOARCH},
Platform: runtime.Version(),
Env: &env{Name: envNameAWSLambda, MemoryMB: 123, Region: "us-east-2"},
Env: &env{
Name: envNameAWSLambda,
MemoryMB: 123,
Region: "us-east-2",
Container: &container{
Orchestrator: "kubernetes",
},
},
})

assertDocsEqual(t, got, want)
Expand All @@ -437,7 +459,12 @@ func TestEncodeClientMetadata(t *testing.T) {
Driver: &driver{Name: driverName, Version: version.Driver},
OS: &dist{Type: runtime.GOOS, Architecture: runtime.GOARCH},
Platform: runtime.Version(),
Env: &env{Name: envNameAWSLambda},
Env: &env{
Name: envNameAWSLambda,
Container: &container{
Orchestrator: "kubernetes",
},
},
})

assertDocsEqual(t, got, want)
Expand All @@ -454,6 +481,10 @@ func TestEncodeClientMetadata(t *testing.T) {

// Calculate what the env.name costs.
ndst := bsoncore.AppendStringElement(nil, "name", envNameAWSLambda)
idx, ndst := bsoncore.AppendDocumentElementStart(ndst, "container")
ndst = bsoncore.AppendStringElement(ndst, "orchestrator", "kubernetes")
ndst, err = bsoncore.AppendDocumentEnd(ndst, idx)
require.NoError(t, err)

// Environment sub name.
envSubName := len(edst) - len(ndst)
Expand All @@ -466,7 +497,12 @@ func TestEncodeClientMetadata(t *testing.T) {
Driver: &driver{Name: driverName, Version: version.Driver},
OS: &dist{Type: runtime.GOOS},
Platform: runtime.Version(),
Env: &env{Name: envNameAWSLambda},
Env: &env{
Name: envNameAWSLambda,
Container: &container{
Orchestrator: "kubernetes",
},
},
})

assertDocsEqual(t, got, want)
Expand Down

0 comments on commit 12064f9

Please sign in to comment.