Skip to content

Conversation

@mvidner
Copy link
Contributor

@mvidner mvidner commented Mar 3, 2025

Problem

agama profile still executes its actions directly from the CLI client, not using the web service. Fix it to make it work like agama --api $URL config ... already does.

agama profile command summary... it is a diverse bunch! (related, unplanned stub: https://trello.com/c/3aUupc2F/461-unify-config-and-profile-commands-in-the-cli )

profile_evaluate(jsonnet_path) -> json stdout
profile_validate(json_path) -> print whether valid, what problems
profile_autoyast(autoyast_url) -> json stdout
profile_import(url) -> performs config_load
  # oh fun!
  if url ends with .xml OR .erb OR /
    profile_autoyast
  else download the file and analyze content
    if is_script # starts with hash-bang and the program is not "jsonnet"
      EXECVE it # which means replace current process. OMG WTF inconsistent UX
    else if is_jsonnet
      profile_evaluate
    else if is_json
      cat
    else
      return Error
  # at this point we have a JSON file
  profile_validate # NOTE that invalid profile is just a message and we continue
  agama_config_load

Solution

Added these 3 new web API paths corresponding 1-1 to the CLI commands:

  • POST /api/profile/validate ?url= ?path= (or body)
  • POST /api/profile/evaluate ?url= ?path= (or body)
  • POST /api/profile/autoyast ?url=

But agama profile import is notable that it effectively includes an agama config load which is implemented as multiple backend calls, so import stays at the front end and the back end only gets what's needed to make it work,
which is running a script:

  • POST /api/profile/execute_script ?url= ?path= (or body)

(TODO explain the rest)

Backend errors now include their causes

Before:

Anyhow(Backend call failed with status 400 and text '{"error":"Error: Could not evaluate the profile"}')

After:

Anyhow(Backend call failed with status 400 and text '{"error":"Error: Could not evaluate the profile: Failed to read system's hardware information: Failed to read agama.libsonnet: No such file or directory (os error 2)"}')

JSON validation errors are more readable now

We used to include a debug representation of the validation error but reading the manual(!) shows there is a way to just point to the offending piece of JSON
$ cat ...
{  "product": {    "ID": "Tumbleweed"  }}
$ agama profile validate rust/agama-lib/share/examples/profile_tw_invalid.json
The profile is not valid. Please, check the following errors:

Before:

  • Additional properties are not allowed ('ID' was unexpected). ValidationError { instance: Object {"ID": String("Tumbleweed")}, kind: AdditionalProperties { unexpected: ["ID"] }, instance_path: JSONPointer([Property("product")]), schema_path: JSONPointer([Keyword("properties"), Property("product"), Keyword("additionalProperties")]) }
  • "id" is a required property. ValidationError { instance: Object {"ID": String("Tumbleweed")}, kind: Required { property: String("id") }, instance_path: JSONPointer([Property("product")]), schema_path: JSONPointer([Keyword("properties"), Property("product"), Keyword("required")]) }

After:

  • Additional properties are not allowed ('ID' was unexpected). /product
  • "id" is a required property. /product

Testing

Unit tests: no

Manual tests

...

Added integration tests expecting a running web service.

Run like (part of .github/workflows/ci-integration-tests.yml)

(cd service/; bundle exec rspec --pattern 'test/integration/**_itest.rb')
  • agama profile validate
  • agama profile evaluate
  • agama profile autoyast ... turns out hard to test with the AY-special URLs as none of them work in my container
  • agama profile import
  • agama --api ... profile ... hitting an opaque SSL handshake error 😭

Screenshots

(textual screenshots coming up)

Documentation

Documented the extended argument handling:

But we are actually still missing documentation for the --api option itself, except for a mention in the blog. IMHO we should add it as part of the config/profile cleanup.

@mvidner
Copy link
Contributor Author

mvidner commented Mar 3, 2025

First commit, client code is missing, server implements only the validation part.

# save auth token
agama auth login
# try what surely works
curl -k -v -H "Authorization: Bearer $(cat $HOME/.local/agama/token)" https://localhost/api/l10n/config
# try out new code
curl -k -H "Authorization: Bearer $(cat $HOME/.local/agama/token)" 'https://localhost/api/profile/validate?path=/etc/os-release'

@coveralls
Copy link

coveralls commented Mar 3, 2025

Pull Request Test Coverage Report for Build 13897313808

Details

  • 1 of 179 (0.56%) changed or added relevant lines in 8 files are covered.
  • No unchanged relevant lines lost coverage.
  • Overall first build on agama-profile-apiurl at 72.138%

Changes Missing Coverage Covered Lines Changed/Added Lines %
rust/agama-server/src/web.rs 0 1 0.0%
rust/xtask/src/main.rs 0 1 0.0%
rust/agama-cli/src/lib.rs 0 2 0.0%
rust/agama-server/src/web/docs/profile.rs 0 6 0.0%
rust/agama-lib/src/profile.rs 0 21 0.0%
rust/agama-server/src/profile/web.rs 0 59 0.0%
rust/agama-cli/src/profile.rs 0 88 0.0%
Totals Coverage Status
Change from base Build 13859864963: 72.1%
Covered Lines: 19939
Relevant Lines: 27640

💛 - Coveralls

@mvidner mvidner force-pushed the agama-profile-apiurl branch 2 times, most recently from 574cb8d to 9effe1e Compare March 6, 2025 14:27
@mvidner mvidner changed the base branch from master to after-release-beta2 March 6, 2025 14:28
@mvidner mvidner force-pushed the agama-profile-apiurl branch from da98de6 to d065983 Compare March 13, 2025 17:06
Base automatically changed from after-release-beta2 to master March 14, 2025 15:25
@mvidner mvidner force-pushed the agama-profile-apiurl branch from 3874138 to d530a71 Compare March 17, 2025 16:10
@mvidner mvidner marked this pull request as ready for review March 19, 2025 14:57
@mvidner mvidner force-pushed the agama-profile-apiurl branch from 5f82310 to df9a2d0 Compare March 20, 2025 10:45
mvidner added 18 commits March 21, 2025 14:12
include instance_path in message, per crate README
> To print causes as well using anyhow’s default formatting of causes, use the alternate selector “{:#}”.

Before:
> Anyhow(Backend call failed with status 400 and text '{"error":"Error: Could not evaluate the profile"}')

After:
> Anyhow(Backend call failed with status 400 and text '{"error":"Error: Could not evaluate the profile: Failed to read system's hardware information: Failed to read agama.libsonnet: No such file or directory (os error 2)"}')
fetches XML or ERB... returns JSON
in preparation for unified passing of input via request body OR ?path= OR ?url=
but the CLI client does not take advantage of it yet

agama profile import will take advantage of it
and it will stay client only, no /api/profile/import
agama profile import file://$(pwd)/rust/agama-lib/share/examples/profile_tw.json
Error reading from file {localization: ...}

-agama profile evaluate PATH
+agama profile evaluate URL_OR_PATH
-agama profile validate PATH
+agama profile validate URL_OR_PATH

this also makes `agama profile autoyast URL` work again

... all of this is so far tested without the `--api URL` option
restore script execution in agama profile import,
via POST /api/profile/execute_script
the web service has a different working directory
so the client makes the path absolute,
but the user does not need to
…ackend

so that filenames with spaces written as " " or "%20" all work
@mvidner mvidner force-pushed the agama-profile-apiurl branch from df9a2d0 to 82eb63c Compare March 21, 2025 13:13
partial cherry pick from master 3415a88
Copy link
Contributor

@imobachgs imobachgs left a comment

Choose a reason for hiding this comment

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

I have reviewed the server part now and we are almost there. IMHO the blocking issues are:

  • As you mentioned, the execute_script blocks the whole server.
  • The progress reporting will not work anymore when using the CLI from a remote system (as it relied on D-Bus).

If you want, we could work on the second problem on a separate PR because it might imply some work.

Ok(router)
}

/// For flexibility, the profile operations take the input as either of:
Copy link
Contributor

Choose a reason for hiding this comment

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

It is fine, but I don't know whether we need this flexibility. In a POST call, I would go with the request body.

match importer_res {
Ok(importer) => Ok(importer.content),
Err(error) => {
// anyhow can be only displayed, not so nice
Copy link
Contributor

Choose a reason for hiding this comment

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

And that's precisely why I tend to avoid anyhow. Do not get me wrong: anyhow is great. However, I would prefer plain errors for libraries and internal APIs where you might need to do some matching.

@imobachgs
Copy link
Contributor

imobachgs commented Apr 2, 2025

JFTR, it could be a good idea to offer a way to upload a local profile from the CLI:

agama profile import --api http://server.lan/api --local import my-profile.jsonnet

But, of course, that's totally out of scope for this PR.

mvidner added 5 commits April 2, 2025 10:49
`cargo doc` warned about that, and it turns out we don't need it

also explain why some of the related tests are disabled
instead of reexporting Url from agama-lib

also remove leftover axum::debug_handler macro usage
/usr/bin/busctl is not our program, don't rewrite its path
…tion

wait only 50ms for a quick failure
test for both
also improve by responding that something is not user's fault,
via 500 internal errors
@mvidner mvidner force-pushed the agama-profile-apiurl branch from ece48fb to 3148fca Compare April 2, 2025 14:53
mvidner added 6 commits April 7, 2025 12:20
Progress monitoring is done with D-Bus and when using a remote Agama
HTTP API, connecting to local D-Bus would crash the monitoring thread,
continuing to work but ugly.

Now it prints an error message, still not nice but better.
Need to use something else for the monitoring.
Before:
> ❯ agama download http:whatever /var
> Anyhow(Cannot write the file '/var'
>
> Caused by:
>     Is a directory (os error 21))

After:
> ❯ agama download http:whatever /var
> Error: Cannot write the file '/var': Is a directory (os error 21)
.spawn()
.context(format!("Spawning script {:?}", path))?;

// Do not child.wait() for the script to finish,
Copy link
Contributor

@imobachgs imobachgs Apr 9, 2025

Choose a reason for hiding this comment

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

As discussed in IRC, I am not sure about this behavior. I would expect to have always the same answer, no matter whether the call takes 50ms or 60ms. I would go for:

  • Waiting until it finishes but without blocking the server. That's why I mentioned using tokio::process::Command instead (but I have not tried if it works as expected). We could even move the script to a separate Tokio task.
  • Or always returning a 200 (if the script was started) and move the script execution to a separate Tokio task.

We even discussed about having an API to run jobs. You would create a job by calling POST /jobs, which would return a job ID. So you can check the current status with GET /jobs/1. Of course, we could integrate this API with the events system too. But I would say it is out of scope.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yay, tokio::process::Command simply works: its Child::wait is async so other requests are served

@mvidner mvidner merged commit 0c9a37b into master Apr 10, 2025
14 checks passed
@mvidner mvidner deleted the agama-profile-apiurl branch April 10, 2025 10:56
ancorgs added a commit to ancorgs/agama that referenced this pull request Apr 10, 2025
This reverts commit 0c9a37b, reversing
changes made to 37803bd.
imobachgs added a commit that referenced this pull request Apr 11, 2025
## Problem

The `agama-auto` service does not work after introducing some changes to
make it possible to use the `agama profile` command from an remote
system (see #2103). The call to the `POST /api/profile/autoyast`
endpoint freezes the whole server.

## Solution

* Make the code that takes care of importing the AutoYaST profile async
(`AutoyastImporter::read`).
* Temporarily disable AutoYaST fetch errors because now they are not
properly handled in the `OEMDRV` feature (it does not pass the
`YAST_SKIP_PROFILE_FETCH_ERROR` env-var). Consider this a temporary
workaround to have a working development image again.
@imobachgs imobachgs mentioned this pull request Apr 22, 2025
imobachgs added a commit that referenced this pull request Apr 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants