Documentation
¶
Overview ¶
Package updater aims to simplify omaha-powered updates.
Its goal is to abstract many of the omaha-protocol details, so users can perform updates without having to understand the omaha protocal internals.
Since the omaha protocol is very powerful, it supports many options that are beyond the scope of this package. So the updater package assumes that there's only one single application involved in the update, and that the update is being performed for one single instance of that application.
It also simplifies the update information that is represented by an omaha response, but allows to retrieve the actual OmahaResponse object if needed.
Basics:
Each update has four main parts involved:
- The application: represents the software getting updated;
- The instance: represents the instance of the application that's getting updated.
- The instance version: this is used by the server to decide how to respond (whether there's a new version available or not).
- The channel: an application may have different channels of releases (this is typical "beta", "stable", etc.).
The way omaha managed updates work is that omaha responds to update checks, and relies on the information given by the application's instance to keep track of each update's state. So the basic workflow for using this updater package is:
- Check for an update, if there's one available then. If there is not, try again later.
- Inform the server we're started it (this is done by sending a progress report of "download started").
- Actually perform the download or whatever represents fetching the update's parts.
- Inform the server that the download is finished it and that we're applying the update (this is done by sending a progress report of "installation started").
- Apply the update (this deeply depends on what each application does, but may involve extracting files into locations, migrating configuration, etc.).
- Inform the server that the update installation is finished; run the new version of the application and report that the update is now complete (these are two different progress reports and may involve running).
Note that if the update requires a restart, then there's a specific progress report for that. The caller is also responsible for keeping any local state the update implementation needs (like e.g. knowing that a restart has happened, or that the version if now running).
Index ¶
Examples ¶
Constants ¶
const ( ProgressDownloadStarted progress = iota ProgressDownloadFinished ProgressInstallationStarted ProgressInstallationFinished ProgressUpdateComplete ProgressUpdateCompleteAndRestarted ProgressError )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct { OmahaURL string AppID string Channel string InstanceID string InstanceVersion string Debug bool OmahaReqHandler OmahaRequestHandler }
Config is used to configure new updater instance.
type HTTPDoer ¶
HTTPDoer interface allows the user to create their custom implementation to handle proxies, retries etc.
type NoUpdateError ¶
NoUpdateError is returned by TryUpdate when no update is available for app.
func (NoUpdateError) Error ¶
func (e NoUpdateError) Error() string
type OmahaRequestHandler ¶
type OmahaRequestHandler interface {
Handle(ctx context.Context, url string, req *omaha.Request) (*omaha.Response, error)
}
OmahaRequestHandler wraps the Handle function which takes in context, url, omaha.Request and returns omaha.Response.
func NewOmahaRequestHandler ¶
func NewOmahaRequestHandler(client HTTPDoer) OmahaRequestHandler
NewOmahaRequestHandler returns a OmahaRequestHandler which uses the HTTPDoer client to handle the post request to the Omaha server. If nil is passed, then http.DefaultClient is used.
type UpdateHandler ¶
type UpdateHandler interface { FetchUpdate(ctx context.Context, info UpdateInfo) error ApplyUpdate(ctx context.Context, info UpdateInfo) error }
UpdateHandler is an interface that wraps the FetchUpdate and ApplyUpdate command. Both FetchUpdate and ApplyUpdate take context and UpdateInfo as args and must return non-nil error if fetching or applying the update fails.
type UpdateInfo ¶
type UpdateInfo struct { HasUpdate bool Version string UpdateStatus string AppID string URLs []string Packages []*omaha.Package // contains filtered or unexported fields }
UpdateInfo wraps helper functions and fields to fetch specific values from the omaha response that was recieved for check if any new update exists request.
func (*UpdateInfo) OmahaResponse ¶
func (u *UpdateInfo) OmahaResponse() *omaha.Response
OmahaReponse returns the raw omaha response.
func (*UpdateInfo) Package ¶
func (u *UpdateInfo) Package() *omaha.Package
Package returns the first package from the omaha response, returns nil if the package is not present in the omaha response.
func (*UpdateInfo) URL ¶
func (u *UpdateInfo) URL() string
URL returns the first update URL in the omaha response, returns "" if the URL is not present in the omaha response.
type Updater ¶
type Updater interface { // SendOmahaRequest sends raw Omaha request provided to the Omaha server. SendOmahaRequest(ctx context.Context, req *omaha.Request) (*omaha.Response, error) // SendOmahaEvent sends Omaha event request to the Omaha server. SendOmahaEvent(ctx context.Context, event *omaha.EventRequest) (*omaha.Response, error) // CheckForUpdates checks if there are new updated versions for the application. CheckForUpdates(ctx context.Context) (*UpdateInfo, error) // ReportProgress reports progress of update to the Omaha server. ReportProgress(ctx context.Context, progress progress) error // ReportError reports errors with custom error code. ReportError(ctx context.Context, errorCode *int) error // CompleteUpdate takes the UpdateInfo, updates the application // version with the UpdateInfo version and reports ProgressUpdateComplete to // the Omaha server. CompleteUpdate(ctx context.Context, info *UpdateInfo) error // TryUpdate function takes an implementation of UpdateHandler // and runs the complete flow from checking updates to reporting update status. TryUpdate(ctx context.Context, handler UpdateHandler) error // InstanceVersion returns the current version of the application. InstanceVersion() string // SetInstanceVersion takes a string and sets it as the application version. SetInstanceVersion(version string) }
Updater interface wraps functions required to update an application
Example ¶
ExampleUpdater shows how to use the updater package to update an application manually.
package main import ( "context" "fmt" "github.com/google/uuid" "github.com/kinvolk/nebraska/updater" ) func someFunctionThatDownloadsAFile(ctx context.Context, url string) (string, error) { // Download file logic goes here return "/tmp/downloads/examplefile.txt", nil } func someFunctionThatExtractsTheUpdateAndInstallIt(ctx context.Context, filePath string) error { // Extract and install update logic goes here return nil } // ExampleUpdater shows how to use the updater package to // update an application manually. func main() error { conf := updater.Config{ OmahaURL: "http://test.omahaserver.com/v1/update/", AppID: "application_id", Channel: "stable", InstanceID: uuid.NewString(), InstanceVersion: "0.0.1", } appUpdater, err := updater.New(conf) if err != nil { return fmt.Errorf("init updater: %w", err) } ctx := context.TODO() updateInfo, err := appUpdater.CheckForUpdates(ctx) if err != nil { return fmt.Errorf("checking updates for app: %q, err: %w", conf.AppID, err) } if !updateInfo.HasUpdate { return fmt.Errorf("No update exists for the application") } // So we got an update, let's report we'll start downloading it. if err := appUpdater.ReportProgress(ctx, updater.ProgressDownloadStarted); err != nil { if progressErr := appUpdater.ReportError(ctx, nil); progressErr != nil { fmt.Println("Reporting progress error:", progressErr) } return fmt.Errorf("reporting download started: %w", err) } // This should be implemented by the caller. filePath, err := someFunctionThatDownloadsAFile(ctx, updateInfo.URL()) if err != nil { // Oops something went wrong. if progressErr := appUpdater.ReportError(ctx, nil); progressErr != nil { fmt.Println("reporting error:", progressErr) } return fmt.Errorf("downloading update: %w", err) } // The download was successful, let's inform that to the Omaha server. if err := appUpdater.ReportProgress(ctx, updater.ProgressDownloadFinished); err != nil { if progressErr := appUpdater.ReportError(ctx, nil); progressErr != nil { fmt.Println("Reporting progress error:", progressErr) } return fmt.Errorf("reporting download finished: %w", err) } // We got our update file, let's install it! if err := appUpdater.ReportProgress(ctx, updater.ProgressInstallationStarted); err != nil { if progressErr := appUpdater.ReportError(ctx, nil); progressErr != nil { fmt.Println("reporting progress error:", progressErr) } return fmt.Errorf("reporting installation started: %w", err) } // This should be your own implementation. if err := someFunctionThatExtractsTheUpdateAndInstallIt(ctx, filePath); err != nil { // Oops something went wrong. if progressErr := appUpdater.ReportError(ctx, nil); progressErr != nil { fmt.Println("Reporting error:", progressErr) } return fmt.Errorf("applying update: %w", err) } if err := appUpdater.CompleteUpdate(ctx, updateInfo); err != nil { if progressErr := appUpdater.ReportError(ctx, nil); progressErr != nil { fmt.Println("reporting progress error:", progressErr) } return fmt.Errorf("reporting complete update: %w", err) } return nil }
Output:
Example (WithUpdateHandler) ¶
ExampleUpdater_withUpdateHandler shows how to use the updater package to update an application automatically using exampleUpdateHandler.
package main import ( "context" "fmt" "github.com/google/uuid" "github.com/kinvolk/nebraska/updater" ) type exampleUpdateHandler struct{} func (e exampleUpdateHandler) FetchUpdate(ctx context.Context, info updater.UpdateInfo) error { // download, err := someFunctionThatDownloadsAFile(ctx, info.GetURL()) // if err != nil { // return err // } return nil } func (e exampleUpdateHandler) ApplyUpdate(ctx context.Context, info updater.UpdateInfo) error { // err := someFunctionThatExtractsTheUpdateAndInstallIt(ctx, getDownloadFile(ctx)) // if err != nil { // // Oops something went wrong // return err // } // err := someFunctionThatExitsAndRerunsTheApp(ctx) // if err != nil { // // Oops something went wrong // return err // } return nil } // ExampleUpdater_withUpdateHandler shows how to use the updater package to // update an application automatically using exampleUpdateHandler. func main() error { conf := updater.Config{ OmahaURL: "http://test.omahaserver.com/v1/update/", AppID: "application_id", Channel: "stable", InstanceID: uuid.NewString(), InstanceVersion: "0.0.1", } appUpdater, err := updater.New(conf) if err != nil { return fmt.Errorf("init updater: %w", err) } ctx := context.TODO() if err := appUpdater.TryUpdate(ctx, exampleUpdateHandler{}); err != nil { return fmt.Errorf("trying update: %w", err) } return nil }
Output: