Skip to content

Commit

Permalink
Merge pull request #57 from samj1912/service-bindings
Browse files Browse the repository at this point in the history
Add support for k8s service bindings during build time
  • Loading branch information
ekcasey authored Jun 21, 2021
2 parents 767778e + fb34931 commit c07e39f
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 11 deletions.
5 changes: 2 additions & 3 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,8 @@ func Build(builder Builder, options ...Option) {
logger.Debug(PlatformFormatter(ctx.Platform))
}

file = filepath.Join(ctx.Platform.Path, "bindings")
if ctx.Platform.Bindings, err = NewBindingsFromPath(file); err != nil {
config.exitHandler.Error(fmt.Errorf("unable to read platform bindings %s\n%w", file, err))
if ctx.Platform.Bindings, err = NewBindingsForBuild(ctx.Platform.Path); err != nil {
config.exitHandler.Error(fmt.Errorf("unable to read platform bindings %s\n%w", ctx.Platform.Path, err))
return
}
logger.Debugf("Platform Bindings: %+v", ctx.Platform.Bindings)
Expand Down
37 changes: 35 additions & 2 deletions platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ const (

// BindingType is the key for a binding's type.
BindingType = "type"

// EnvServiceBindings is the name of the environment variable that contains the path to service bindings directory.
//
// See the Service Binding Specification for Kubernetes for more details - https://k8s-service-bindings.github.io/spec/
EnvServiceBindings = "SERVICE_BINDING_ROOT"

// EnvCNBBindings is the name of the environment variable that contains the path to the CNB bindings directory. The CNB
// bindings spec will eventually by deprecated in favor of the Service Binding Specification for Kubernetes -
// https://github.com/buildpacks/rfcs/blob/main/text/0055-deprecate-service-bindings.md.
//
// See the CNB bindings extension spec for more details - https://github.com/buildpacks/spec/blob/main/extensions/bindings.md
EnvCNBBindings = "CNB_BINDINGS"
)

// Binding is a projection of metadata about an external entity to be bound to.
Expand Down Expand Up @@ -134,13 +146,20 @@ type Bindings []Binding

// NewBindingsFromEnvironment creates a new bindings from all the bindings at the path defined by $SERVICE_BINDING_ROOT
// or $CNB_BINDINGS if it does not exist. If neither is defined, returns an empty collection of Bindings.
// Note - This API is deprecated. Please use NewBindingsForLaunch instead.
func NewBindingsFromEnvironment() (Bindings, error) {
if path, ok := os.LookupEnv("SERVICE_BINDING_ROOT"); ok {
return NewBindingsForLaunch()
}

// NewBindingsForLaunch creates a new bindings from all the bindings at the path defined by $SERVICE_BINDING_ROOT
// or $CNB_BINDINGS if it does not exist. If neither is defined, returns an empty collection of Bindings.
func NewBindingsForLaunch() (Bindings, error) {
if path, ok := os.LookupEnv(EnvServiceBindings); ok {
return NewBindingsFromPath(path)
}

// TODO: Remove as CNB_BINDINGS ages out
if path, ok := os.LookupEnv("CNB_BINDINGS"); ok {
if path, ok := os.LookupEnv(EnvCNBBindings); ok {
return NewBindingsFromPath(path)
}

Expand All @@ -167,6 +186,20 @@ func NewBindingsFromPath(path string) (Bindings, error) {
return bindings, nil
}

// NewBindingsForBuild creates a new bindings from all the bindings at the path defined by $SERVICE_BINDING_ROOT
// or $CNB_BINDINGS if it does not exist. If neither is defined, bindings are read from <platform>/bindings, the default
// path defined in the CNB Binding extension specification.
func NewBindingsForBuild(platformDir string) (Bindings, error) {
if path, ok := os.LookupEnv(EnvServiceBindings); ok {
return NewBindingsFromPath(path)
}
// TODO: Remove as CNB_BINDINGS ages out
if path, ok := os.LookupEnv(EnvCNBBindings); ok {
return NewBindingsFromPath(path)
}
return NewBindingsFromPath(filepath.Join(platformDir, "bindings"))
}

// Platform is the contents of the platform directory.
type Platform struct {

Expand Down
103 changes: 97 additions & 6 deletions platform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ func testPlatform(t *testing.T, context spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect

path string
path, platformPath string
)

it.Before(func() {
var err error
path, err = ioutil.TempDir("", "bindings")
platformPath, err = ioutil.TempDir("", "platform")
path = filepath.Join(platformPath, "bindings")
Expect(err).NotTo(HaveOccurred())
})

Expand Down Expand Up @@ -155,11 +156,11 @@ func testPlatform(t *testing.T, context spec.G, it spec.S) {

context("from environment", func() {
it.Before(func() {
Expect(os.Setenv("CNB_BINDINGS", path))
Expect(os.Setenv(libcnb.EnvCNBBindings, path))
})

it.After(func() {
Expect(os.Unsetenv("CNB_BINDINGS"))
Expect(os.Unsetenv(libcnb.EnvCNBBindings))
})

it("creates bindings from path in $CNB_BINDINGS", func() {
Expand Down Expand Up @@ -278,11 +279,11 @@ func testPlatform(t *testing.T, context spec.G, it spec.S) {

context("from environment", func() {
it.Before(func() {
Expect(os.Setenv("SERVICE_BINDING_ROOT", path))
Expect(os.Setenv(libcnb.EnvServiceBindings, path))
})

it.After(func() {
Expect(os.Unsetenv("SERVICE_BINDING_ROOT"))
Expect(os.Unsetenv(libcnb.EnvServiceBindings))
})

it("creates bindings from path in SERVICE_BINDING_ROOT", func() {
Expand All @@ -304,6 +305,96 @@ func testPlatform(t *testing.T, context spec.G, it spec.S) {
}))
})
})

context("from environment or path", func() {
context("when SERVICE_BINDING_ROOT is defined but CNB_BINDINGS or the passed path does not exist", func() {
it.Before(func() {
Expect(os.Setenv(libcnb.EnvServiceBindings, path))
Expect(os.Setenv(libcnb.EnvCNBBindings, "does not exist"))
})

it.After(func() {
Expect(os.Unsetenv(libcnb.EnvServiceBindings))
Expect(os.Unsetenv(libcnb.EnvCNBBindings))
})

it("creates bindings from path in SERVICE_BINDING_ROOT", func() {
Expect(libcnb.NewBindingsForBuild("random-path-that-does-not-exist")).To(Equal(libcnb.Bindings{
libcnb.Binding{
Name: "alpha",
Path: filepath.Join(path, "alpha"),
Type: "test-type",
Provider: "test-provider",
Secret: map[string]string{"test-secret-key": "test-secret-value"},
},
libcnb.Binding{
Name: "bravo",
Path: filepath.Join(path, "bravo"),
Type: "test-type",
Provider: "test-provider",
Secret: map[string]string{"test-secret-key": "test-secret-value"},
},
}))
})
})

context("when CNB_BINDINGS is defined but the path does not exist", func() {
it.Before(func() {
Expect(os.Setenv(libcnb.EnvCNBBindings, path))
})

it.After(func() {
Expect(os.Unsetenv(libcnb.EnvCNBBindings))
})

it("creates bindings from path in CNB_BINDINGS", func() {
Expect(libcnb.NewBindingsForBuild("random-path-that-does-not-exist")).To(Equal(libcnb.Bindings{
libcnb.Binding{
Name: "alpha",
Path: filepath.Join(path, "alpha"),
Type: "test-type",
Provider: "test-provider",
Secret: map[string]string{"test-secret-key": "test-secret-value"},
},
libcnb.Binding{
Name: "bravo",
Path: filepath.Join(path, "bravo"),
Type: "test-type",
Provider: "test-provider",
Secret: map[string]string{"test-secret-key": "test-secret-value"},
},
}))
})
})

context("when SERVICE_BINDING_ROOT and CNB_BINDINGS is not defined but the path exists", func() {
it("creates bindings from the given path", func() {
Expect(libcnb.NewBindingsForBuild(platformPath)).To(Equal(libcnb.Bindings{
libcnb.Binding{
Name: "alpha",
Path: filepath.Join(path, "alpha"),
Type: "test-type",
Provider: "test-provider",
Secret: map[string]string{"test-secret-key": "test-secret-value"},
},
libcnb.Binding{
Name: "bravo",
Path: filepath.Join(path, "bravo"),
Type: "test-type",
Provider: "test-provider",
Secret: map[string]string{"test-secret-key": "test-secret-value"},
},
}))
})
})

context("when no valid binding variable is set", func() {
it("returns an an empty binding", func() {
Expect(libcnb.NewBindingsForBuild("does-not-exist")).To(Equal(libcnb.Bindings{}))
})
})

})
})
})

Expand Down

0 comments on commit c07e39f

Please sign in to comment.