[RFE] Create client library to handle common updates workflow
Closed this issue · 2 comments
Current situation
Nebraska implements the Omaha protocol and thus any clients should be able to interact with it (i.e. perform update requests, and communicate the state back throughout the update workflow) using it.
Impact
This however requires knowledge of the Omaha protocol itself (what an update request gets as response, what state to send when an update is finished, etc.) and that can become a complex task in itself.
Ideal future situation
We want to lower the barrier for projects to start using Omaha-managed updates, and thus would like to have a library that requires the minimum configuration/code from clients in order to manage updates for a certain product.
For example, at a minimum, users should have to tell this library what the Nebraska/Omaha endpoint is + what to do with a downloaded update payload. All state communication/handling should be done with default settings by the library.
**Implementation options
We can start by making it a go package omahaclient github.com/nebraska/omahaclient/v1
.
type updater struct {
omahaclient.ContainerImageUpdater
}
func (* updater) Deploy(manifest *updater.UpdateManifest) omahaclient.Error {
// Do whatever and then docker run again...
}
// Client initialization
ne := updater.New("https://myserver/app/io.org.App/")
ne.SetUpdater(updater)
ne.SetCurrentVersion("v0.1.2")
ne.SetChannel("beta")
ne.Start()
Maybe my go code above is not the best but hopefully gives the idea.
After ne.Start()
, the updater starts performing update requests to the server. When an update exists, Nebraska responds with a URL and a metadata. Since we are composing a "container image updater", it's default "Download" implementation will pull + verify the new image, and then our implementation will stop a service and run the image again (we get details from the image in the UpdateManifest struct which is TBD).
Functionality we need
Even though Omaha can support several apps in one request, let's focus on one app request.
required
means the update workflow cannot start unless we have this info.
Initialize:
- (required) Set the update endpoint:
updater.New("https://myserver/app/io.org.App/v1/updates")
- Set the app id:
updater.NewFromConfig("https://myserver/app/io.org.App/v1/updates", &updater.Config{AppID: "io.kinvolk.DockerContainerApp"})
, may be alsoupdaterInst.SetAppID("io.kinvolk.DockerContainerApp")
, default is "" - Set the channel: (same as above but with the channel info), default is ""
- Set the current version: (same as above but with the version), default is ""
Update start:
updater.Start()
will set up a timer to check for updates from 5-15 mins initially, and after every failed attempt or finished update will be 45-90 mins.
(We should of course add an updater.Stop
which will stop the update checker timer)
Let's worry about configuring the timer's details later.
Update workflow:
Every time an update check is triggered, and an update is available, then:
- Interpret the update response data and perform the
GetUpdate
step which by default will do the following, based on the update type:
application/vnd.oci.image
-> pull from the container image with the defined digest, from the defined registry
binary
-> download into a temporary location
otherwise -> fail because it's not implemented
- If the
GetUpdate
was successfully run, then callApplyUpdate
which needs to be implemented by the client
Error handling
Any "automatic" behavior (pulling an image, downloading a binary) should automatically report any errors to the server (Omaha events).
If a method implemented by the client returns an error, this error is also sent out to the server.
Besides, we should have updater.SendEvent(...)
if more events (including errors) need to be sent.
For the event/error definitions, I guess we can rely on go-omaha's ones for now...
See also #420 .
Additional information
This was a rough example, maybe other basic functions related to authentication need to be done. Take it with a grain of sault.
Acceptance Criteria:
- lib has documentation/examples
- lib has testing
- lib allows basic update workflow
- lib has been reviewed/checked and approved by CNI
sounds great.
the requirements/outcome:
- anything considered (like openapi/swagger) does not break the existing API used by all the Flatcar machines in the world
- it's very intuitive for users to read and discover how to write code to either Consume from a channel, or publish new content to a channel, using the API and libraries
- it's easy to manage for the project (write once, and generate; as opposed to having to make changes across code and docs manually)
I agree. Please also note that in my description above, it shouldn't affect the server code at all. I am currently writing another issue regarding how to cover different payload response types, and that's where we should be more careful.
For the admin/write CLI we can manage it in the #357 .