-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
IAM-286-Create-GLAuth-Operator #2
Conversation
…dap_public lib fix: unit test fixes, charm fixes
feat: unit tests verifying that ldap_public provider and users.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the repo should be renamed to glauth-k8s -operator (see this guideline).
Would it be possible to split this PR and open a separate one for the COS integration? I think it will ease the review process.
LICENSE
Outdated
@@ -187,7 +187,7 @@ | |||
same "printed page" as the copyright notice for easier | |||
identification within third-party archives. | |||
|
|||
Copyright 2023 identity-team | |||
Copyright 2022 Canonical Ltd. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should add the year in which the repo was first published.
Copyright 2022 Canonical Ltd. | |
Copyright 2023 Canonical Ltd. |
|
||
## Canonical Observability Stack | ||
|
||
This charm offers integration with observability tools in the [Canonical Observability Stack](https://charmhub.io/topics/canonical-observability-stack). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tbh I would split this pr and have the COS integration added in a separate one.
config.yaml
Outdated
ldap_port: | ||
description: | | ||
Port on which LDAP service is accessible | ||
type: string | ||
default: "3893" | ||
http_port: | ||
description: | | ||
Port on which HTTP services are accessible | ||
type: string | ||
default: "5555" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that we need to make the ports configurable, as we want to maintain only the necessary config options. We don't have that configurable in login ui, for example.
# is also used by the 'canonical/charming-actions' Github action for automated releasing. | ||
upstream-source: kennethreitz/httpbin | ||
description: GLAuth oci-image | ||
upstream-source: glauth_postgres |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no such image, which is why the CI is failing
src/utils.py
Outdated
# Copyright 2023 Canonical Ltd. | ||
# See LICENSE file for licensing details. | ||
|
||
"""Utility functions for the Kratos charm.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"""Utility functions for the Kratos charm.""" | |
"""Utility functions for the glauth-k8s charm.""" |
tests/integration/test_charm.py
Outdated
status="active", | ||
raise_on_blocked=True, | ||
timeout=1000, | ||
wait_for_exact_units=3, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not needed because we are waiting for the "full" charm to become active anyways.
wait_for_exact_units=3, |
tests/unit/test_charm.py
Outdated
@@ -1,68 +1,216 @@ | |||
# Copyright 2023 identity-team | |||
# Copyright 2022 Canonical Ltd. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Copyright 2022 Canonical Ltd. | |
# Copyright 2023 Canonical Ltd. |
src/charm.py
Outdated
self, | ||
relation_name=self._db_relation_name, | ||
database_name=self._db_name, | ||
extra_user_roles="SUPERUSER", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you put the extra user roles in a global variable?
src/charm.py
Outdated
|
||
class GlauthK8SCharm(CharmBase): | ||
"""Charmed GLAuth.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe
"""Charmed GLAuth.""" | |
"""Charmed GLAuth Operator for Kubernetes.""" |
?
src/charm.py
Outdated
if not self._container.isdir(str(self._log_dir)): | ||
self._container.make_dir(path=str(self._log_dir), make_parents=True) | ||
logger.info(f"Created directory {self._log_dir}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you move the comment to this block?
if not self._container.isdir(str(self._log_dir)): | |
self._container.make_dir(path=str(self._log_dir), make_parents=True) | |
logger.info(f"Created directory {self._log_dir}") | |
if not self._container.isdir(str(self._log_dir)): | |
# Create a directory for log forwarding | |
self._container.make_dir(path=str(self._log_dir), make_parents=True) | |
logger.info(f"Created directory {self._log_dir}") |
src/charm.py
Outdated
@property | ||
def _glauth_service_is_running(self) -> bool: | ||
if not self._container.can_connect(): | ||
return False | ||
|
||
try: | ||
service = self._container.get_service(self._container_name) | ||
except (ModelError, RuntimeError): | ||
return False | ||
return service.is_running() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this property is not used anywhere
src/charm.py
Outdated
"services": { | ||
"httpbin": { | ||
self._container_name: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's change this variable name to service_name
(or something along those line) even it has the same value.
src/charm.py
Outdated
def _on_database_created(self, event: DatabaseCreatedEvent) -> None: | ||
"""Event Handler for database created event.""" | ||
if not self._container.can_connect(): | ||
event.defer() | ||
logger.info("Cannot connect to GLAuth container. Deferring event.") | ||
self.unit.status = WaitingStatus("Waiting to connect to glauth container") | ||
return | ||
|
||
self.unit.status = MaintenanceStatus( | ||
"Configuring container and resources for database connection" | ||
) | ||
|
||
try: | ||
self._container.get_service(self._container_name) | ||
except (ModelError, RuntimeError): | ||
event.defer() | ||
self.unit.status = WaitingStatus("Waiting for glauth service") | ||
logger.info("GLAuth service is absent. Deferring database created event.") | ||
return | ||
|
||
logger.info("Updating GLAuth config and restarting service") | ||
self._container.add_layer(self._container_name, self._pebble_layer, combine=True) | ||
self._container.push(self._config_file_path, self._render_conf_file(), make_dirs=True) | ||
|
||
self._container.start(self._container_name) | ||
self.unit.status = ActiveStatus() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when the db is first connected, I use the container's start method. On other db changes and config changes I use the restart method. Not sure what happens when calling restart method on unstarted container. Let me know if you rather I invoked the _handle_status_update_config as response to DataBaseCreatedEvent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell this is the same as the logic in _handle_status_update_config
, let's not duplicate code unless we have to.
src/charm.py
Outdated
def _metrics_external_url(self) -> str: | ||
return normalise_url(self.ingress.url) if self.ingress.is_ready() else "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The prometheus charm client has the option to use an external url to access scraping features. If there's an ingress relation, then we're passing the ingress url to prometheus, otherwise we pass the empty string instead of None, which is the default value for the external_url parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I wasn't clear enough, we are exposing this URL to prometheus, why do we need to "normalise" it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assumed normalising the urls was like a new standard of how we share urls. But you're right, it's not necessary here.
src/utils.py
Outdated
def normalise_url(url: str) -> str: | ||
"""Convert a URL to a more user friendly HTTPS URL. | ||
|
||
The user will be redirected to this URL, we need to use the https prefix | ||
in order to be able to set cookies (secure attribute is set). Also we remove | ||
the port from the URL to make it more user-friendly. | ||
|
||
For example: | ||
http://ingress:80 -> https://ingress | ||
http://ingress:80/ -> https://ingress/ | ||
http://ingress:80/path/subpath -> https://ingress/path/subpath | ||
|
||
This conversion works under the following assumptions: | ||
1) The ingress will serve https under the 443 port, the user-agent will | ||
implicitly make the request on that port | ||
2) The provided URL is not a relative path | ||
3) No user/password is provided in the netloc | ||
|
||
This is a hack and should be removed once traefik provides a way for us to | ||
request the https URL. | ||
""" | ||
p = urlparse(url) | ||
|
||
p = p._replace(scheme="https") | ||
p = p._replace(netloc=p.netloc.rsplit(":", 1)[0]) | ||
|
||
return urlunparse(p) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assumed this was how we were supposed to format ingress urls based on the hydra and kratos charms.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in Hydra and Kratos we do that because we expose those URLS to the user so we want to prettify them. Is this needed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed it.
src/charm.py
Outdated
def _basedn(self) -> str: | ||
# baseDN example: "dc=glauth,dc=com" | ||
dn_input = self.config["base_dn"] | ||
list_dns = dn_input.split(",") | ||
list_dc = [f"dc={dn}" for dn in list_dns] | ||
return ",".join(list_dc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason we don't expect the user to define the config with the proper syntax, ie: dc=example,dc=com
. Since it is a pretty common config for LDAP I don't think we need to be doing this transformation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm somewhat new to LDAP, but I've seen a bunch of docs where people use space instead of a coma, or use all caps for for dc, and ou and such. It would be probably better though to change it, and then normalize the input.
IgnoreCapabilities = {{ ignore_capabilities }} | ||
LimitFailedBinds = {{ limited_failed_binds }} | ||
NumberOfFailedBinds = {{ number_of_failed_binds }} | ||
PeriodOfFailedBinds = {{ period_of_failed_binds }} | ||
BlockFailedBindsFor = {{ block_failed_binds_for }} | ||
PruneSourceTableEvery = {{ prune_source_tables_every }} | ||
PruneSourcesOlderThan = {{ prune_sources_older_than }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we sure we want to expose these as config? Isn't it our job as charmers to configure these to make the user's life easier? (cc @massigori)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On one hand yes, however: there are default values for all these, so users don't have to interact with these configs if they don't want to. If we don't make these configs exportable, then it's really hard to change them for the user.
src/charm.py
Outdated
self._ingress_relation_name = "ingress" | ||
|
||
self.service_patcher = KubernetesServicePatch( | ||
self, [(self.app.name, int(self._ldap_port))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we patch the api
port as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So the API port is something we only use for prometheus metrics. I'm under the impression that the http server is something we only want to use if it's behind a proxy. With that being said I can patch that port too.
Shall we close this in favor of #15? |
Charmed operator for GLAuth
Implemented:
Missing: