Skip to content

Commit

Permalink
Adding download and apply commands
Browse files Browse the repository at this point in the history
  • Loading branch information
mullerch committed Feb 29, 2024
1 parent a4575f5 commit 7651e31
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 1 deletion.
136 changes: 136 additions & 0 deletions app/standalone.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,79 @@ type standaloneData struct {
installers []installer.PayloadUpdatePerformer
}

func DoStandaloneDownload(device *dev.DeviceManager, updateURI string,
clientConfig client.Config,
stateExec statescript.Executor) error {

var image io.ReadCloser
var imageSize int64
var err error
var upclient client.Updater

if strings.HasPrefix(updateURI, "http:") ||
strings.HasPrefix(updateURI, "https:") {
log.Infof("Performing remote update from: [%s].", updateURI)

var ac *client.ApiClient
// we are having remote update
ac, err = client.NewApiClient(clientConfig)
if err != nil {
return errors.New("Can not initialize client for performing network update.")
}
upclient = client.NewUpdate()

log.Debug("Client initialized. Start downloading image.")

image, imageSize, err = upclient.FetchUpdate(ac, updateURI, 0)
log.Debugf("Image downloaded: %d [%v] [%v]", imageSize, image, err)
} else {
// perform update from local file
log.Infof("Start updating from local image file: [%s]", updateURI)
image, imageSize, err = installer.FetchUpdateFromFile(updateURI)

log.Debugf("Fetching update from file results: [%v], %d, %v", image, imageSize, err)
}

if image == nil || err != nil {
return errors.Wrapf(err, "Error while installing Artifact from command line")
}
defer image.Close()

fmt.Fprintf(os.Stdout, "Installing Artifact of size %d...\n", imageSize)
p := utils.NewProgressWriter(imageSize)
tr := io.TeeReader(image, p)

standaloneData, err := doStandaloneInstallStatesDownload(ioutil.NopCloser(tr), device, stateExec)
if err != nil {
return err
}

err = storeStandaloneData(device.Store, standaloneData)
if err != nil {
log.Errorf("Could not update database: %s", err.Error())
return err
}

return nil
}

// This will be run manually from command line ONLY
func DoStandaloneApply(device *dev.DeviceManager, stateExec statescript.Executor, rebootExitCode bool) error {

standaloneData, err := restoreStandaloneData(device)
if err != nil {
log.Errorf("Could not restore standalone data: %s. Have you downloaded an image before applying it?", err.Error())
return err
}

err = doStandaloneInstallStatesApply(standaloneData, device, stateExec, rebootExitCode)
if err != nil {
return err
}

return nil
}

// This will be run manually from command line ONLY
func DoStandaloneInstall(device *dev.DeviceManager, updateURI string,
clientConfig client.Config,
Expand Down Expand Up @@ -180,6 +253,69 @@ func doStandaloneInstallStatesDownload(art io.ReadCloser,
return standaloneData, nil
}

func doStandaloneInstallStatesApply(standaloneData *standaloneData, device *dev.DeviceManager, stateExec statescript.Executor,
rebootExitCode bool) error {

installers := standaloneData.installers

rollbackSupport, err := determineRollbackSupport(installers)
if err != nil {
log.Error(err.Error())
_ = doStandaloneFailureStates(device, standaloneData, stateExec, false, false, true)
return err
}

// ArtifactInstall state
err = stateExec.ExecuteAll("ArtifactInstall", "Enter", false, nil)
if err != nil {
log.Errorf("ArtifactInstall_Enter script failed: %s", err.Error())
callErrorScript("ArtifactInstall", stateExec)
_ = doStandaloneFailureStates(device, standaloneData, stateExec, true, true, true)
return err
}
for _, inst := range installers {
err = inst.InstallUpdate()
if err != nil {
log.Errorf("Installation failed: %s", err.Error())
callErrorScript("ArtifactInstall", stateExec)
_ = doStandaloneFailureStates(device, standaloneData, stateExec, true, true, true)
return err
}
}
err = stateExec.ExecuteAll("ArtifactInstall", "Leave", false, nil)
if err != nil {
log.Errorf("ArtifactInstall_Leave script failed: %s", err.Error())
callErrorScript("ArtifactInstall", stateExec)
doStandaloneFailureStates(device, standaloneData, stateExec, true, true, true)
return err
}

rebootNeeded, err := determineRebootNeeded(installers)
if err != nil {
doStandaloneFailureStates(device, standaloneData, stateExec, true, true, true)
return err
}

if rollbackSupport {
fmt.Println("Use 'commit' to update, or 'rollback' to roll back the update.")
} else {
fmt.Println("Artifact doesn't support rollback. Committing immediately.")
err = DoStandaloneCommit(device, stateExec)
if err != nil {
return err
}
}

if rebootNeeded {
fmt.Println("At least one payload requested a reboot of the device it updated.")
if rebootExitCode {
return ErrorManualRebootRequired
}
}

return nil
}

func doStandaloneInstallStates(art io.ReadCloser,
device *dev.DeviceManager, stateExec statescript.Executor,
rebootExitCode bool) error {
Expand Down
24 changes: 23 additions & 1 deletion cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,26 @@ func SetupCLI(args []string) error {
},
},
},
{
Name: "download",
Usage: "Mender Artifact to download - " +
"local file or a `URL`.",
ArgsUsage: "<IMAGEURL>",
Action: func(ctx *cli.Context) error {
runOptions.imageFile = ctx.Args().First()
if len(runOptions.imageFile) == 0 {
cli.ShowAppHelpAndExit(ctx, 1)
}
return runOptions.handleCLIOptions(ctx)
},
},
{
Name: "apply",
Usage: "Apply the downloaded artifact",
Action: func(ctx *cli.Context) error {
return runOptions.handleCLIOptions(ctx)
},
},
{
Name: "rollback",
Usage: "Rollback current Artifact. Returns (2) " +
Expand Down Expand Up @@ -447,7 +467,7 @@ func SetupCLI(args []string) error {
func (runOptions *runOptionsType) commonCLIHandler(
ctx *cli.Context) (*conf.MenderConfig, error) {

if ctx.Command.Name != "install" && ctx.Args().Len() > 0 {
if (ctx.Command.Name != "install" && ctx.Command.Name != "download") && ctx.Args().Len() > 0 {
return nil, errors.Errorf(
errMsgAmbiguousArgumentsGivenF,
ctx.Args().First())
Expand Down Expand Up @@ -525,6 +545,8 @@ func (runOptions *runOptionsType) handleCLIOptions(ctx *cli.Context) error {
case "show-artifact",
"show-provides",
"install",
"download",
"apply",
"commit",
"rollback":
return handleArtifactOperations(ctx, *runOptions, config)
Expand Down
7 changes: 7 additions & 0 deletions cli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,13 @@ func handleArtifactOperations(ctx *cli.Context, runOptions runOptionsType,
return app.DoStandaloneInstall(deviceManager, runOptions.imageFile,
runOptions.Config, stateExec, runOptions.rebootExitCode)

case "download":
return app.DoStandaloneDownload(deviceManager, runOptions.imageFile,
runOptions.Config, stateExec)

case "apply":
return app.DoStandaloneApply(deviceManager, stateExec, runOptions.rebootExitCode)

case "commit":
return app.DoStandaloneCommit(deviceManager, stateExec)

Expand Down

0 comments on commit 7651e31

Please sign in to comment.