Skip to content
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

Shell script friendly API and OpenAPI doc #46

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open

Conversation

abstractionfactory
Copy link
Contributor

@abstractionfactory abstractionfactory commented Jan 28, 2025

Blocked by: #44

This PR adds new API endpoints that are more convenient to use from shell scripts. This is a precursor to fixing #30.

API doc preview: https://api.opentofu-get.pages.dev/tofu/api/

Specifically, this exposes the following endpoints:

  • /tofu/api/{versionFragment}.version.txt
  • /tofu/api/{versionFragment}.files.txt
  • /tofu/api/{versionFragment}.version.json
  • /tofu/api/{versionFragment}.files.json

Where versionFragment can be latest, 1, 1.9, or a full version. Furthermore, this updates the /tofu/api.json to include a new latest_versions section for easier programmatic access to the latest versions.

It also makes a Redoc page available at /tofu/api/ for easier implementation.

Note

This code still depends on the GitHub release order and patch releases should always be done in order.

Open questions

  • Given that we expose more endpoints, should we stay on /tofu/api... or should we move to /api and deprecate /tofu/api.json? Admittedly, the whole thing is kind of an API as shown in the API docs above.

Signed-off-by: AbstractionFactory <179820029+abstractionfactory@users.noreply.github.com>
Copy link

cloudflare-workers-and-pages bot commented Jan 28, 2025

Deploying opentofu-get with  Cloudflare Pages  Cloudflare Pages

Latest commit: f851020
Status: ✅  Deploy successful!
Preview URL: https://7030119a.opentofu-get.pages.dev
Branch Preview URL: https://api.opentofu-get.pages.dev

View logs

Signed-off-by: AbstractionFactory <179820029+abstractionfactory@users.noreply.github.com>
Signed-off-by: AbstractionFactory <179820029+abstractionfactory@users.noreply.github.com>
Signed-off-by: AbstractionFactory <179820029+abstractionfactory@users.noreply.github.com>
Signed-off-by: AbstractionFactory <179820029+abstractionfactory@users.noreply.github.com>
Signed-off-by: AbstractionFactory <179820029+abstractionfactory@users.noreply.github.com>
Signed-off-by: AbstractionFactory <179820029+abstractionfactory@users.noreply.github.com>
@abstractionfactory abstractionfactory changed the base branch from main to license-change January 28, 2025 12:27
@ProbstDJakob
Copy link

This looks great. Only thing to add is, since it is now an interactive web server I would suggest always returning a JSON response (unless otherwise state like with the shell script). This would make it easer to handle error cases. For example when trying to fetch version 1.5 via https://api.opentofu-get.pages.dev/tofu/api/1.5.version.json currently no response is supplied, but something like {"status": "error", "reason": "not found"} would probably be better. Same for all other invalid paths within /tofu/api. If this suggestion would be applied, it might also be good to add the status key to all other endpoints, so for example https://api.opentofu-get.pages.dev/tofu/api/1.version.json would return something like {"status": "ok", "version": "1.9.0"} (note: it no more responds with a simple string).

@abstractionfactory
Copy link
Contributor Author

abstractionfactory commented Feb 3, 2025

Hi @ProbstDJakob the files are still just statically generated, there is no interactivity here, otherwise I would have added an error response. Referencing a non-existent version would return with a 404 status code.

We could add a web worker for the purposes of only the error responses maybe? @Yantrio you know more about web workers, what do you think?

@ProbstDJakob
Copy link

Oh ok, then my assumption was wrong, but in most static web servers you can specify a default response (file). This could then contain {"status": "error", "reason": "not found"}.

For example for nginx it is documented here: https://nginx.org/en/docs/http/ngx_http_core_module.html#error_page
And for Caddy: https://caddyserver.com/docs/caddyfile/directives/handle_errors

But in either cases I have not tested how it handles non-HTML files, i.e. if it correctly sets the Content-Type Header to application/json for .json files. How it is handled in various web service providers I am not sure, but it is most likely possible to configure a 404 page, although maybe only for the whole website and not just for parts like /tofu/api/*.

@abstractionfactory
Copy link
Contributor Author

@ProbstDJakob we are using Cloudflare, so I believe that would require a worker. I'll discuss with @Yantrio what's possible here, he knows the Cloudflare ecosystem better than I do.

@cam72cam
Copy link
Member

@abstractionfactory this is now unblocked

@abstractionfactory abstractionfactory changed the base branch from license-change to main February 12, 2025 21:17
@abstractionfactory abstractionfactory requested a review from a team February 12, 2025 21:18
@@ -35,6 +38,26 @@ func githubResponseToIndex(response github.ReleasesResponse) *Index {
return files
}(),
}
result.Versions[i] = verStruct
parts := strings.Split(ver, "-")
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to use something like mod/semver for this parsing?

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 intentionally didn't because a lot more tools depend on how we name our versions, not everything goes that semver allows.

result[version.ID+"/index.html"], err = renderTemplate(releaseTemplate, version)
if err != nil {
return nil, fmt.Errorf("failed to render release template for version %s: %w", version.ID, err)
}
}
result["api.txt"] = []byte(strings.Join(versionIDList, "\n"))
Copy link
Member

Choose a reason for hiding this comment

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

Sug: versions.txt as it's not structured or similar to api.json

Copy link
Contributor Author

@abstractionfactory abstractionfactory Feb 13, 2025

Choose a reason for hiding this comment

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

We can do that, although in retrospect I'm not particularly happy with using the /api.json either, it should have rather been /tofu/api/versions.json or similar, but there's no fixing that now. Alternatively, we could also transition to the /api endpoint and deprecate /tofu/api.json, which would be much cleaner.

return nil, fmt.Errorf("failed to marshal version files API file for %s: %w", version.ID, err)
}

result["api/"+ver+".version.txt"] = []byte(version.ID)
Copy link
Member

Choose a reason for hiding this comment

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

What does the txt version of these actually give us / has it been requested?

Copy link
Contributor Author

@abstractionfactory abstractionfactory Feb 13, 2025

Choose a reason for hiding this comment

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

It lets shell scripts easily figure out what versions are available. JSON parsing usually depends on jq being present, which is not a given, especially not in a CI environment. (Currently the install-opentofu.sh make very few assumptions about the tools being present.) So, the user can specify install-opentofu.sh --version 1.7 and it will fetch the latest 1.7 version once using this API is implemented. This has been requested in #30.

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.

3 participants