Skip to content

Conversation

@winiciusallan
Copy link
Contributor

This pull request introduces support for the Keystone Service controller

Closes: #429

Reviewer notes:

  • Because of this issue, I needed to handle with Type field in some reconcile operations.
  • I was in doubt about the adoption implementation, so checking in other controllers, I decided to just make available all current list options (name and type).

@github-actions
Copy link

Failed to assess the semver bump. See logs for details.

@mandre
Copy link
Collaborator

mandre commented Nov 16, 2025

I have found it very helpful, when working on controllers after we created the scaffolding tool, to have at least 2 separate commits:

  • the first one that just runs the scaffolding tool and commits all the generated files. We should write the exact command we ran in the commit message for reproducibility.
  • a second commit that fills the blank and effectively implements the specific parts of the controller, including the API changes and the tests.
  • more commits as necessary.

This helps figure out quickly the specific parts of the controller and find potential bugs and missing bits more easily. If we agree on this patter, we may want to add it as a requirement in our developers docs.

@github-actions
Copy link

Failed to assess the semver bump. See logs for details.

@winiciusallan
Copy link
Contributor Author

winiciusallan commented Nov 16, 2025

This helps figure out quickly the specific parts of the controller and find potential bugs and missing bits more easily. If we agree on this patter, we may want to add it as a requirement in our developers docs.

As discussed in #567, we have some gaps to fill in the developer documentation. This may be a good addition.

I've reverted some stuff and pushed the commits. I will just need to make another commit to resolve merge conflicts.

@github-actions
Copy link

Failed to assess the semver bump. See logs for details.

@github-actions
Copy link

Failed to assess the semver bump. See logs for details.

@winiciusallan winiciusallan marked this pull request as draft November 17, 2025 16:47
Enabled *bool `json:"enabled,omitempty"`

// extra indicates key-value information about the service.
Extra map[string]any `json:"extra,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That appears to be legacy, undocumented API. I'd recommend we stick with what's documented at https://docs.openstack.org/api-ref/identity/v3/#create-service.

@dlaw4608 had the same problem with the Role controller, and he ended up updating gophercloud to properly support the missing fields (so that we can eventually deprecate Extra in gophercloud). Have a look at gophercloud/gophercloud#3532.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it makes sense to update gophercloud struct to support only keystone API fields. I can open a PR for this.

Although keystone has extra in its schema, it should get only valid values when making requests.

https://github.com/openstack/keystone/blob/master/keystone/catalog/backends/sql.py#L60

I think we can block this PR for now until gophercloud's PR is not accepted. If I keep working on this we'll need to change the Service controller and all others that depend on it when deprecating Extra. Wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't want to wait until the next release, I can make it work with free-forms Extra values and make the changes to use valid API values after the release.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case, we don't want to expose undocumented Keystone fields in the ORC API. We should have Description even if internally we implement it via the Extra field, until gophercloud properly has first class support for Description.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, we want to add Name.

Copy link
Collaborator

@mandre mandre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments on the API (this is really the critical part in ORC and we want to get it right). We shouldn't expose the undocumented Extra fields of keystone, even if internally we may use it until gophercloud implements all the fields.

Enabled *bool `json:"enabled,omitempty"`

// extra indicates key-value information about the service.
Extra map[string]any `json:"extra,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In any case, we don't want to expose undocumented Keystone fields in the ORC API. We should have Description even if internally we implement it via the Extra field, until gophercloud properly has first class support for Description.

