Skip to content

refactor: simplify c8y_http_proxy#3224

Merged
didier-wenzek merged 17 commits intothin-edge:mainfrom
didier-wenzek:refactor/c8y_http_proxy
Nov 21, 2024
Merged

refactor: simplify c8y_http_proxy#3224
didier-wenzek merged 17 commits intothin-edge:mainfrom
didier-wenzek:refactor/c8y_http_proxy

Conversation

@didier-wenzek
Copy link
Copy Markdown
Contributor

@didier-wenzek didier-wenzek commented Nov 5, 2024

Proposed changes

Can c8y_http_proxy be fully deprecated and replaced by c8y_auth_proxy ?

As noted here the c8y mapper is already bypassing c8y_http_proxy in favor of c8y_auth_proxy but only partially: to upload the file attached to an event but not to create that event.

Actually, c8y_mapper_ext (and also c8y_firmware_manager) can be independent of c8y_http_proxy and c8y_auth_proxy altogether. Indeed, the key point is related to url rewriting.

  1. To use the local auth proxy to Cumulocity, the mapper has simply to rewrite the urls provided by c8y_api::http_proxy::C8yEndPoint using the c8y_auth_proxy::url::ProxyUrlGenerator. There is no need for a direct dependency on the c8y_auth_proxy crate beyond that url generator.
  2. All the c8y urls being rewritten to go through the local auth proxy, there is no more needs for the mapper to fetch JWT tokens. Hence, the code of the c8y_http_proxy can be simplified to focus only one URL generation.

Tasks:

  • Remove unused method C8YHttpProxy::upload_log_binary
  • Move ProxyUrlGenerator from c8y_auth_proxy to c8y_api
  • Combine C8yEndPoint with ProxyUrlGenerator: enforce the use of the auth proxy for c8y urls
  • Remove JWT fetching from c8y_http_proxy
  • Remove the dependencies on c8y_http_proxy from c8y_auth_proxy, c8y_firmware_plugin, and c8y_firmware_manager (this is only to get some struct definitions).
  • Fully remove c8y_http_proxy.

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Improvement (general improvements like code refactoring that doesn't explicitly fix a bug or add any new functionality)
  • Documentation Update (if none of the other choices apply)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Paste Link to the issue

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA (in all commits with git commit -s)
  • I ran cargo fmt as mentioned in CODING_GUIDELINES
  • I used cargo clippy as mentioned in CODING_GUIDELINES
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Further comments

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Nov 5, 2024

Robot Results

✅ Passed ❌ Failed ⏭️ Skipped Total Pass % ⏱️ Duration
524 0 2 524 100 1h31m11.367236999s

@didier-wenzek didier-wenzek force-pushed the refactor/c8y_http_proxy branch from 3aa9fad to a1ed6f6 Compare November 15, 2024 14:59
@didier-wenzek didier-wenzek added refactoring Developer value theme:c8y Theme: Cumulocity related topics labels Nov 15, 2024
@didier-wenzek didier-wenzek marked this pull request as ready for review November 15, 2024 15:01
Copy link
Copy Markdown
Contributor

@albinsuresh albinsuresh left a comment

Choose a reason for hiding this comment

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

Everything looks fine, except for dropping the get_internal_id retry logic which may have other side effects. Happy to approve once that comment is resolved.

Comment on lines +34 to +39
let request = HttpRequestBuilder::get(&url_get_id).build()?;

let mut attempt = 0;
loop {
attempt += 1;
let request = HttpRequestBuilder::get(&url_get_id).build()?;
let endpoint = request.uri().path().to_owned();
let method = request.method().to_owned();

match self.http.await_response(request).await? {
Ok(response) => match response.status() {
StatusCode::OK => {
let internal_id_response: InternalIdResponse = response.json().await?;
let internal_id = internal_id_response.id();

return Ok(internal_id);
}
StatusCode::NOT_FOUND | StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => {
if attempt > 3 {
error!("Failed to fetch internal id for {device_id} even after multiple attempts");
response.error_for_status()?;
}
info!("Re-fetching internal id for {device_id}, attempt: {attempt}");
sleep(self.config.retry_interval).await;
continue;
}
code => {
return Err(C8YRestError::FromHttpError(
tedge_http_ext::HttpError::HttpStatusError {
code,
endpoint,
method,
},
))
}
},

Err(e) => return Err(C8YRestError::FromHttpError(e)),
}
}
let http_result = self.http.await_response(request).await?;
let http_response = http_result.error_for_status()?;
let internal_id_response: InternalIdResponse = http_response.json().await?;
let internal_id = internal_id_response.id();
Ok(internal_id)
Copy link
Copy Markdown
Contributor

@albinsuresh albinsuresh Nov 21, 2024

Choose a reason for hiding this comment

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

From what I remember, the retry mechanism was added to prevent the mapper from failing on startup if the device did not have internet connection at that very moment. Wouldn't that be broken with this simplification? Or we're hoping that the service manager would restart the mapper on this failure anyway and hence it will recover eventually?

Copy link
Copy Markdown
Contributor

@albinsuresh albinsuresh Nov 21, 2024

Choose a reason for hiding this comment

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

Ignore that comment. That internal id lookup was there earlier when we were eagerly fetching the internal id of the main device during the startup (in the C8yHttpActor::init()). Now that we don't have that init logic and internal id is always fetched on-demand, this shouldn't be an issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, this was one of the intent, however the removed piece of code was useless in case of internet disruptions: TCP and TLS errors breaking the retry loop.

The only place where a retry might is useful is when publishing the software list on start. But this should be done at a different level - in the c8y mapper and not blindly at the HTTP level.

Comment on lines +47 to +50
let c8y_host = tedge_config
.c8y
.try_get(c8y_profile)?
.http
.or_config_not_set()?
.to_string();
let c8y_mqtt_host = tedge_config
.c8y
.try_get(c8y_profile)?
.mqtt
.or_config_not_set()?
.to_string();

let c8y_config = tedge_config.c8y.try_get(c8y_profile)?;
let auth_proxy_addr = c8y_config.proxy.client.host.clone();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
let c8y_host = tedge_config
.c8y
.try_get(c8y_profile)?
.http
.or_config_not_set()?
.to_string();
let c8y_mqtt_host = tedge_config
.c8y
.try_get(c8y_profile)?
.mqtt
.or_config_not_set()?
.to_string();
let c8y_config = tedge_config.c8y.try_get(c8y_profile)?;
let auth_proxy_addr = c8y_config.proxy.client.host.clone();
let c8y_config = tedge_config.c8y.try_get(c8y_profile)?;
let c8y_host = c8y_config
.http
.or_config_not_set()?
.to_string();
let c8y_mqtt_host = c8y_config
.mqtt
.or_config_not_set()?
.to_string();
let auth_proxy_addr = c8y_config.proxy.client.host.clone();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

StatusCode::OK => {
let internal_id_response: InternalIdResponse = response.json().await?;
let internal_id = internal_id_response.id();
pub async fn try_get_internal_id(&mut self, device_id: &str) -> Result<String, C8YRestError> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm assuming that even the remaining functions in this "non-actor" would be moved to handle in the next step.

Copy link
Copy Markdown
Contributor

@albinsuresh albinsuresh left a comment

Choose a reason for hiding this comment

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

LGTM.

Copy link
Copy Markdown
Member

@Bravo555 Bravo555 left a comment

Choose a reason for hiding this comment

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

Happy to approve as it is and defer the full removal of c8y_http_proxy to another PR, as this one is already quite big.

@Bravo555 Bravo555 removed their assignment Nov 21, 2024
This is an intermediate step.
C8yEndPoint has been enriched with a ProxyUrlGenerator
and local_proxy_url method.
But the clients has not been changed beyond the miminum to get the
C8yEndPoint. They are still using a combination of C8yEndPoint and ProxyUrlGenerator
to generate the urls to the local auth proxy. The next step will be to
make the ProxyUrlGenerator private and enforce the use of
C8yEndPoint::local_proxy_url()

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
This is done my making private the method `maybe_tenant_url`
and let the c8y mapper uses `local_proxy_url` instead.

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
The C8YHttpProxyActor was adding a level of indirection with little
value. Message generated by C8YHttpProxy was pushed to C8YHttpProxyActor
and then to an HttpActor.

This commit is an intermediate step. The C8YHttpProxyActor is no more an
actor but has been removed. The next step will be to merge C8YHttpProxy
and C8YHttpProxyActor.

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
This endpoint is duplicated into CumulocityConverter::http_proxy

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
This endpoint is already provided by OperationHandler::http_proxy

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
No more used.

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
The retry logic was unnecessary complicated (with nested loops, caches
and cache invalidation. As a first step these loops are replaced by
simple sequences: get the c8y internal id for the device, then use this
id to proceed (i.e. to post a software list or to create an event).

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
This cache is no more used and in any case this is not right place for a
cache as the role of this struct is to name the misc urls used by C8Y
REST API. If a cache is re-introduce it should be in the C8Y HTTP proxy.

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
Now that the local c8y auth proxy is used for all HTTP interaction with
c8y, the HTTP actor needs no more JWT token support but has to be
configured for TLS in case mTLS is enabled for the local c8y auth proxy.

Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
@didier-wenzek didier-wenzek force-pushed the refactor/c8y_http_proxy branch from 09b0c2a to 2c69cdb Compare November 21, 2024 13:04
@didier-wenzek didier-wenzek added this pull request to the merge queue Nov 21, 2024
Merged via the queue into thin-edge:main with commit 8dc5fbe Nov 21, 2024
@didier-wenzek didier-wenzek deleted the refactor/c8y_http_proxy branch November 21, 2024 13:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactoring Developer value theme:c8y Theme: Cumulocity related topics

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants