= Exoscale Marketplace Integration

To integrate services from the https://products.vshn.ch/appcat/services_index.html[VSHN Application Catalog^] into the https://www.exoscale.com/marketplace/[Exoscale Marketplace^], we provide an Exoscale-specific https://github.com/openservicebrokerapi/servicebroker/blob/master/spec.md[Open Service Broker (OSB) API^] endpoint to provision Organizations and enable access to the service plans. The Servala web portal is used by the end-user of Exoscale to provision and manage services.

When an Exoscale customer enables a service on the Exoscale Marketplace, access to the service will be enabled on the xref:web-portal.adoc[Servala Web Portal].
The Exoscale customer then self-service provisions and configures the service instances via the portal.
Disabling a service by an Exoscale customer will disable access to the service on Servala, and after a grace period, all instances are deleted if they haven't been deleted by the customer yet.

Except the Servala portal everything runs on Exoscale premises.
Every Exoscale zone will run at least one Worker Cluster which hosts the service instances and a xref:control-planes.adoc[Control Plane (CP)].

We regularly send usage data from https://central.vshn.ch/[VSHN Central (Odoo)^] to Exoscale for invoicing the end-user.
Every month an invoice is sent from VSHN to Exoscale for the service usage of the last month.

== Terminology

Open Service Broker API::
"The https://www.openservicebrokerapi.org/[Open Service Broker API project^] allows independent software vendors, SaaS providers and developers to easily provide backing services to workloads running on cloud native platforms."
See official https://github.com/openservicebrokerapi/servicebroker/blob/master/spec.md[API spec^].

OSB API::
Abbreviation of Open Service Broker API.

OSB Specific Terms::
Excerpt from https://github.com/openservicebrokerapi/servicebroker/blob/v2.17/spec.md#terminology[API spec Terminology^]:
* _Service Offering_: The advertisement of a Service that a Service Broker supports.
* _Service Plan_: The representation of the costs and benefits for a given variant of the Service Offering, potentially as a tier.
* _Service Instance_: An instantiation of a Service Offering and Service Plan.

Exoscale Marketplace::
The https://www.exoscale.com/marketplace/[marketplace^] of Exoscale enabling the self-service ordering of services from third-party providers.

== Architecture

[mermaid,arch,png]
....
flowchart TB
    exo["Exoscale Marketplace"]
    eu("Exoscale End-User")
    vshn["Servala Exoscale OSB API"]
    vshnccpapi["Control Plane(s)"]
    vshnccpui["Servala Portal"]

    exo-- OSB API -->vshn-- API --> vshnccpui
    eu-- WebUI -->exo
    eu-- WebUI -->vshnccpui-- K8s API --> vshnccpapi
....

The xref:web-portal.adoc[Servala Web Portal] is doing the heavy lifting, while the custom OSB API endpoint is used for Exoscale integration.

The "Enable" button in the Exoscale Marketplace Portal will:

* Provision an Organization at Servala (if it not already exists) which is tied to Exoscale (`origin=exoscale`). See <<Exoscale Organizations>>.
* Enable access to the enabled service, ready to be provisioned in the Servala Portal

This Organization will be tied to Exoscale services, it can only see services from Exoscale and only provision at Exoscale premises.

=== OSB API at Exoscale

Exoscale uses the OpenServiceBroker (OSB) API for service provisioning and deprovisioning with external vendors, that's why we're integrating this way.

The API currently https://community.exoscale.com/documentation/vendor/marketplace-managed-services-provision/#open-service-broker-api-osbapi[has the following features^]:

* Service instance provisioning
* Service instance update (plan changes, suspensions, user sync)
* Service instance deprovisioning
* Synchronous requests support

== Service Listing

Every service of the VSHN Application Catalog that is available in the Exoscale Marketplace is individually listed in the Exoscale Marketplace.
Listing in the Exoscale Marketplace is done by Exoscale themselves (they manually add them to their database).

