Service Binding Operator manages the data plane for workloads and backing services. This guide provides instructions with examples to help you create a database instance, deploy an application, and use Service Binding Operator to create a binding connection between the application and the database service.

Prerequisites

  • You have access to an OKD cluster using an account with cluster-admin permissions.

  • You have installed the oc CLI.

  • You have installed PostgreSQL psql CLI.

  • You have installed Service Binding Operator from OperatorHub.

  • You have installed the Crunchy Postgres for Kubernetes Operator from OperatorHub using the v5 Update channel. The installed Operator is available in an appropriate namespace, such as the my-postgresql namespace.

Creating a PostgreSQL database instance

To create a PostgreSQL database instance, you must create a PostgresCluster custom resource (CR) and configure the database.

Procedure
  1. Create the PostgresCluster CR and the my-postgresql namespace by running the following command in shell:

    $ oc apply -f - << EOD
    ---
    apiVersion: v1
    kind: Namespace
    metadata:
     name: my-postgresql
    ---
    apiVersion: postgres-operator.crunchydata.com/v1beta1
    kind: PostgresCluster
    metadata:
      name: hippo
      namespace: my-postgresql
      annotations:
        service.binding: 'path={.metadata.annotations.dbsecret},objectType=Secret'
        dbsecret: hippo-pguser-hippo
        proxy: hippo-pgbouncer
        type: postgresql
        service.binding/database: path={.metadata.name}
        service.binding/port: path={.spec.port}
        service.binding/username: path={.metadata.name}
        service.binding/host: path={.metadata.annotations.proxy}
        service.binding/type: path={.metadata.annotations.type}
    spec:
      image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres-ha:centos8-13.4-0
      postgresVersion: 13
      instances:
        - name: instance1
          dataVolumeClaimSpec:
            accessModes:
            - "ReadWriteOnce"
            resources:
              requests:
                storage: 1Gi
      backups:
        pgbackrest:
          image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:centos8-2.33-2
          repos:
          - name: repo1
            volume:
              volumeClaimSpec:
                accessModes:
                - "ReadWriteOnce"
                resources:
                  requests:
                    storage: 1Gi
          - name: repo2
            volume:
              volumeClaimSpec:
                accessModes:
                - "ReadWriteOnce"
                resources:
                  requests:
                    storage: 1Gi
      proxy:
        pgBouncer:
            image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbouncer:centos8-1.15-2
    EOD

    The annotations added in this PostgresCluster CR help in enabling the service binding connection and trigger the Operator reconciliation.

    The output verifies that the database instance is created:

    Example output
    namespace/my-postgresql configured
    postgrescluster.postgres-operator.crunchydata.com/hippo created
  2. After you have created the database instance, ensure that all the pods in my-postgresql namespace are running (it will take a few minutes):

    $ oc get pods -n my-postgresql

    The output verifies that the database is created:

    Example output
    NAME                                     READY   STATUS      RESTARTS   AGE
    hippo-backup-6th6--1-28849               0/1     Completed   0          1m
    hippo-instance1-sl4r-0                   2/2     Running     0          2m
    hippo-pgbouncer-8454474bc7-lhcn9         2/2     Running     0          2m

    The new database is empty at this stage. You can set its schema and project a sample data set to interact with the sample application.

  3. Initialize the database with the schema and sample data. To do so, use the following custom shell script by copying the code into the shell and running it:

    $ cat << EOD | bash
    #!/bin/bash
    
    export pgo_cluster_name=hippo
    export cluster_namespace=my-postgresql
    export pgo_cluster_username=hippo
    export PGPASSWORD=\$(oc -n "\${cluster_namespace}" get secrets \
      "\${pgo_cluster_name}-pguser-\${pgo_cluster_username}" -o "jsonpath={.data['password']}" | base64 -d)
    nohup oc -n \${cluster_namespace} port-forward svc/hippo-pgbouncer 5432:5432 &
    sleep 5
    curl -LO https://raw.githubusercontent.com/spring-petclinic/spring-petclinic-rest/master/src/main/resources/db/postgresql/initDB.sql
    psql -h localhost -U "\${pgo_cluster_username}" "\${pgo_cluster_name}" -f initDB.sql
    curl -LO https://raw.githubusercontent.com/spring-petclinic/spring-petclinic-rest/master/src/main/resources/db/postgresql/populateDB.sql
    psql -h localhost -U "\${pgo_cluster_username}" "\${pgo_cluster_name}" -f populateDB.sql
    EOD

    The output in the terminal shows you that the database is being configured for the application.

After the database is configured, you can deploy the sample application and connect it to the database service.

Deploying the Spring PetClinic sample application

To deploy the Spring PetClinic sample application on an OKD cluster, you must use a deployment configuration and configure your local environment to be able to test the application.

Procedure
  1. Deploy the spring-petclinic-rest application with the PostgresCluster custom resource (CR) by running the following command in shell:

    $ oc apply -f - << EOD
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: spring-petclinic-rest
      namespace: my-postgresql
      labels:
        app: spring-petclinic-rest
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: spring-petclinic-rest
      template:
        metadata:
          labels:
            app: spring-petclinic-rest
        spec:
          containers:
            - name: application
              image: quay.io/baijum/spring-petclinic-rest:latest
              env:
              - name: SPRING_PROFILES_ACTIVE
                value: postgresql,spring-data-jpa
              ports:
              - name: http
                containerPort: 9966
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: spring-petclinic-rest
      namespace: my-postgresql
    spec:
      ports:
      - port: 80
        targetPort: 9966
      selector:
        app: spring-petclinic-rest
    EOD

    The output verifies that the Spring PetClinic sample application is created and deployed:

    Example output
    deployment.apps/spring-petclinic-rest created
    service/spring-petclinic-rest created
  2. Set up port forwarding from the application port to access the sample application from your local environment:

    $ oc port-forward --address 0.0.0.0 svc/spring-petclinic-rest 9966:80 -n my-postgresql
  3. Access http://localhost:9966/petclinic.

    You can now remotely access the Spring PetClinic sample application at localhost:9966.

    The application is not yet connected to the database service. If you try to interact with the application, it will return errors.

    For example, if you try to access the list of all pets using curl, you can see an error message similar to the following sample message:

    $ curl -X GET "http://localhost:9966/petclinic/api/pets" -H "accept: application/json"
    
    {"className":"org.springframework.transaction.CannotCreateTransactionException","exMessage":"Could
    not open JPA EntityManager for transaction; nested exception is
    org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC
    Connection"}

You can now use the Service Binding Operator to connect the application to the database service.

Connecting the Spring PetClinic sample application to the PostgreSQL database service

To connect the sample application to the database service, you must create a ServiceBinding custom resource (CR) that triggers the Service Binding Operator to project the binding data into the application.

Procedure
  1. Create a ServiceBinding CR to project the binding data:

    $ oc apply -f - << EOD
    ---
    apiVersion: binding.operators.coreos.com/v1alpha1
    kind: ServiceBinding
    metadata:
        name: spring-petclinic-rest
        namespace: my-postgresql
    spec:
        services: (1)
        - group: postgres-operator.crunchydata.com
          version: v1beta1
          kind: PostgresCluster (2)
          name: hippo
        - group: ""
          version: v1
          kind: Secret
          name: hippo-pguser-hippo
        application: (3)
          name: spring-petclinic-rest
          group: apps
          version: v1
          resource: deployments
    1 Specifies a list of service resources.
    2 The CR of the database.
    3 The sample application that points to a Deployment or any other similar resource with an embedded PodSpec.

    The output verifies that the ServiceBinding CR is created to project the binding data into the sample application.

    Example output
    servicebinding.binding.operators.coreos.com/spring-petclinic-rest created
  2. To verify that the binding is successful, check the status conditions of the binding resource:

    $ oc get servicebindings spring-petclinic-rest -n my-postgresql -o jsonpath-as-json='{.status.conditions}'
    Example output
    [
      [
        {
          "lastTransitionTime": "2021-09-06T13:42:28Z",
          "message": "",
          "reason": "DataCollected",
          "status": "True",
          "type": "CollectionReady",
        },
        {
          "lastTransitionTime": "2021-09-06T13:42:28Z",
          "message": "",
          "reason": "ApplicationUpdated",
          "status": "True",
          "type": "InjectionReady",
        },
        {
          "lastTransitionTime": "2021-09-06T13:42:28Z",
          "message": "",
          "reason": "ApplicationsBound",
          "status": "True",
          "type": "Ready",
        },
      ],
    ]

    By default, the values from the binding data of the database service are projected as files into the workload container that runs the sample application.

  3. To verify that the files in the application contain the projected binding data, use the following command in shell to print out the contents:

    $ for i in username password host port type; do oc exec -it deploy/spring-petclinic-rest -n my-postgresql -- /bin/bash -c 'find /bindings/*/'$i' -exec echo -n {}:" " \; -exec cat {} \;'; echo; done
    Example output: With all the values from the secret resource
    /bindings/spring-petclinic-rest/username: hippo
    /bindings/spring-petclinic-rest/password: w0ZB<0j1W|K;+4*TlK7-w^z/
    /bindings/spring-petclinic-rest/host: hippo-pgbouncer
    /bindings/spring-petclinic-rest/port: 5432
    /bindings/spring-petclinic-rest/type: postgresql
  4. Set up the port forwarding from the application port to access the sample application from your local environment:

    $ oc port-forward --address 0.0.0.0 svc/spring-petclinic-rest 9966:80 -n my-postgresql
  5. Access http://localhost:9966/petclinic.

    You can now remotely access the Spring PetClinic sample application at localhost:9966.

  6. To verify that the application is now connected to the database service, access the list of all pets:

    $ curl -X GET "http://localhost:9966/petclinic/api/pets" -H "accept: application/json"
    Example output
    [{"id":1,"name":"Leo","birthDate":"2000/09/07","type":{"id":1,"name":"cat"},
    "owner":{"id":1,"firstName":"George","lastName":"Franklin","address":"110...

    The previous output shows the initially configured sample data and verifies that the application is now connected to the database service.