mirror of
https://github.com/gravitational/teleport
synced 2024-10-21 17:53:28 +00:00
Fetch cloud environment in parallel (#23472)
This speeds up the amount of time it takes to determine which cloud environment Teleport is running on, and properly propagates a context as an argument instead of stashing it in a config struct.
This commit is contained in:
parent
5b2998a455
commit
fb6e7912fd
|
@ -44,7 +44,7 @@ func Get(ctx context.Context) (*Metadata, error) {
|
|||
go func() {
|
||||
defaultFetcher := &fetchConfig{}
|
||||
defaultFetcher.setDefaults()
|
||||
metadata = defaultFetcher.fetch()
|
||||
metadata = defaultFetcher.fetch(context.Background())
|
||||
|
||||
// Signal that the metadata is ready.
|
||||
close(metadataReady)
|
||||
|
|
|
@ -59,7 +59,6 @@ type Metadata struct {
|
|||
|
||||
// fetchConfig contains the configuration used by the FetchMetadata method.
|
||||
type fetchConfig struct {
|
||||
context context.Context
|
||||
// getenv is the method called to retrieve an environment
|
||||
// variable.
|
||||
// It is configurable so that it can be mocked in tests.
|
||||
|
@ -79,9 +78,6 @@ type fetchConfig struct {
|
|||
// commands, performing http requests, etc.
|
||||
// Having these methods configurable allows us to mock them in tests.
|
||||
func (c *fetchConfig) setDefaults() {
|
||||
if c.context == nil {
|
||||
c.context = context.Background()
|
||||
}
|
||||
if c.getenv == nil {
|
||||
c.getenv = os.Getenv
|
||||
}
|
||||
|
@ -116,7 +112,7 @@ func (c *fetchConfig) setDefaults() {
|
|||
}
|
||||
|
||||
// fetch fetches all metadata.
|
||||
func (c *fetchConfig) fetch() *Metadata {
|
||||
func (c *fetchConfig) fetch(ctx context.Context) *Metadata {
|
||||
return &Metadata{
|
||||
OS: c.fetchOS(),
|
||||
OSVersion: c.fetchOSVersion(),
|
||||
|
@ -124,8 +120,8 @@ func (c *fetchConfig) fetch() *Metadata {
|
|||
GlibcVersion: c.fetchGlibcVersion(),
|
||||
InstallMethods: c.fetchInstallMethods(),
|
||||
ContainerRuntime: c.fetchContainerRuntime(),
|
||||
ContainerOrchestrator: c.fetchContainerOrchestrator(),
|
||||
CloudEnvironment: c.fetchCloudEnvironment(),
|
||||
ContainerOrchestrator: c.fetchContainerOrchestrator(ctx),
|
||||
CloudEnvironment: c.fetchCloudEnvironment(ctx),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,7 +196,7 @@ func (c *fetchConfig) fetchContainerRuntime() string {
|
|||
// running on kubernetes.
|
||||
// This function performs the equivalent of the following:
|
||||
// curl -k https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_SERVICE_PORT/version | jq .gitVersion
|
||||
func (c *fetchConfig) fetchContainerOrchestrator() string {
|
||||
func (c *fetchConfig) fetchContainerOrchestrator(ctx context.Context) string {
|
||||
host := c.getenv("KUBERNETES_SERVICE_HOST")
|
||||
port := c.getenv("KUBERNETES_SERVICE_PORT")
|
||||
if host == "" || port == "" {
|
||||
|
@ -208,7 +204,7 @@ func (c *fetchConfig) fetchContainerOrchestrator() string {
|
|||
}
|
||||
|
||||
url := fmt.Sprintf("https://%s:%s/version", host, port)
|
||||
req, err := http.NewRequestWithContext(c.context, http.MethodGet, url, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
@ -240,15 +236,36 @@ func (c *fetchConfig) fetchContainerOrchestrator() string {
|
|||
|
||||
// fetchCloudEnvironment returns aws, gpc or azure if the instance is running on
|
||||
// such cloud environments.
|
||||
func (c *fetchConfig) fetchCloudEnvironment() string {
|
||||
if c.awsHTTPGetSuccess() {
|
||||
return "aws"
|
||||
func (c *fetchConfig) fetchCloudEnvironment(ctx context.Context) string {
|
||||
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// kick off 3 checks in parallel, at most 1 will succeed
|
||||
checks := []struct {
|
||||
env string
|
||||
f func(context.Context) bool
|
||||
}{
|
||||
{"aws", c.awsHTTPGetSuccess},
|
||||
{"gcp", c.gcpHTTPGetSuccess},
|
||||
{"azure", c.azureHTTPGetSuccess},
|
||||
}
|
||||
if c.gcpHTTPGetSuccess() {
|
||||
return "gcp"
|
||||
|
||||
cloudEnv := make(chan string, len(checks))
|
||||
for _, check := range checks {
|
||||
check := check
|
||||
go func() {
|
||||
if check.f(ctx) {
|
||||
cloudEnv <- check.env
|
||||
} else {
|
||||
cloudEnv <- ""
|
||||
}
|
||||
}()
|
||||
}
|
||||
if c.azureHTTPGetSuccess() {
|
||||
return "azure"
|
||||
|
||||
for range checks {
|
||||
if env := <-cloudEnv; env != "" {
|
||||
return env
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -256,9 +273,9 @@ func (c *fetchConfig) fetchCloudEnvironment() string {
|
|||
// awsHTTPGetSuccess hits the AWS metadata endpoint in order to detect whether
|
||||
// the instance is running on AWS.
|
||||
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
|
||||
func (c *fetchConfig) awsHTTPGetSuccess() bool {
|
||||
func (c *fetchConfig) awsHTTPGetSuccess(ctx context.Context) bool {
|
||||
url := "http://169.254.169.254/latest/meta-data/"
|
||||
req, err := http.NewRequestWithContext(c.context, http.MethodGet, url, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
@ -269,9 +286,9 @@ func (c *fetchConfig) awsHTTPGetSuccess() bool {
|
|||
// gcpHTTPGetSuccess hits the GCP metadata endpoint in order to detect whether
|
||||
// the instance is running on GCP.
|
||||
// https://cloud.google.com/compute/docs/metadata/overview#parts-of-a-request
|
||||
func (c *fetchConfig) gcpHTTPGetSuccess() bool {
|
||||
func (c *fetchConfig) gcpHTTPGetSuccess(ctx context.Context) bool {
|
||||
url := "http://metadata.google.internal/computeMetadata/v1"
|
||||
req, err := http.NewRequestWithContext(c.context, http.MethodGet, url, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
@ -283,9 +300,9 @@ func (c *fetchConfig) gcpHTTPGetSuccess() bool {
|
|||
// azureHTTPGetSuccess hits the Azure metadata endpoint in order to detect whether
|
||||
// the instance is running on Azure.
|
||||
// https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service
|
||||
func (c *fetchConfig) azureHTTPGetSuccess() bool {
|
||||
func (c *fetchConfig) azureHTTPGetSuccess(ctx context.Context) bool {
|
||||
url := "http://169.254.169.254/metadata/instance?api-version=2021-02-01"
|
||||
req, err := http.NewRequestWithContext(c.context, http.MethodGet, url, nil)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -255,11 +255,10 @@ func TestFetchContainerOrchestrator(t *testing.T) {
|
|||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
c := &fetchConfig{
|
||||
context: context.Background(),
|
||||
getenv: tc.getenv,
|
||||
httpDo: tc.httpDo,
|
||||
getenv: tc.getenv,
|
||||
httpDo: tc.httpDo,
|
||||
}
|
||||
require.Equal(t, tc.expected, c.fetchContainerOrchestrator())
|
||||
require.Equal(t, tc.expected, c.fetchContainerOrchestrator(context.Background()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -352,10 +351,9 @@ func TestFetchCloudEnvironment(t *testing.T) {
|
|||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
c := &fetchConfig{
|
||||
context: context.Background(),
|
||||
httpDo: tc.httpDo,
|
||||
httpDo: tc.httpDo,
|
||||
}
|
||||
require.Equal(t, tc.expected, c.fetchCloudEnvironment())
|
||||
require.Equal(t, tc.expected, c.fetchCloudEnvironment(context.Background()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue