Application developers consume service bindings by reading volume mounted Secret
s from a directory. The specification’s Workload Projection section describes this in detail but there are three important parts:
- A
SERVICE_BINDING_ROOT
environment variable that specifies the directory - A subdirectory for each binding with a name matching the binding name
- A file for each
Secret
entry with a name matching the entry key and content matching the entry value
Let’s take a look at the example given in the spec:
$SERVICE_BINDING_ROOT
├── account-database
│ ├── type
│ ├── provider
│ ├── uri
│ ├── username
│ └── password
└── transaction-event-stream
├── type
├── connection-count
├── uri
├── certificates
└── private-key
In the example, there are two bindings under the $SERVICE_BINDING_ROOT
directory with the names account-database
and transaction-event-stream
. In order for a workload to configure itself, it must select the proper binding for each client type. Each binding directory has a special file named type
that is used to identify the type of the binding projected into that directory (e.g. mysql
, kafka
).
In most cases, the type
entry should be enough to select the appropriate binding. In the cases where it is not (e.g. when there are different providers for the same service type), the optional but strongly encouraged provider
entry should be used to further differentiate bindings. For example, when the type
is mysql
, provider
values of mariadb
, oracle
, bitnami
, or aws-rds
can be used to choose a binding.
type
entry and, if necessary, the provider
entry. Consider using the name of the binding only as a last resort.Language-specific Libraries
A binding can almost always be consumed directly with features found in any programming language. However, it’s often preferable to use a language-specific library that adds semantic meaning to the code. There’s no “right” way to interact with a binding, so here’s a partial list of libraries you might use:
- .NET
- Go
- JVM
- NodeJS
- Python
- Ruby
- Rust
To illustrate the advantages of of using a library, here are a few examples:
Java
import com.nebhale.bindings.Binding;
import com.nebhale.bindings.Bindings;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Application {
public static void main(String[] args) {
Binding[] bindings = Bindings.fromServiceBindingRoot();
bindings = Bindings.filter(bindings, "postgresql");
if (bindings.length != 1) {
System.err.printf("Incorrect number of PostgreSQL drivers: %d\n", bindings.length);
System.exit(1);
}
String url = bindings[0].get("url");
if (url == null) {
System.err.println("No URL in binding");
System.exit(1);
}
Connection conn;
try {
conn = DriverManager.getConnection(url);
} catch (SQLException e) {
System.err.printf("Unable to connect to database: %s", e);
System.exit(1);
}
// ...
}
}
(Example taken from https://github.com/nebhale/client-jvm)
Go
import (
"context"
"fmt"
"github.com/jackc/pgx/v4"
"github.com/baijum/servicebinding/binding"
"os"
)
func main() {
sb, err := bindings.FromServiceBindingRoot()
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, "Could not read service bindings")
os.Exit(1)
}
b, err := sb.Bindings("postgresql")
if err != nil {
_, _ = fmt.Fprintln(os.Stderr, "Unable to find postgresql binding")
os.Exit(1)
}
if len(b) != 1 {
_, _ = fmt.Fprintf(os.Stderr, "Incorrect number of PostgreSQL bindings: %d\n", len(b))
os.Exit(1)
}
u, ok := b[0]["url"]
if !ok {
_, _ = fmt.Fprintln(os.Stderr, "No URL in binding")
os.Exit(1)
}
conn, err := pgx.Connect(context.Background(), u)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
os.Exit(1)
}
defer conn.Close(context.Background())
// ...
}
(Example taken from https://github.com/baijum/servicebinding)