type ServiceResourceSpec struct {
// type indicates which resource the service is responsible for.
// +kubebuilder:validation:MinLength:=1
// +kubebuilder:validation:MaxLength:=255
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


// enabled indicates whether the service is enabled or not.
// +kubebuilder:default=true
Enabled *bool `json:"enabled,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's a default value (docs say it's optional), we want to make the field optional. If not, we should make it required and drop the pointer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although docs say its optional, I decided to define a default value because the schema defines it.

https://github.com/openstack/keystone/blob/master/keystone/catalog/backends/sql.py#L57

Enabled *bool `json:"enabled,omitempty"`

// extra indicates key-value information about the service.
Extra map[string]any `json:"extra,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, we want to add Name.

// github.com/gophercloud/gophercloud/v2/openstack/identity/v3/services
// extra indicates key-value information about the service.
// +optional
Extra map[string]any `json:"extra,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There too, we'll want to add Name and Description. In the controller's implementation, we'll fetch that from the Extra fields until gophercloud has better support for these fields.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Handling the description was kinda tricky, but I believe it is working properly now.

@github-actions
Copy link

Failed to assess the semver bump. See logs for details.

@github-actions
Copy link

Failed to assess the semver bump. See logs for details.

@winiciusallan
Copy link
Contributor Author

winiciusallan commented Nov 23, 2025

@mandre do you have a clue why make-generate is failing? I have run locally a couple of times and it seems ok. Replacing the diff text manually makes sense?

also, I already fixed the lint. I'm waiting for your response to the above question to commit once. 😄

@mandre
Copy link
Collaborator

mandre commented Nov 24, 2025

@mandre do you have a clue why make-generate is failing? I have run locally a couple of times and it seems ok. Replacing the diff text manually makes sense?

I believe it's because of #565, you'll need to rebase your change.

$ go run ./cmd/scaffold-controller \
    -interactive=false \
    -kind Service \
    -gophercloud-client NewIdentityV3 \
    -gophercloud-module github.com/gophercloud/gophercloud/v2/openstack/identity/v3/services

Signed-off-by: Winicius Silva <winiciusab12@gmail.com>
@github-actions github-actions bot added the semver:major Breaking change label Nov 24, 2025
@winiciusallan winiciusallan marked this pull request as ready for review November 24, 2025 14:33
Copy link
Collaborator

@mandre mandre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems very close to a merging state. Really nice work!
I've noticed you made iterative commits, where you added new commits to address review comments. That's OK during the review phase as it makes each change stand out, but we should rebase and squash the commits to keep clean history when we're happy with the PR status. Would you mind doing it before we merge your PR?

// +kubebuilder:validation:MinLength:=1
// +kubebuilder:validation:MaxLength:=255
// +optional
Description *string `json:"description,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should drop the pointer as it's not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused about when I need to use a pointer for types. If my Description isn't a pointer, when unmarshalling from the user input, if I don't specify the Description field in spec, will it fall as a default value (like an empty string)? And consequently omitted from json, as I passed omitempty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if that wasn't clear, we should drop the pointer from Description in the ServiceResourceStatus but not in the ServiceResourceSpec, because we want to leave the possibility to unset the value in the Spec but we will always report what OpenStack returns in the Status (in other words, we shouldn't have changed the types the scaffolding did for Description).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my mistake, actually. I thought you were talking about Description in spec, so I removed pointer from there 😅 . I'll fix it

Thanks for the explanation, this makes a lot of sense to me. The docs is also very clear about it too.

assertAll:
- celExpr: "service.status.id != ''"
- celExpr: "service.status.resource.extra.name != ''"
- celExpr: "service.status.resource.name != ''"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two checks (name and type being != '') are redundant with the kuttl matcher. Not a big deal, but would be cleaner to remove them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@winiciusallan
Copy link
Contributor Author

winiciusallan commented Nov 24, 2025

This seems very close to a merging state. Really nice work! I've noticed you made iterative commits, where you added new commits to address review comments. That's OK during the review phase as it makes each change stand out, but we should rebase and squash the commits to keep clean history when we're happy with the PR status. Would you mind doing it before we merge your PR?

Thanks! That's ok for me!

For the sake of clarity, do you want me to keep only commits with changes after running the scaffolding-tool and the implementation itself?

Before merging, I would like to highlight something I noticed yesterday. I don't implement support for filling the Extra field, so if a user wants to add an email, for example, it won't be possible. Actually, I don't see many use cases for free-forms extra fields, but I've seen in gophercloud test files this such case.

@mandre
Copy link
Collaborator

mandre commented Nov 24, 2025

This seems very close to a merging state. Really nice work! I've noticed you made iterative commits, where you added new commits to address review comments. That's OK during the review phase as it makes each change stand out, but we should rebase and squash the commits to keep clean history when we're happy with the PR status. Would you mind doing it before we merge your PR?

Thanks! That's ok for me!

For the sake of clarity, do you want me to keep only commits with changes after running the scaffolding-tool and the implementation itself?

The scaffolding should be its own commit (just running the command that is documented in the commit message + make generate), then all the necessary commits to implement the controller. Typically, you'd end up with 2 commits: the scaffolding and the implementation of the controller + tests, but it's very possible that you'd have more commits if for example you need to do refactoring or have other controllers make use of your new controllers. We want to split the commits in logical changes that make sense separately (I'll leave that up to you how you want to logically split your commits). Take a look at how @eshulman2 did on #568, this is a very good example.

Before merging, I would like to highlight something I noticed yesterday. I don't implement support for filling the Extra field, so if a user wants to add an email, for example, it won't be possible. Actually, I don't see many use cases for free-forms extra fields, but I've seen in gophercloud test files this such case.

That's fine, we don't want to expose Extra from the ORC API because:

  • it's undocumented and could go away at any time
  • we don't need it

@winiciusallan
Copy link
Contributor Author

Hi @mandre! Thanks for the clear explanation. I've decided to keep only two commits (one with scaffolding tool generated code, and another with controller implementation itself).

You may take a last look, but I believe it is now ready to go.

// +kubebuilder:validation:MinLength:=1
// +kubebuilder:validation:MaxLength:=255
// +optional
Description *string `json:"description,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if that wasn't clear, we should drop the pointer from Description in the ServiceResourceStatus but not in the ServiceResourceSpec, because we want to leave the possibility to unset the value in the Spec but we will always report what OpenStack returns in the Status (in other words, we shouldn't have changed the types the scaffolding did for Description).

// name is a Human-readable name for the resource. Might not be unique.
// +kubebuilder:validation:MaxLength=1024
// name indicates the name of service.
// +kubebuilder:validation:MinLength:=1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not add minimum length validation to the status. We will just return what OpenStack return [1]. We however want to have a limit on the maximum size of the resource for CEL validation budget. You may want to check this talk from Joel Speed who explains the reasons [2].

[1] https://k-orc.cloud/development/api-contracts/#resourcestatus
[2] https://www.youtube.com/watch?v=IfaPAqDfJHk

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! In the video, Joel talks a lot about MaxLength, but out of curiosity, can MinLength in some way affect the cost of validation? Well, if kube-apiserver considers the worst case for the cost, I don't think it should be considered.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Collaborator

@mandre mandre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the lag, I thought I submitted my review yesterday but looks like not.

I just have minor nits, then it's good to go.

// description is a human-readable description for the resource.
// +kubebuilder:validation:MaxLength=1024
// description indicates the description of service.
// +kubebuilder:validation:MinLength:=1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's drop the MinLength check in the resource status (we should make that part of the linter if we figure out how to do it).

Signed-off-by: Winicius Silva <winiciusab12@gmail.com>
@mandre mandre added this pull request to the merge queue Nov 26, 2025
Merged via the queue into k-orc:main with commit f5861de Nov 26, 2025
9 checks passed
@winiciusallan winiciusallan deleted the service-controller branch December 1, 2025 17:33
@mandre mandre added this to the Release 2.4 milestone Dec 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

semver:major Breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Keystone Service controller

2 participants