We specifically only list services in the Servala Portal which are enabled on the Exoscale Marketplace when being in the context of an organization with origin Exoscale.

Other available services do get a hint in the Servala Portal that they need to be enabled first on the Exoscale Marketplace to get available for provisioning.

This is done due to the fact that there is no backchannel from the Servala Portal to the Exoscale Marketplace.
Would we allow to provision other services than enabled in the Exoscale Marketplace, there would be a mismatch, also from billing and legal perspectives.

=== OSB Plans and Services

There are a few https://github.com/openservicebrokerapi/servicebroker/blob/master/spec.md#service-plan-object[OSB plan^] requirements by Exoscale:

* An OSB API service must match one product on the Exoscale marketplace.
* An OSB API plan should be setup for each published plan for that product.
* An additional technical plan must be available for managing suspensions of end user organizations.

OSB services and plans, including their mapping to specific configurations are configured in the Portal database.

== Exoscale Organizations

When an Exoscale user clicks on the green "Enable" button on a service offering page, the <<Onboarding, onboarding flow>> is triggered in the Servala Portal.

image::exoscale-marketplace-empty-service.png[]

=== Onboarding

[mermaid,onboarding,png]
....
sequenceDiagram
    autonumber
    actor EU as Exoscale User
    participant EP as Exoscale Portal
    participant VCA as Servala OSB API
    participant CCP as Servala Portal

    EU->>EP: Enable VSHN Service
    EP->>VCA: OSB API "PUT"
    VCA-->>CCP: Create "Organization"<br/>(if not exist)
    CCP-->>EU: Send invitation to organization<br/> via E-Mail (if new Organization)
    VCA->>CCP: Enable Service Plan
    CCP->>EU: Send Service Welcome Mail
    VCA->>EP: OSB API Confirmation
    Note over VCA,EP: see return codes below
    EP->>EU: Confirmation
....

.OSB API Provisioning call from Exoscale to VSHN
[source,json]
----
PUT http://exo-osbapi.vshn.net/v2/service_instances/:instance_id
{
    "service_id": "service-test-guid", <1>
    "plan_id": "plan1-test-guid", <2>
    "organization_guid": "org-guid-here", <3>
    "space_guid": "org-guid-here", <3>
    "parameters": {
        "users": [ <4>
            {
                "email":"email",
                "full_name": "full name",
                "role":"owner|tech"
            }
        ]
    },
    "context": {
        "platform": "exoscale",
        "organization_guid": "org-guid-here", <3>
        "space_guid": "org-guid-here", <3>
        "organization_name": "organization-name",
        "organization_display_name": "organization-display-name",
    }
}
----
<1> The ID of the service on VSHN side
<2> The ID of the plan on VSHN side
<3> The Exoscale organization UUID
<4> List of users

https://github.com/openservicebrokerapi/servicebroker/blob/master/spec.md#response-3[HTTP response codes^]:

* `200`: Service already enabled
* `201`: Successfully enabled service

Sources:

* https://community.exoscale.com/documentation/vendor/marketplace-managed-services-provision/#provisioning[Exoscale docs - Provisioning^]
* https://github.com/openservicebrokerapi/servicebroker/blob/master/spec.md#provisioning[OSB API Spec^]

In the Servala Portal an Organization is created by the OSB API if it doesn't exist yet.

Organization Display Name::
The display name is set to the name of the Exoscale organization

Organization Origin::
The organization origin is set to `exoscale` (hardcoded in the OSB API service)

Invitation::
When the Organization is created the first time, an invitation is sent to the user in the field `parameters.users[0].email` from the OSB API.

=== Suspension

This flow is triggered when an Exoscale organization:

* changes their current plan
* is suspended
* changes the user list on Exoscale side and user sync is turned on

The suspension uses a special "suspension" plan.

[mermaid,suspension,png]
....
sequenceDiagram
    autonumber
    participant EP as Exoscale Portal
    participant VCA as Servala OSB API
    participant CCP as Servala Portal
    participant VSHNEER as VSHNeer

    EP->>VCA: OSB API "PATCH"
    Note over EP, VCA: Set suspension Plan
    VCA->>CCP: Disable Service
    CCP->>VSHNEER: Send E-Mail
    VCA->>EP: OSB API Confirmation
    Note over VCA,EP: see return codes below
....

[source,json]
----
PATCH http://exo-osbapi.vshn.net/v2/service_instances/:instance_id

{
    "service_id": "service-test-guid",
    "plan_id": "plan1-test-guid", <1>
    "parameters": {
        "users": [
            {
                "email":"email",
                "full_name": "full name",
                "role":"owner|tech"
            }
        ]
    }
}
----
<1> Special suspension plan, to be defined

https://github.com/openservicebrokerapi/servicebroker/blob/master/spec.md#response-5[HTTP response codes^]:

* `200`: Service is disabled

Sources:

* https://community.exoscale.com/documentation/vendor/marketplace-managed-services-provision/#service-instance-update[Exoscale docs - Service Instance Update^]
* https://github.com/openservicebrokerapi/servicebroker/blob/master/spec.md#updating-a-service-instance[OSB API Spec^]

When the suspension plan is triggered, we send an E-Mail to customers@vshn.ch with all the information we have, so that we can check back with Exoscale what to do.
No service is automatically suspended. If it has to happen, we'll do it manually.

=== Offboarding

This flow is triggered when an Exoscale organization:

* decides to unsubscribe the product
* suspension is not resolved before 7 days in trial mode, or 30 days outside of trial mode, which triggers a purge of their resources
* decides to close their Exoscale account, or their account is terminated

[mermaid.offboarding,png]
....
sequenceDiagram
    autonumber
    actor EU as Exoscale User
    participant EP as Exoscale Portal
    participant VCA as Servala OSB API
    participant CCP as Servala Portal
    participant CP as Control Plane
    
    EU->>EP: Disable VSHN Service
    EP->>VCA: OSB API "DELETE"
    VCA->>CCP: Disable Service
    CCP->>EU: Send Deletion Confirmation Mail
    CCP->>VCA: Confirmation
    VCA->>EP: OSB API Confirmation
    Note over VCA,EP: see return codes below
    EP->>EU: Confirmation
    CCP->>CP: Delete service instances<br />after grace period
....

[source,json]
----
DELETE http://exo-osbapi.vshn.net/v2/service_instances/:instance_id?service_id=service-test-guid&plan_id=plan1-test-guid
----

https://github.com/openservicebrokerapi/servicebroker/blob/master/spec.md#response-10[HTTP response codes^]:

* `200`: Service disabled

Sources:

* https://community.exoscale.com/documentation/vendor/marketplace-managed-services-provision/#deprovisioning[Exoscale docs - Deprovisioning^]
* https://github.com/openservicebrokerapi/servicebroker/blob/master/spec.md#deprovisioning[OSB API Spec^]

When all services are deleted (none exists anymore), an email is sent to customer@vshn.ch for the final closure of the organization.

Also, there is a monitoring check which triggers when no service is available, but service instances are still there and the deletion grace period is over.
This means something failed in cleaning up.

See also <<Deprovisioning>>, which details the single service deprovisioning.

=== User Synchronization

We don't do https://community.exoscale.com/documentation/vendor/marketplace-managed-services-provision/#user-sync[user synchronization^] from Exoscale to VSHN.

____
When user sync is disabled, only the information of the user that made the product purchase will be provided. The information will never be updated.
____

== Instances

=== Provisioning

Instances aren't directly provisioned via the OSB API.
Instead, the service is enabled in the Servala Portal for the organization.

An E-Mail is sent with a well-crafted link to the portal to actually provision the instance.

The portal link encodes:

* The Organization GUID
* The `service_id`
* The `plan_id`

When this portal link is opened, a pre-filled service ordering form is presented in the portal, ready for the user to actually provision the service.

This flow allows an Exoscale user to have more than one instance per service per Exoscale organization.

=== Plan Change

We don't support plan changes on the Exoscale console, all service parameters are configured on our portal on the actual service provisioning.
There is only one plan per service, the default plan.

One exception is the "suspension plan" which is described in the <<Suspension, suspension flow>>.

=== Deprovisioning

See also <<Offboarding>> which talks about Organization offboarding and the OSB API flow.

TODO

We also send an E-Mail for each service instance which gets deleted that way, telling the customer that the service either has to be removed from the VSHN Portal or that it's automatically deleted after the deletion grace period.

== Billing

NOTE: This part is still in its early stages!

The basic flow: We send billing data to Exoscale, Exoscale invoices the end-user, VSHN sends an invoice to Exoscale, Exoscale pays VSHN.

[mermaid,billing,png]
....
flowchart TB
    exo["Exoscale"]
    exocust["Exoscale Customer"]
    vshn["VSHN"]

    exo-- Invoices --> exocust
    exocust-- Pays -->exo

    vshn-- Invoices --> exo
    exo-- Pays -->vshn
....

Exoscale must keep track on our pricing on their end, because we only send usage data and they do the calculation.

TODO

* Send billing data to Exoscale billing API - Exoscale does invoicing to customer - we send invoice to Exoscale
* One SO on VSHN side for Exoscale, to send invoice to Exoscale
** We track the Exoscale organization ID in the SO
** Maybe different product in product DB? Or different variant?
* How to send billing data to Exoscale? Once per month directly from Odoo data, so that we send the same data?

From Exoscale docs:

____

You can define one or more plans corresponding to various service offerings or service levels on your platform.

*Monthly fees*

Each plan can have an optional monthly fee.
When a subscriber unsubscribes from your service, the service is cancelled immediately and they are charged with a pro-rated amount dating from their last subscription charge.

*Additional charges*

It is possible to charge for additional products and services in addition to the optional monthly fee.
All additional billing dimensions must be declared in advance with a defined price for each available plan.

Billing dimensions are specified by:

* a technical name
* a unit

Supported units are:

* h : hours
* gb : gigabytes
* gb.h : gigabytes per hour
* u : arbitrary quantity

The frequency of metering reporting is up to the vendor. You can meter as frequently as every hour.
Metering should be reported at least once a month per customer.
Metering is reported per client organization with the consumption that has occurred since the last successful report. Multiple charges can be reported at once.
When reporting usage, you send the quantity for each defined variable and the client is charged accordingly.
____


[source,json]
----
POST /orgs/:uuid/usage <1>

{
    "records": [
        {
            "variable": "something",
            "quantity": 12.5
        },
        {
            "variable": "something_else",
            "quantity": 1.2
        }
    ]
}
----
<1> `:uuid` is the technical ID of the client organization in the Exoscale backend, which will be shared during the onboarding process.

== Resources

* https://kb.vshn.ch/appuio-cloud/references/architecture/control-api.html[APPUiO Control API Architecture^]
* https://kb.vshn.ch/appuio-cloud/references/architecture/invitations.html[APPUiO Invitations]
* https://github.com/vshn/crossplane-service-broker[Crossplane Service Broker (Code)^] - xref:how-tos/crossplane_service_broker/overview.adoc[Crossplane Service Broker (Docs)]
* https://github.com/vshn/swisscom-service-broker[Swisscom Service Broker^]
* https://community.exoscale.com/documentation/vendor/marketplace-managed-services/[Exoscale Vendor Documentation - Managed Services^]
* https://community.exoscale.com/documentation/vendor/marketplace-managed-services-billing/[Exoscale Vendor Documentation - Managed Services Billing^]
* https://community.exoscale.com/documentation/vendor/marketplace-managed-services-provision/[Exoscale Vendor Documentation - Managed Services Provisioning^]
