Skip to content

Commit 60df949

Browse files
authored
Read bindings from VCAP_SERVICES (#228)
Read bindings from VCAP_SERVICES Signed-off-by: Ralf Pannemans <[email protected]>
1 parent 8b7f797 commit 60df949

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed

platform.go

+40
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package libcnb
1818

1919
import (
20+
"encoding/json"
2021
"fmt"
2122
"os"
2223
"path/filepath"
@@ -41,6 +42,9 @@ const (
4142
// See the Service Binding Specification for Kubernetes for more details - https://k8s-service-bindings.github.io/spec/
4243
EnvServiceBindings = "SERVICE_BINDING_ROOT"
4344

45+
// EnvVcapServices is the name of the environment variable that contains the bindings in cloudfoundry
46+
EnvVcapServices = "VCAP_SERVICES"
47+
4448
// EnvCNBBindings is the name of the environment variable that contains the path to the CNB bindings directory.
4549
// See the CNB bindings extension spec for more details - https://github.com/buildpacks/spec/blob/main/extensions/bindings.md
4650
//
@@ -113,6 +117,34 @@ func NewBindingFromPath(path string) (Binding, error) {
113117
return NewBinding(filepath.Base(path), path, secret), nil
114118
}
115119

120+
type vcapServicesBinding struct {
121+
Name string `json:"name"`
122+
Credentials map[string]string `json:"credentials"`
123+
}
124+
125+
// NewBindingsFromVcapServicesEnv creates a new instance from all the bindings given from the VCAP_SERVICES.
126+
func NewBindingsFromVcapServicesEnv(content string) (Bindings, error) {
127+
var contentTyped map[string][]vcapServicesBinding
128+
129+
err := json.Unmarshal([]byte(content), &contentTyped)
130+
if err != nil {
131+
return Bindings{}, nil
132+
}
133+
134+
bindings := Bindings{}
135+
for t, bArray := range contentTyped {
136+
for _, b := range bArray {
137+
bindings = append(bindings, Binding{
138+
Name: b.Name,
139+
Type: t,
140+
Secret: b.Credentials,
141+
})
142+
}
143+
}
144+
145+
return bindings, nil
146+
}
147+
116148
func (b Binding) String() string {
117149
var s []string
118150
for k := range b.Secret {
@@ -163,6 +195,10 @@ func NewBindingsForLaunch() (Bindings, error) {
163195
return NewBindingsFromPath(path)
164196
}
165197

198+
if content, ok := os.LookupEnv(EnvVcapServices); ok {
199+
return NewBindingsFromVcapServicesEnv(content)
200+
}
201+
166202
return Bindings{}, nil
167203
}
168204

@@ -201,6 +237,10 @@ func NewBindingsForBuild(platformDir string) (Bindings, error) {
201237
if path, ok := os.LookupEnv(EnvCNBBindings); ok {
202238
return NewBindingsFromPath(path)
203239
}
240+
241+
if content, ok := os.LookupEnv(EnvVcapServices); ok {
242+
return NewBindingsFromVcapServicesEnv(content)
243+
}
204244
return NewBindingsFromPath(filepath.Join(platformDir, "bindings"))
205245
}
206246

platform_test.go

+51
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,57 @@ func testPlatform(t *testing.T, context spec.G, it spec.S) {
4040
path = filepath.Join(platformPath, "bindings")
4141
})
4242

43+
context("Cloudfoundry VCAP_SERVICES", func() {
44+
context("Build", func() {
45+
it("creates a bindings from VCAP_SERVICES", func() {
46+
content, err := os.ReadFile("testdata/vcap_services.json")
47+
Expect(err).NotTo(HaveOccurred())
48+
t.Setenv(libcnb.EnvVcapServices, string(content))
49+
50+
bindings, err := libcnb.NewBindingsForBuild("")
51+
Expect(err).NotTo(HaveOccurred())
52+
53+
Expect(bindings).To(HaveLen(2))
54+
55+
types := []string{bindings[0].Type, bindings[1].Type}
56+
Expect(types).To(ContainElements("elephantsql", "sendgrid"))
57+
})
58+
59+
it("creates empty bindings from empty VCAP_SERVICES", func() {
60+
t.Setenv(libcnb.EnvVcapServices, "{}")
61+
62+
bindings, err := libcnb.NewBindingsForBuild("")
63+
Expect(err).NotTo(HaveOccurred())
64+
65+
Expect(bindings).To(HaveLen(0))
66+
})
67+
})
68+
69+
context("Launch", func() {
70+
it("creates a bindings from VCAP_SERVICES", func() {
71+
content, err := os.ReadFile("testdata/vcap_services.json")
72+
Expect(err).NotTo(HaveOccurred())
73+
t.Setenv(libcnb.EnvVcapServices, string(content))
74+
75+
bindings, err := libcnb.NewBindingsForLaunch()
76+
Expect(err).NotTo(HaveOccurred())
77+
78+
Expect(bindings).To(HaveLen(2))
79+
types := []string{bindings[0].Type, bindings[1].Type}
80+
Expect(types).To(ContainElements("elephantsql", "sendgrid"))
81+
})
82+
83+
it("creates empty bindings from empty VCAP_SERVICES", func() {
84+
t.Setenv(libcnb.EnvVcapServices, "{}")
85+
86+
bindings, err := libcnb.NewBindingsForLaunch()
87+
Expect(err).NotTo(HaveOccurred())
88+
89+
Expect(bindings).To(HaveLen(0))
90+
})
91+
})
92+
})
93+
4394
context("CNB Bindings", func() {
4495
it.Before(func() {
4596
Expect(os.MkdirAll(filepath.Join(path, "alpha", "metadata"), 0755)).To(Succeed())

testdata/vcap_services.json

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"elephantsql": [
3+
{
4+
"name": "elephantsql-binding-c6c60",
5+
"binding_guid": "44ceb72f-100b-4f50-87a2-7809c8b42b8d",
6+
"binding_name": "elephantsql-binding-c6c60",
7+
"instance_guid": "391308e8-8586-4c42-b464-c7831aa2ad22",
8+
"instance_name": "elephantsql-c6c60",
9+
"label": "elephantsql",
10+
"tags": [
11+
"postgres",
12+
"postgresql",
13+
"relational"
14+
],
15+
"plan": "turtle",
16+
"credentials": {
17+
"uri": "postgres://exampleuser:[email protected]:5432/exampleuser"
18+
},
19+
"syslog_drain_url": null,
20+
"volume_mounts": []
21+
}
22+
],
23+
"sendgrid": [
24+
{
25+
"name": "mysendgrid",
26+
"binding_guid": "6533b1b6-7916-488d-b286-ca33d3fa0081",
27+
"binding_name": null,
28+
"instance_guid": "8c907d0f-ec0f-44e4-87cf-e23c9ba3925d",
29+
"instance_name": "mysendgrid",
30+
"label": "sendgrid",
31+
"tags": [
32+
"smtp"
33+
],
34+
"plan": "free",
35+
"credentials": {
36+
"hostname": "smtp.example.com",
37+
"username": "QvsXMbJ3rK",
38+
"password": "HCHMOYluTv"
39+
},
40+
"syslog_drain_url": null,
41+
"volume_mounts": []
42+
}
43+
]
44+
}

0 commit comments

Comments
 (0)