From c3325b0c58a8ae08ab1fd8864983b1e756019148 Mon Sep 17 00:00:00 2001 From: wepudt Date: Wed, 29 Apr 2026 09:59:58 +0200 Subject: [PATCH] bumped DT libbuildpack to support filebased VCAP Services --- go.mod | 2 +- go.sum | 2 + .../libbuildpack-dynatrace/README.md | 21 ++++- .../Dynatrace/libbuildpack-dynatrace/hook.go | 82 ++++++++++++++----- .../libbuildpack-dynatrace/windows.go | 36 ++++---- vendor/modules.txt | 2 +- 6 files changed, 101 insertions(+), 44 deletions(-) diff --git a/go.mod b/go.mod index e9548caef..9735e1485 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/cloudfoundry/go-buildpack go 1.24.0 require ( - github.com/Dynatrace/libbuildpack-dynatrace v1.8.0 + github.com/Dynatrace/libbuildpack-dynatrace v1.9.0 github.com/Masterminds/semver v1.5.0 github.com/ZiCog/shiny-thing v0.0.0-20121130081921-e9e19444ccf5 github.com/cloudfoundry/libbuildpack v0.0.0-20260306125332-dcaf55eb6f33 diff --git a/go.sum b/go.sum index 9c31b715c..42671dbd1 100644 --- a/go.sum +++ b/go.sum @@ -562,6 +562,8 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Dynatrace/libbuildpack-dynatrace v1.8.0 h1:VNcd8+rurUUdY12emGfLGUUj5cMH4hkNgrdk8LO3dHE= github.com/Dynatrace/libbuildpack-dynatrace v1.8.0/go.mod h1:Uu9aa5UFAk1Ua+zZXnvzo+avDXuEi+GtegeOyja9xg4= +github.com/Dynatrace/libbuildpack-dynatrace v1.9.0 h1:3tJzXt7VVTsvPPS9Q7+e/KAk0ccC2eAbgHib5C/XRhA= +github.com/Dynatrace/libbuildpack-dynatrace v1.9.0/go.mod h1:Uu9aa5UFAk1Ua+zZXnvzo+avDXuEi+GtegeOyja9xg4= github.com/GoogleCloudPlatform/docker-credential-gcr v2.0.5+incompatible/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= diff --git a/vendor/github.com/Dynatrace/libbuildpack-dynatrace/README.md b/vendor/github.com/Dynatrace/libbuildpack-dynatrace/README.md index c85864380..724328951 100644 --- a/vendor/github.com/Dynatrace/libbuildpack-dynatrace/README.md +++ b/vendor/github.com/Dynatrace/libbuildpack-dynatrace/README.md @@ -21,7 +21,12 @@ func init() { ## Configuration -The Hook will look for credentials in the configurations for existing services (which is represented in the runtime as the VCAP_SERVICES environment variable in JSON format.) We look for service names having the 'dynatrace' substring. +The Hook will look for credentials in the configurations for existing services. It searches for service credentials depending on the binding type: + +1. **File-based**: If the `VCAP_SERVICES_FILE_PATH` environment variable is set, the hook reads the VCAP_SERVICES JSON from the file at that path. +2. **Environment variable**: If the env var `VCAP_SERVICES` is set, it reads the JSON from the `VCAP_SERVICES` environment variable directly. + +In both cases, we look for service names having the 'dynatrace' substring. We support the following configuration fields, @@ -47,8 +52,9 @@ We also support standard Dynatrace environment variables. ## Requirements -- Go 1.11 -- Linux to run the tests. +- Go 1.19 or higher. +- Deployment targets: Linux and Windows. +- Development and testing: Linux, Mac OS, and Windows. ## Development @@ -57,7 +63,14 @@ You can download or clone the repository. You can run tests through, ``` -go test +go test ./... +``` + +By default, tests simulate the Linux platform. To test against a different target OS, use the `-os` flag: + +``` +go test ./... -os=windows +go test ./... -os=linux ``` If you modify/add interfaces, you may need to regenerate the mocks. For this you need [gomock](https://github.com/golang/mock): diff --git a/vendor/github.com/Dynatrace/libbuildpack-dynatrace/hook.go b/vendor/github.com/Dynatrace/libbuildpack-dynatrace/hook.go index bef46b816..2a3c99eea 100644 --- a/vendor/github.com/Dynatrace/libbuildpack-dynatrace/hook.go +++ b/vendor/github.com/Dynatrace/libbuildpack-dynatrace/hook.go @@ -66,7 +66,12 @@ func NewHook(technologies ...string) libbuildpack.Hook { func (h *Hook) AfterCompile(stager *libbuildpack.Stager) error { // All other methods in this package are called from here, which // makes it the main entry-point. + return h.injectDynatrace(stager, runtime.GOOS) +} + +// injectDynatrace is an indirection to get rid of the tight coupling to the underlying operating system +func (h *Hook) injectDynatrace(stager *libbuildpack.Stager, operatingSystem string) error { var err error h.Log.Debug("Checking for enabled dynatrace service...") @@ -84,30 +89,30 @@ func (h *Hook) AfterCompile(stager *libbuildpack.Stager) error { // download installer var installerFilename string - if runtime.GOOS == "linux" { + if operatingSystem == "linux" { installerFilename = "paasInstaller.sh" - } else if runtime.GOOS == "windows" { + } else if operatingSystem == "windows" { installerFilename = "paasInstaller.zip" } else { // This is the only place where we need to return an error. // All following operating system checks are just to determine installation specifics. - return errors.New("libbuildpack-dynatrace: Unsupported operating system: " + runtime.GOOS) + return errors.New("libbuildpack-dynatrace: Unsupported operating system: " + operatingSystem) } installerFilePath := filepath.Join(os.TempDir(), installerFilename) - url := h.getDownloadURL(creds) + url := h.getDownloadURL(creds, operatingSystem) err = h.download(url, installerFilePath, stager, creds) if err != nil && creds.SkipErrors { h.Log.Warning("Error during installer download, skipping installation") return nil - }else if err != nil { + } else if err != nil { return err } // run installer - if runtime.GOOS == "linux" { + if operatingSystem == "linux" { err = h.runInstallerUnix(installerFilePath, installDir, creds, stager) - } else if runtime.GOOS == "windows" { + } else if operatingSystem == "windows" { err = h.runInstallerWindows(installerFilePath, installDir, creds, stager) } @@ -137,9 +142,45 @@ func (h *Hook) AfterCompile(stager *libbuildpack.Stager) error { return nil } +// loadVCAPServicesData returns the raw VCAP_SERVICES JSON data from the appropriate source. +func (h *Hook) loadVCAPServicesData() []byte { + filePath, filePathSet := os.LookupEnv("VCAP_SERVICES_FILE_PATH") + + if filePathSet { + if filePath == "" { + h.Log.Debug("VCAP_SERVICES_FILE_PATH is set but empty") + return nil + } + + h.Log.Debug("Loading VCAP services from file: %s", filePath) + fileContent, err := os.ReadFile(filePath) + if err != nil { + h.Log.Error("Failed to read VCAP services file %s: %s", filePath, err) + return nil + } + h.Log.Debug("Successfully read VCAP Service data.") + return fileContent + + } + + h.Log.Debug("Loading VCAP services from environment variable VCAP_SERVICES") + envData := os.Getenv("VCAP_SERVICES") + if envData == "" { + h.Log.Debug("Environment variable VCAP_SERVICES is not set or empty") + return nil + } + h.Log.Debug("Successfully read VCAP Service data from environment variable.") + return []byte(envData) +} + // getCredentials returns the configuration from the environment, or nil if not found. The credentials are represented -// as a JSON object in the VCAP_SERVICES environment variable. +// as a JSON object loaded via loadVCAPServicesData. func (h *Hook) getCredentials() *credentials { + data := h.loadVCAPServicesData() + if data == nil { + return nil + } + // Represent the structure of the JSON object in VCAP_SERVICES for parsing. var vcapServices map[string][]struct { @@ -147,7 +188,7 @@ func (h *Hook) getCredentials() *credentials { Credentials map[string]interface{} `json:"credentials"` } - if err := json.Unmarshal([]byte(os.Getenv("VCAP_SERVICES")), &vcapServices); err != nil { + if err := json.Unmarshal(data, &vcapServices); err != nil { h.Log.Debug("Failed to unmarshal VCAP_SERVICES: %s", err) return nil } @@ -176,13 +217,13 @@ func (h *Hook) getCredentials() *credentials { SkipErrors: queryString("skiperrors") == "true", NetworkZone: queryString("networkzone"), EnableFIPS: queryString("enablefips") == "true", - AddTechnologies: queryString("addtechnologies"), + AddTechnologies: queryString("addtechnologies"), } if (creds.EnvironmentID != "" && creds.APIToken != "") || creds.CustomOneAgentURL != "" { found = append(found, creds) } else if !(creds.EnvironmentID == "" && creds.APIToken == "") { // One of the fields is empty. - h.Log.Warning("Incomplete credentials for service: %s, environment ID: %s, API token: %s", creds.ServiceName, + h.Log.Error("Incomplete credentials for service: %s, environment ID: %s, API token: %s", creds.ServiceName, creds.EnvironmentID, creds.APIToken) } } @@ -194,7 +235,7 @@ func (h *Hook) getCredentials() *credentials { } if len(found) > 1 { - h.Log.Warning("More than one matching service found!") + h.Log.Error("More than one matching service found!") } return nil @@ -206,10 +247,10 @@ func (h *Hook) download(url, filePath string, stager *libbuildpack.Stager, creds req, _ := http.NewRequest("GET", url, nil) if creds.CustomOneAgentURL == "" { ver, err := stager.BuildpackVersion() - if err != nil { - h.Log.Warning("Failed to get buildpack version: %v", err) - ver = "unknown" - } + if err != nil { + h.Log.Warning("Failed to get buildpack version: %v", err) + ver = "unknown" + } req.Header.Set("User-Agent", fmt.Sprintf("cf-%s-buildpack/%s", stager.BuildpackLanguage(), ver)) req.Header.Set("Authorization", fmt.Sprintf("Api-Token %s", creds.APIToken)) } @@ -219,7 +260,7 @@ func (h *Hook) download(url, filePath string, stager *libbuildpack.Stager, creds return err } defer out.Close() - + const baseWaitTime = 3 * time.Second for i := 0; ; i++ { resp, err := client.Do(req) @@ -268,12 +309,13 @@ func (h *Hook) download(url, filePath string, stager *libbuildpack.Stager, creds } -func (h *Hook) getDownloadURL(c *credentials) string { +func (h *Hook) getDownloadURL(c *credentials, operatingSystem string) string { var osType, installerType string - if runtime.GOOS == "linux" { + switch operatingSystem { + case "linux": osType = "unix" installerType = "paas-sh" - } else if runtime.GOOS == "windows" { + case "windows": osType = "windows" installerType = "paas" } diff --git a/vendor/github.com/Dynatrace/libbuildpack-dynatrace/windows.go b/vendor/github.com/Dynatrace/libbuildpack-dynatrace/windows.go index 2763fb11f..2301e5913 100644 --- a/vendor/github.com/Dynatrace/libbuildpack-dynatrace/windows.go +++ b/vendor/github.com/Dynatrace/libbuildpack-dynatrace/windows.go @@ -39,16 +39,16 @@ func (h *Hook) runInstallerWindows(installerFilePath, installDir string, creds * } func (h *Hook) setUpDotNetCorProfilerInjection(creds *credentials, installDir string, stager *libbuildpack.Stager) error { - loaderPath, err := h.findAbsoluteLoaderPath(stager, installDir) + agentPath, err := h.findAbsoluteAgentPath(stager, installDir) if err != nil { - return fmt.Errorf("cannot find oneagentloader.dll: %s", err) + return fmt.Errorf("cannot find oneagentdotnet.dll: %s", err) } scriptContent := "set COR_ENABLE_PROFILING=1\n" scriptContent += "set COR_PROFILER={B7038F67-52FC-4DA2-AB02-969B3C1EDA03}\n" scriptContent += "set DT_AGENTACTIVE=true\n" scriptContent += "set DT_BLOCKLIST=powershell*\n" - scriptContent += fmt.Sprintf("set COR_PROFILER_PATH_64=%s\n", loaderPath) + scriptContent += fmt.Sprintf("set COR_PROFILER_PATH_64=%s\n", agentPath) if creds.NetworkZone != "" { h.Log.Debug("Setting DT_NETWORK_ZONE...") @@ -68,32 +68,32 @@ func (h *Hook) setUpDotNetCorProfilerInjection(creds *credentials, installDir st return nil } -func (h *Hook) findAbsoluteLoaderPath(stager *libbuildpack.Stager, installDir string) (string, error) { +func (h *Hook) findAbsoluteAgentPath(stager *libbuildpack.Stager, installDir string) (string, error) { - // look for dotnet loader DLL file relative to the root of the downloaded zip archive - // and get the path from the manifest e.g. agent/bin/windows-x86-64/oneagentloader.dll - loaderDllPath, err := h.findAgentPath(filepath.Join(stager.BuildDir(), installDir), "dotnet", "loader", "oneagentloader.dll", "windows-x86-64") + // look for dotnet agent DLL file relative to the root of the downloaded zip archive + // and get the path from the manifest e.g. agent/bin/windows-x86-64/oneagentdotnet .dll + agentDllPath, err := h.findAgentPath(filepath.Join(stager.BuildDir(), installDir), "dotnet", "primary", "oneagentdotnet.dll", "windows-x86-64") if err != nil { h.Log.Error("Manifest handling failed!") return "", err } // windows path separator is "\" instead of "/" - loaderDllPath = strings.ReplaceAll(loaderDllPath, "/", "\\") + agentDllPath = strings.ReplaceAll(agentDllPath, "/", "\\") - // build the loader DLL path relative to the app directory - // e.g. dynatrace/oneagent/agent/bin/windows-x86-64/oneagentloader.dll - loaderDllPathInAppDir := filepath.Join(installDir, loaderDllPath) + // build the agent DLL path relative to the app directory + // e.g. dynatrace/oneagent/agent/bin/windows-x86-64/oneagentdotnet.dll + agentDllPathInAppDir := filepath.Join(installDir, agentDllPath) - // check that the loader dll is present in the build dir - // e.g. at \tmp\app\dynatrace\oneagent\agent\bin\1.303.0.20240930-081133\windows-x86-32\oneagentloader.dll - loaderDllPathInBuildDir := filepath.Join(stager.BuildDir(), loaderDllPathInAppDir) + // check that the agent dll is present in the build dir + // e.g. at \tmp\app\dynatrace\oneagent\agent\bin\1.303.0.20240930-081133\windows-x86-32\oneagentdotnet.dll + agentDllPathInBuildDir := filepath.Join(stager.BuildDir(), agentDllPathInAppDir) - if _, err = os.Stat(loaderDllPathInBuildDir); os.IsNotExist(err) { - h.Log.Error("Agent library (%s) not found!", loaderDllPathInBuildDir) + if _, err = os.Stat(agentDllPathInBuildDir); os.IsNotExist(err) { + h.Log.Error("Agent library (%s) not found!", agentDllPathInBuildDir) return "", err } - // build the absolute path of the loader DLL as it will be available at runtime - return filepath.Join("C:\\users\\vcap\\app", loaderDllPathInAppDir), nil + // build the absolute path of the agent DLL as it will be available at runtime + return filepath.Join("C:\\users\\vcap\\app", agentDllPathInAppDir), nil } diff --git a/vendor/modules.txt b/vendor/modules.txt index c9964ddb2..e648af28b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,7 +1,7 @@ # code.cloudfoundry.org/lager v2.0.0+incompatible ## explicit code.cloudfoundry.org/lager -# github.com/Dynatrace/libbuildpack-dynatrace v1.8.0 +# github.com/Dynatrace/libbuildpack-dynatrace v1.9.0 ## explicit; go 1.19 github.com/Dynatrace/libbuildpack-dynatrace # github.com/Masterminds/semver v1.5.0