From ae19961704dca36439bb48593e11479b1cc21e22 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Wed, 21 Jan 2026 14:20:13 +0200 Subject: [PATCH 1/3] Add VCAP_SERVICES_FILE_PATH support --- src/java/common/context.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/java/common/context.go b/src/java/common/context.go index d91b36ae8..ebfadfbd8 100644 --- a/src/java/common/context.go +++ b/src/java/common/context.go @@ -106,14 +106,26 @@ type VCAPService struct { Credentials map[string]interface{} `json:"credentials"` } -// GetVCAPServices parses the VCAP_SERVICES environment variable -// Returns an empty VCAPServices map if VCAP_SERVICES is not set +// GetVCAPServices parses the VCAP_SERVICES or VCAP_SERVICES_FILE_PATH environment variable +// Returns an empty VCAPServices map if VCAP_SERVICES or VCAP_SERVICES_FILE_PATH is not set func GetVCAPServices() (VCAPServices, error) { - vcapServicesStr := os.Getenv("VCAP_SERVICES") - if vcapServicesStr == "" { - return VCAPServices{}, nil + if vcapServicesStr, ok := os.LookupEnv("VCAP_SERVICES"); ok && vcapServicesStr != "" { + return loadVcapServices(vcapServicesStr) } + if vcapServicesFileName, ok := os.LookupEnv("VCAP_SERVICES_FILE_PATH"); ok && vcapServicesFileName != "" { + vcapServicesContent, err := os.ReadFile(vcapServicesFileName) + if err != nil { + return VCAPServices{}, err + } + return loadVcapServices(string(vcapServicesContent)) + } + + return VCAPServices{}, nil +} + +// loadVcapServices loads services json content into VCAPServices map +func loadVcapServices(vcapServicesStr string) (VCAPServices, error) { var services VCAPServices if err := json.Unmarshal([]byte(vcapServicesStr), &services); err != nil { return nil, err From e475fc9e421b1f9daa9a9cb1823b07688d97adce Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Wed, 21 Jan 2026 14:27:47 +0200 Subject: [PATCH 2/3] Add tests --- src/java/frameworks/framework_test.go | 31 +++++++++++++++++++ .../frameworks/testdata/vcap_services.json | 10 ++++++ 2 files changed, 41 insertions(+) create mode 100644 src/java/frameworks/testdata/vcap_services.json diff --git a/src/java/frameworks/framework_test.go b/src/java/frameworks/framework_test.go index 908d75819..55ece2228 100644 --- a/src/java/frameworks/framework_test.go +++ b/src/java/frameworks/framework_test.go @@ -93,6 +93,9 @@ var _ = Describe("VCAP Services", func() { AfterEach(func() { os.Unsetenv("VCAP_SERVICES") }) + AfterEach(func() { + os.Unsetenv("VCAP_SERVICES_FILE_PATH") + }) Context("with empty VCAP_SERVICES", func() { It("returns empty services map", func() { @@ -104,6 +107,16 @@ var _ = Describe("VCAP Services", func() { }) }) + Context("with empty VCAP_SERVICES_FILE_PATH", func() { + It("returns empty services map", func() { + os.Setenv("VCAP_SERVICES_FILE_PATH", "") + + services, err := frameworks.GetVCAPServices() + Expect(err).NotTo(HaveOccurred()) + Expect(services).To(HaveLen(0)) + }) + }) + Context("with valid VCAP_SERVICES JSON", func() { It("parses services correctly", func() { vcapJSON := `{ @@ -133,6 +146,24 @@ var _ = Describe("VCAP Services", func() { }) }) + Context("with valid VCAP_SERVICES_FILE_PATH JSON", func() { + It("parses services correctly", func() { + os.Setenv("VCAP_SERVICES_FILE_PATH", "testdata/vcap_services.json") + + services, err := frameworks.GetVCAPServices() + Expect(err).NotTo(HaveOccurred()) + Expect(services.HasService("newrelic")).To(BeTrue()) + + service := services.GetService("newrelic") + Expect(service).NotTo(BeNil()) + Expect(service.Name).To(Equal("newrelic-service")) + + licenseKey, ok := service.Credentials["licenseKey"].(string) + Expect(ok).To(BeTrue()) + Expect(licenseKey).To(Equal("test-key-123")) + }) + }) + Context("with multiple services", func() { It("handles multiple service instances", func() { vcapJSON := `{ diff --git a/src/java/frameworks/testdata/vcap_services.json b/src/java/frameworks/testdata/vcap_services.json new file mode 100644 index 000000000..48f82609e --- /dev/null +++ b/src/java/frameworks/testdata/vcap_services.json @@ -0,0 +1,10 @@ +{ + "newrelic": [{ + "name": "newrelic-service", + "label": "newrelic", + "tags": ["apm", "monitoring"], + "credentials": { + "licenseKey": "test-key-123" + } + }] +} \ No newline at end of file From 2d39278e31738e47d534701326963fa7031441b4 Mon Sep 17 00:00:00 2001 From: Kiril Keranov Date: Wed, 21 Jan 2026 14:42:43 +0200 Subject: [PATCH 3/3] Adjust doc --- docs/IMPLEMENTING_FRAMEWORKS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/IMPLEMENTING_FRAMEWORKS.md b/docs/IMPLEMENTING_FRAMEWORKS.md index d8ffdf74a..bc551d02d 100644 --- a/docs/IMPLEMENTING_FRAMEWORKS.md +++ b/docs/IMPLEMENTING_FRAMEWORKS.md @@ -105,11 +105,11 @@ err := ctx.Installer.InstallDependency(dep, targetDir) ### Type 1: Service-Bound Frameworks -Detect when a specific Cloud Foundry service is bound via `VCAP_SERVICES`. +Detect when a specific Cloud Foundry service is bound via `VCAP_SERVICES` or `VCAP_SERVICES_FILE_PATH`. **Examples:** New Relic, AppDynamics, Seeker Security Provider -**Detection:** Looks for service name/label/tags in `VCAP_SERVICES` +**Detection:** Looks for service name/label/tags in `VCAP_SERVICES` or `VCAP_SERVICES_FILE_PATH`. ### Type 2: Configuration-Based Frameworks