-
Notifications
You must be signed in to change notification settings - Fork 16
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
Add basic cloudapi support #150
base: main
Are you sure you want to change the base?
Conversation
This PR is stale because it has been open 30 days with no activity. Remove "Stale" label or comment or this will be closed in 7 days. |
Needs to copy arch from the blueprint, if set, to the cloudapi request instead of using the host arch. |
This PR is stale because it has been open 30 days with no activity. Remove "Stale" label or comment or this will be closed in 7 days. |
7341469
to
1a4a012
Compare
1a4a012
to
f3f3464
Compare
- PostRaw and PostJSON functions - GetJSON function - Exists function with test support - ErrorToString function to parse a cloudapi error json response into a string - Mock cloud http client for testing - Tests Related: RHEL-60148
Related: RHEL-60148
This allows you to build weldr-client with a custom version, eg. for development so that the NEVRA will always be greater than the last build. Use it like this: BASEVER=$(grep ^VERSION Makefile | awk '{ print $3 }') VERSION=$BASEVER.$(date +%Y%m%d%H%M) make build-in-podman
This will check to see if the cloudapi socket is available, and if so it will show the Cloud API version number and name of the service. Related: RHEL-60148
Retrieve the cloud API's openapi info. Related: RHEL-60148
This function uses the cloud API to start a compose using a blueprint that is included in the compose request instead of one that has been previously uploaded. Related: RHEL-60124
When a local file is used in place of the blueprint name composer-cli will use the cloud API to upload the blueprint and start the compose. Currently this expects the osbuild-composer server to have the 'localsave' option enabled in its osbuild-composer.service file. Resolves: RHEL-60124
The upload options are passed via the cloud API's image request ImageOptions: https://github.com/osbuild/osbuild-composer/blob/main/internal/cloudapi/v2/openapi.v2.yml#L795 the cloud client code does not do any validation of the format or content, that is all handled by the server which will return an error describing what went wrong if there is a problem. Related: RHEL-60124
The upload format for the cloud API server is different from that used by the weldr API. It is described in the cloud API's openapi schema document in the UploadOptions section: https://github.com/osbuild/osbuild-composer/blob/main/internal/cloudapi/v2/openapi.v2.yml#L1015 Related: RHEL-60124
The ExecutionError already prints 'ERROR' so there is no need for it in the strings passed to it. Related: RHEL-60148
f3f3464
to
a83140d
Compare
Related: RHEL-60123
Related: RHEL-60148
Related: RHEL-60123
The composes built with the cloud API are not stored in the weldr API store so the UUID from the user may belong to one or the other. Check cloud API first, then weldr API and return the weldr API error if it is not found in either place. Note that the result printed is different. Cloud API uses 'success' and 'failure' and WELDR API uses 'FINISHED' and 'FAILED'. Related: RHEL-60124 Related: RHEL-60134
Related: RHEL-60123
Related: RHEL-60148
a83140d
to
06dc9d6
Compare
Added tests for ComposeInfo and ComposeWait |
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.
Partial review, will continue later.
// used to query the server. | ||
func NewClient(ctx context.Context, socket HTTPClient, socketPath string) Client { | ||
// TODO | ||
// - check for valid server path |
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'll assume this is about the socketPath
, what kind of validation do you want to do?
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.
Good question. There's a similar comment in the weldr client version of this. I think it may be old. IIRC I was going to check to make sure the socket existed here, but it ends up being easier to use the Exists() and checkSocketError functions since the commands first try cloud and then fall back to weldr if it isn't available. So I'm pretty sure these TODOs can now go away.
if route[0] == '/' { | ||
route = route[1:] | ||
} | ||
return fmt.Sprintf("%s://%s/%s", c.protocol, c.host, route) |
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 prefer to use net/url
's URL.String()
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.
That would work too, but I prefer to leave it for now. The protocol/host code needs a refactor in a later commit -- it was never hooked up to the cmdline and is always http and localhost. I think we're always going to be using domain sockets for this so they can go away at some point.
|
||
// IsStringInSlice returns true if the string is present, false if not | ||
// slice must be sorted | ||
func IsStringInSlice(slice []string, s string) bool { |
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.
Can this be slices.Contains
instead or was that introduced later than our minimum version?
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 change that in a followup commit that refactors some common things between cloud and weldr into a common package.
} | ||
|
||
// GetContentFilename returns the filename from a content disposition header | ||
func GetContentFilename(header string) (string, error) { |
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 can be mime.ParseMediaType
; you'll get out a map
that contains "filename"
as the key.
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.
Good idea. I'll do that in the followup PR (I move both copies of this into a common location)
} | ||
|
||
// AppendQuery adds the query string to the current url using ? for the first and & for subsequent ones | ||
func AppendQuery(url, query string) string { |
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 can also be done through net/url
.
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.
True. See comment on other url handling.
host string // defaults to localhost | ||
socketPath string | ||
rawFunc func(string, string, int, []byte) // Pass the raw json data to a user function | ||
Test bool // Used to fake the presense of the socket for testing |
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.
Normally we would make Client an interface with an additional test implementation, but I can see why that would be a hassle. Still, it might be nicer if this was a private property that could be set with a separate constructor, NewTestClient()
. Kinda feels strange to have it there to set freely. But I wont block on it.
// Is the blueprint a local file? If so, try to use the cloud API for the compose | ||
f, err := os.Open(args[0]) | ||
if err == nil { | ||
defer f.Close() | ||
|
||
if !root.Cloud.Exists() { | ||
return root.ExecutionError(cmd, "Using a local blueprint requires server support. Check to make sure that the cloudapi socket is enabled.") |
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 probably not a real issue because of extension conventions, but it is conceivable that someone might do:
echo 'name = "myblueprint"' > myblueprint
composer-cli blueprints push myblueprint
composer-cli compose start myblueprint qcow2
and get this error.
if err != nil { | ||
return root.ExecutionError(cmd, "Error starting cloud API compose: %s", err) | ||
} | ||
} 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.
Might be better to check if the error is os.ErrNotExist
than just assuming it's a blueprint name when os.Open()
fails. It could be a permission error, for example, in which case the real error is never shown. Instead of getting a Permission denied
, the user would get some error about the blueprint not existing from osbuild-composer.
This adds support for cloudapi in the existing version of osbuild-composer
composer-cli status show
--wait
to cloudapi compose start and tocomposer-cli wait
command