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

[RFC] cloud: add AwsUploader.Result() with structured info #1213

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mvo5
Copy link
Contributor

@mvo5 mvo5 commented Feb 12, 2025

[draft: needs tests and discussion, could also wait until we have a GCP uploader so that we know if this translates to that reasonablly well]

This commit adds a new AwsUploader.Result() call that allows to get structured information about the upload result. This is needed for e.g. osbuild-composer.

It is not part of the UploadAndRegister() call because the exact information about the result is cloud specific. Having a common uploader interface is nice as it simplifies/unifies the common code paths (eg. [0]). So this tries to be find the right balance. Just like each uploader will have a different NewUploader() with different arguments the Result() will also be slightly different for them all.

[0] https://github.com/osbuild/image-builder-cli/blob/main/cmd/image-builder/upload.go#L36

This commit adds a new `AwsUploader.Result()` call that allows to
get structured information about the upload result. This is needed
for e.g. osbuild-composer.

It is not part of the `UploadAndRegister()` call because the exact
information about the result is cloud specific. Having a common
uploader interface is nice as it simplifies/unifies the common code
paths. So this tries to be find the right balance. Just like each
uploader will have a different `NewUploader()` with different
arguments the `Result()` will also be slightly different for them
all.
@mvo5 mvo5 requested a review from thozza February 12, 2025 10:48
Copy link
Member

@thozza thozza left a comment

Choose a reason for hiding this comment

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

Thanks.

I was thinking more along the lines of extending the Uploader interface either by returning the results from the UploadAndRegister or adding a Result() method to the interface. That has the downside of requiring the method signature to be the same for all cloud-specific Uploaders.

While the implied expectation is probably that the Uploader instance will be for single use, nothing prevents one from using it more than once. The action may fail if the image with the given name already exists (I'm not sure if this is a problem since, in AWS, the name is just a tag). If it does not, the AMI in the result would be overwritten.

I quickly drafted my idea so that we also have a different version. And yeah, it is similar to how we handled target results in composer, but I found that approach reasonably clean.

@mvo5
Copy link
Contributor Author

mvo5 commented Feb 12, 2025

Thanks.

I was thinking more along the lines of extending the Uploader interface either by returning the results from the UploadAndRegister or adding a Result() method to the interface. That has the downside of requiring the method signature to be the same for all cloud-specific Uploaders.

Yeah, its an interessting problem. I think in practice by returning the result from UploaderAndRegister() we will return an interface{} as the (cloud specific) Result and then the caller needs to cast it (sadly go makes this hard). The caller knows what cloud it used so that is fine. But it seems equivalent to having to call a (cloud specific) Result() method that will give us a compile time type-safe result as the benefit (and arguably we already need cloud specific NewUploader() so this is not fully abstracted already anyway).

While the implied expectation is probably that the Uploader instance will be for single use, nothing prevents one from using it more than once. The action may fail if the image with the given name already exists (I'm not sure if this is a problem since, in AWS, the name is just a tag). If it does not, the AMI in the result would be overwritten.

I agree that its not super nice, but maybe not such a big deal, Result() would simply the last result, we could even rename it accordingly. We could of course also change the code to prevent multiple uploads but that seems not necessary.

I quickly drafted my idea so that we also have a different version. And yeah, it is similar to how we handled target results in composer, but I found that approach reasonably clean.

I am not a fan of the IsUploadResult pattern (at all), if we go this route I would really like to avoid using them (but then maybe I'm missing something about the pattern).

@thozza
Copy link
Member

thozza commented Feb 13, 2025

I've created https://github.com/orgs/osbuild/discussions/43 to discuss this.

@achilleas-k achilleas-k changed the title [RFC] cloud: add AwsUploader.Result() with strutured info [RFC] cloud: add AwsUploader.Result() with structured info Feb 13, 2025
return nil
}

func (au *AwsUploader) Result() (ami, region string, err error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

As I commented in the discussion, I believe that this generalization should not be done this way. It exposes the data directly, what this should return instead is an interface that can be rendered (shown to the user) in various ways. Data can be either pulled out of it via getter, or pushed like a formatter that injects data into a common structure. Example of a pull approach that could be suitable for a CLI caller:

type Value struct {
 K string
 V string
}

type ValuesResult interface {
 Values() []Value
}

Or push approach:

type ResultWriter interface {
 WriteResults(io.Writer)
}

Where results are literally written as text/json or whatever.

@mvo5
Copy link
Contributor Author

mvo5 commented Feb 14, 2025

It seems we are not really reaching consensus just yet, what we could do is add one (or two) more clouds (gcp first) and apply what we learned and get back to this discussion?

@croissanne
Copy link
Member

croissanne commented Feb 14, 2025

I'd be interested to see another cloud or two. But if it's just to pass around within go code (not being serialised), I'm erring on the side of no isFoo interfaces, unless that interface actually has a meaningful abstraction.

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.

4 participants