Implement Exoscale onboarding API endpoint #199

Merged
rixx merged 14 commits from 37-exoscale-onboarding into main 2025-10-03 07:04:44 +00:00
Member

Implementation notes:

  • We currently do not save invite information, instead creating the user / assigning owner permissions, and letting the (new) user know which email to sign up with. We'll want to change this at some point, though. I looked at django-organizations again and I do not think its data model will fit our use case very well, and implementing a custom invitation model would work better – particularly as we do not really need to handle sign-up, and instead want to handle delayed role assignment. Happy to either implement full invitations now or later, as you prefer.
  • As directed in #37, we currently only link to a service instance creation site encoding the service and the offering, but not the plan passed by ID from Exoscale. As an offering may have multiple plans, this information is lost on the way. (However, we do not use the Plan data model in Servala yet at all, so this is just something to keep in mind for when we start using this model.)
  • The billing entity is currently auto-generated and fairly empty; we'll be able to remove this code once #198 is up.

This PR adds a couple of config options, which I added to the .env.example file. Currently, Servala will create the Exoscale origin on the fly if it does not exist – we could also create it in a database migration to make sure it always exists – functionally, there is no real difference between the two, with on-the-fly creation being a bit more stable in case of accidental deletion (and allowing us to more easily compact migrations in the future).

I spent some time fixing the existing test setup, and the new API endpoint is 100% covered by tests – this should both help guarantee that we do not break the integration in the future, and make it easier to set up additional API tests later on.

As the API endpoint is on the very simple side, I have chosen not to add Django RestFramework to the project for now, as it is a rather big and clunky tool, more suitable to developing full REST APIs than one-off endpoints.

Implementation notes: - We currently do not save invite information, instead creating the user / assigning owner permissions, and letting the (new) user know which email to sign up with. We'll want to change this at some point, though. I looked at django-organizations again and I do not think its data model will fit our use case very well, and implementing a custom invitation model would work better – particularly as we do not really need to handle sign-up, and instead want to handle delayed role assignment. Happy to either implement full invitations now or later, as you prefer. - As directed in #37, we currently only link to a service instance creation site encoding the service and the offering, but not the plan passed by ID from Exoscale. As an offering may have multiple plans, this information is lost on the way. (However, we do not use the Plan data model in Servala yet at all, so this is just something to keep in mind for when we start using this model.) - The billing entity is currently auto-generated and fairly empty; we'll be able to remove this code once #198 is up. This PR adds a couple of config options, which I added to the `.env.example` file. Currently, Servala will create the Exoscale origin on the fly if it does not exist – we could also create it in a database migration to make sure it always exists – functionally, there is no real difference between the two, with on-the-fly creation being a bit more stable in case of accidental deletion (and allowing us to more easily compact migrations in the future). I spent some time fixing the existing test setup, and the new API endpoint is 100% covered by tests – this should both help guarantee that we do not break the integration in the future, and make it easier to set up additional API tests later on. As the API endpoint is on the very simple side, I have chosen not to add Django RestFramework to the project for now, as it is a rather big and clunky tool, more suitable to developing full REST APIs than one-off endpoints.
tobru force-pushed 37-exoscale-onboarding from 0f6d58912e to 63a769e13a 2025-09-26 13:53:35 +00:00 Compare
rixx force-pushed 37-exoscale-onboarding from 63a769e13a to 2778ab2457 2025-09-26 17:02:13 +00:00 Compare
Owner

Tested with:

curl -X PUT -d "@tmp/osb-instance-create.json" -H "Content-Type: application/json" -u exo:blubber http://localhost:8000/v2/service_instances/myinstance

Content of tmp/osb-instance-create.json:

{
  "service_id": "1",
  "plan_id": "1",
  "organization_guid": "01998651-dc86-7d43-9e49-cdb790fcc4f7",
  "space_guid": "01998651-dc86-7d43-9e49-cdb790fcc4f7",
  "parameters": {
    "users": [
      {
        "email": "hello@world.com",
        "full_name": "Max Tester",
        "role": "owner"
      }
    ]
  },
  "context": {
    "platform": "exoscale",
    "organization_guid": "01998651-dc86-7d43-9e49-cdb790fcc4f7",
    "space_guid": "01998651-dc86-7d43-9e49-cdb790fcc4f7",
    "organization_name": "OSBACMEINC",
    "organization_display_name": "OSB ACME Inc."
  }
}

When setting the field service_id to a string:

ValueError: Field 'id' expected a number but got 'forgejo'.

According to the spec, the field id is a string. I think we should have a separate, unique field in the following models to store OSB specific IDs and match against them: Service and Plan.

When I create another service (different service_id and plan_id with the same organization, it returns "Service already enabled". I don't think this is correct? But I guess this will be improved in #38

We currently do not save invite information, instead creating the user / assigning owner permissions, and letting the (new) user know which email to sign up with. We'll want to change this at some point, though. I looked at django-organizations again and I do not think its data model will fit our use case very well, and implementing a custom invitation model would work better – particularly as we do not really need to handle sign-up, and instead want to handle delayed role assignment. Happy to either implement full invitations now or later, as you prefer.

Let's implement the custom invitation flow in #19. I moved that to the selected stage in the development planning board.

As directed in #37, we currently only link to a service instance creation site encoding the service and the offering, but not the plan passed by ID from Exoscale. As an offering may have multiple plans, this information is lost on the way. (However, we do not use the Plan data model in Servala yet at all, so this is just something to keep in mind for when we start using this model.)

For now, this is enough. On Exoscale we will only have one plan: "Enable this Service".

PS: I see that the tests are failing in CI.

Tested with: ```shell curl -X PUT -d "@tmp/osb-instance-create.json" -H "Content-Type: application/json" -u exo:blubber http://localhost:8000/v2/service_instances/myinstance ``` Content of `tmp/osb-instance-create.json`: ```json { "service_id": "1", "plan_id": "1", "organization_guid": "01998651-dc86-7d43-9e49-cdb790fcc4f7", "space_guid": "01998651-dc86-7d43-9e49-cdb790fcc4f7", "parameters": { "users": [ { "email": "hello@world.com", "full_name": "Max Tester", "role": "owner" } ] }, "context": { "platform": "exoscale", "organization_guid": "01998651-dc86-7d43-9e49-cdb790fcc4f7", "space_guid": "01998651-dc86-7d43-9e49-cdb790fcc4f7", "organization_name": "OSBACMEINC", "organization_display_name": "OSB ACME Inc." } } ``` When setting the field `service_id` to a string: ``` ValueError: Field 'id' expected a number but got 'forgejo'. ``` According to the spec, the field `id` is a string. I think we should have a separate, unique field in the following models to store OSB specific IDs and match against them: Service and Plan. When I create another service (different `service_id` and `plan_id` with the same organization, it returns `"Service already enabled"`. I don't think this is correct? But I guess this will be improved in #38 > We currently do not save invite information, instead creating the user / assigning owner permissions, and letting the (new) user know which email to sign up with. We'll want to change this at some point, though. I looked at django-organizations again and I do not think its data model will fit our use case very well, and implementing a custom invitation model would work better – particularly as we do not really need to handle sign-up, and instead want to handle delayed role assignment. Happy to either implement full invitations now or later, as you prefer. Let's implement the custom invitation flow in #19. I moved that to the selected stage in the development planning board. > As directed in #37, we currently only link to a service instance creation site encoding the service and the offering, but not the plan passed by ID from Exoscale. As an offering may have multiple plans, this information is lost on the way. (However, we do not use the Plan data model in Servala yet at all, so this is just something to keep in mind for when we start using this model.) For now, this is enough. On Exoscale we will only have one plan: "Enable this Service". PS: I see that the tests are failing in CI.
Author
Member

I think we should have a separate, unique field in the following models to store OSB specific IDs and match against them: Service and Plan.

We currently do not use the Plan model yet at all apart from in this PR, fwiw. If I understand this correctly, you'd like an osb_id of type string / CharField on both the Service and the Plan model and match against those in the API PUT request?

[200 Service Already Enabled]

I do see that the current behaviour present in this PR cannot be correct. I'm confused as to what this response code should do. How do we track if a service is "enabled" for an exoscale user/organisation/what does "enabled" mean?

Let's implement the custom invitation flow in #19.

Should I do this before moving forward with this PR or after it was merged?

I see that the tests are failing in CI.

Forgot to commit the updated dependencies, tests are passing now.

> I think we should have a separate, unique field in the following models to store OSB specific IDs and match against them: Service and Plan. We currently do not use the `Plan` model yet at all apart from in this PR, fwiw. If I understand this correctly, you'd like an `osb_id` of type `string` / `CharField` on both the `Service` and the `Plan` model and match against those in the API PUT request? > [200 Service Already Enabled] I do see that the current behaviour present in this PR cannot be correct. I'm confused as to what this response code *should* do. How do we track if a service is "enabled" for an exoscale user/organisation/what does "enabled" mean? > Let's implement the custom invitation flow in #19. Should I do this before moving forward with this PR or after it was merged? > I see that the tests are failing in CI. Forgot to commit the updated dependencies, tests are [passing now](https://servala.app.codey.ch/servala/servala-portal/actions/runs/647).
Owner

We currently do not use the Plan model yet at all apart from in this PR, fwiw. If I understand this correctly, you'd like an osb_id of type string / CharField on both the Service and the Plan model and match against those in the API PUT request?

Actually, you're right. The Plan model isn't used yet. The OSB plan would fit much better to the Service Offering model. Let's add an osb_plan_id field to the ServiceOffering field and use that one for matching in the OSB API PUT. I also suggest we get rid of the unused Plan model - I see no use right now and also currently not in the future.
Also, in the same way, let's use an osb_service_id in the Service model.

A clarification: We don't really make use of the plan information. You can see in the Exoscale marketplace screenshot at https://docs.servala.com/exoscale-osb.html#_exoscale_organizations that there is just a single "Enable" button. This is the only "plan" available. Please note that the suspension feature is also done via a specific OSB plan. Once we implement the suspension feature, we will have to catch this special plan without a lookup in the database.

See also https://community.exoscale.com/vendor/marketplace-managed-svc-prov/#plan-setup - In our case we only offer exactly one plan.

I do see that the current behaviour present in this PR cannot be correct. I'm confused as to what this response code should do. How do we track if a service is "enabled" for an exoscale user/organisation/what does "enabled" mean?

We will track if a service is enabled in #38 "Service availability". I guess once we implement #38, we will have a correct behavior. IMHO for now, it's OK to leave it as it is and correct it later.

Should I do this before moving forward with this PR or after it was merged?

I'd do it afterward. I like small steps, one after the other, instead of a big bang =) This is the same reason why I'm OK to not have the fully correct behavior right now. Once we finished all steps, it should be "perfect".

> We currently do not use the Plan model yet at all apart from in this PR, fwiw. If I understand this correctly, you'd like an osb_id of type string / CharField on both the Service and the Plan model and match against those in the API PUT request? Actually, you're right. The Plan model isn't used yet. The OSB plan would fit much better to the Service Offering model. Let's add an `osb_plan_id` field to the `ServiceOffering` field and use that one for matching in the OSB API PUT. I also suggest we get rid of the unused Plan model - I see no use right now and also currently not in the future. Also, in the same way, let's use an `osb_service_id` in the `Service` model. A clarification: We don't really make use of the plan information. You can see in the Exoscale marketplace screenshot at https://docs.servala.com/exoscale-osb.html#_exoscale_organizations that there is just a single "Enable" button. This is the only "plan" available. Please note that the [suspension](https://docs.servala.com/exoscale-osb.html#_suspension) feature is also done via a specific OSB plan. Once we implement the suspension feature, we will have to catch this special plan without a lookup in the database. See also https://community.exoscale.com/vendor/marketplace-managed-svc-prov/#plan-setup - In our case we only offer exactly one plan. > I do see that the current behaviour present in this PR cannot be correct. I'm confused as to what this response code should do. How do we track if a service is "enabled" for an exoscale user/organisation/what does "enabled" mean? We will track if a service is enabled in #38 "Service availability". I guess once we implement #38, we will have a correct behavior. IMHO for now, it's OK to leave it as it is and correct it later. > Should I do this before moving forward with this PR or after it was merged? I'd do it afterward. I like small steps, one after the other, instead of a big bang =) This is the same reason why I'm OK to not have the fully correct behavior right now. Once we finished all steps, it should be "perfect".
tobru reviewed 2025-09-30 06:02:05 +00:00
@ -0,0 +6,4 @@
urlpatterns = [
path(
"v2/service_instances/<str:instance_id>",
Owner

Let's move this to api/osb/v2/service_instances/<str:instance_id>

Let's move this to `api/osb/v2/service_instances/<str:instance_id>`
rixx marked this conversation as resolved
rixx force-pushed 37-exoscale-onboarding from 49e553b1af to e459047622 2025-10-02 07:49:37 +00:00 Compare
rixx merged commit cc23730d33 into main 2025-10-03 07:04:44 +00:00
rixx deleted branch 37-exoscale-onboarding 2025-10-03 07:04:45 +00:00
Sign in to join this conversation.
No description provided.