Beispiel #1
0
func (cts *ClientTestServer) logReq(w http.ResponseWriter, r *http.Request, id string) {
	log.Infof("got log request deployment ID: %v, %v", id, r)
	cts.Log.Called = true

	if !isMethod(http.MethodPut, w, r) {
		return
	}

	if !isContentType("application/json", w, r) {
		return
	}

	if !cts.verifyAuth(w, r) {
		return
	}

	logs, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Errorf("error when receiving logs: %v", err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	log.Infof("got logs: %v", logs)
	cts.Log.Logs = logs
	w.WriteHeader(http.StatusNoContent)
}
Beispiel #2
0
// Write writes data `p` to underlying block device. Will automatically open
// the device in a write mode. Otherwise, behaves like io.Writer.
func (bd *BlockDevice) Write(p []byte) (int, error) {
	if bd.out == nil {
		log.Infof("opening device %s for writing", bd.Path)
		out, err := os.OpenFile(bd.Path, os.O_WRONLY, 0)
		if err != nil {
			return 0, err
		}

		size, err := BlockDeviceGetSizeOf(out)
		if err != nil {
			log.Errorf("failed to read block device size: %v", err)
			out.Close()
			return 0, err
		}
		log.Infof("partition %s size: %v", bd.Path, size)

		bd.out = out
		bd.w = &utils.LimitedWriter{
			W: out,
			N: size,
		}
	}

	w, err := bd.w.Write(p)
	if err != nil {
		log.Errorf("written %v out of %v bytes to partition %s: %v",
			w, len(p), bd.Path, err)
	}
	return w, err
}
Beispiel #3
0
func (cts *ClientTestServer) inventoryReq(w http.ResponseWriter, r *http.Request) {
	log.Infof("got inventory request %v", r)
	cts.Inventory.Called = true

	if !isMethod(http.MethodPatch, w, r) {
		return
	}

	if !isContentType("application/json", w, r) {
		return
	}

	if !cts.verifyAuth(w, r) {
		return
	}

	var attrs []client.InventoryAttribute

	if err := fromJSON(r.Body, &attrs); err != nil {
		log.Errorf("failed to parse attrs data: %v", err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	log.Infof("got attrs: %v", attrs)
	cts.Inventory.Attrs = attrs
	w.WriteHeader(http.StatusOK)
}
Beispiel #4
0
func (a *AuthorizedState) Handle(ctx *StateContext, c Controller) (State, bool) {
	// restore previous state information
	sd, err := LoadStateData(ctx.store)

	// tricky part - try to figure out if there's an update in progress, if so
	// proceed to UpdateCommitState; in case of errors that occur either now or
	// when the update was being feched/installed previously, try to handle them
	// gracefully

	// handle easy case first, no update info present, means no update in progress
	if err != nil && os.IsNotExist(err) {
		log.Debug("no update in progress, proceed")
		return inventoryUpdateState, false
	}

	if err != nil {
		log.Errorf("failed to restore update information: %v", err)
		me := NewFatalError(errors.Wrapf(err, "failed to restore update information"))

		// report update error with unknown deployment ID
		// TODO: fill current image ID?
		return NewUpdateErrorState(me, client.UpdateResponse{
			ID: "unknown",
		}), false
	}

	log.Infof("handling state: %v", sd.Id)

	// chack last known status
	switch sd.Id {
	// update process was finished; check what is the status of update
	case MenderStateReboot:
		return NewUpdateVerifyState(sd.UpdateInfo), false

		// update prosess was initialized but stopped in the middle
	case MenderStateUpdateFetch, MenderStateUpdateInstall:
		// TODO: for now we just continue sending error report to the server
		// in future we might want to have some recovery option here
		me := NewFatalError(errors.New("update process was interrupted"))
		return NewUpdateErrorState(me, sd.UpdateInfo), false

		// there was some error while reporting update status
	case MenderStateUpdateStatusReport:
		log.Infof("restoring update status report state")
		if sd.UpdateStatus != client.StatusFailure &&
			sd.UpdateStatus != client.StatusSuccess {
			return NewUpdateStatusReportState(sd.UpdateInfo, client.StatusError), false
		}
		// check what is exact state of update before reporting anything
		return NewUpdateVerifyState(sd.UpdateInfo), false

		// this should not happen
	default:
		log.Errorf("got invalid update state: %v", sd.Id)
		me := NewFatalError(errors.New("got invalid update state"))
		return NewUpdateErrorState(me, sd.UpdateInfo), false
	}
}
Beispiel #5
0
func (usr *UpdateStatusReportState) trySend(send SendData, c Controller) (error, bool) {
	poll := c.GetUpdatePollInterval()
	if poll == 0 {
		poll = 5 * time.Second
	}
	maxAttempts := int(maxReportSendingTime / poll)

	for usr.triesSendingReport < maxAttempts {
		log.Infof("attempting to report data of deployment [%v] to the backend;"+
			" deployment status [%v], try %d",
			usr.update.ID, usr.status, usr.triesSendingReport)
		if err := send(usr.update, usr.status, c); err != nil {
			log.Errorf("failed to report data %v: %v", usr.status, err.Cause())
			// error reporting status or sending logs;
			// wait for some time before trying again
			if wc := usr.Wait(c.GetUpdatePollInterval()); wc == false {
				// if the waiting was interrupted don't increase triesSendingReport
				return nil, true
			}
			usr.triesSendingReport++
			continue
		}
		// reset counter
		usr.triesSendingReport = 0
		return nil, false
	}
	return NewFatalError(errors.New("error sending data to server")), false
}
Beispiel #6
0
func (m *mender) InventoryRefresh() error {
	ic := client.NewInventory()
	idg := NewInventoryDataRunner(path.Join(getDataDirPath(), "inventory"))

	idata, err := idg.Get()
	if err != nil {
		// at least report device type
		log.Errorf("failed to obtain inventory data: %s", err.Error())
	}

	reqAttr := []client.InventoryAttribute{
		{"device_type", m.GetDeviceType()},
		{"image_id", m.GetCurrentImageID()},
		{"client_version", VersionString()},
	}

	if idata == nil {
		idata = make(client.InventoryData, 0, len(reqAttr))
	}
	idata.ReplaceAttributes(reqAttr)

	if idata == nil {
		log.Infof("no inventory data to submit")
		return nil
	}

	err = ic.Submit(m.api.Request(m.authToken), m.config.ServerURL, idata)
	if err != nil {
		return errors.Wrapf(err, "failed to submit inventory data")
	}

	return nil
}
Beispiel #7
0
func (cts *ClientTestServer) statusReq(w http.ResponseWriter, r *http.Request, id string) {
	log.Infof("got status request deployment ID: %v, %v", id, r)
	cts.Status.Called = true

	if !isMethod(http.MethodPut, w, r) {
		return
	}

	if !isContentType("application/json", w, r) {
		return
	}

	if !cts.verifyAuth(w, r) {
		return
	}

	var report client.StatusReport
	if err := fromJSON(r.Body, &report); err != nil {
		log.Errorf("failed to parse status data: %v", err)
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	cts.Status.Status = report.Status

	w.WriteHeader(http.StatusNoContent)
}
Beispiel #8
0
func (e *ErrorState) Handle(ctx *StateContext, c Controller) (State, bool) {
	log.Infof("handling error state, current error: %v", e.cause.Error())
	// decide if error is transient, exit for now
	if e.cause.IsFatal() {
		return doneState, false
	}
	return initState, false
}
Beispiel #9
0
func (cts *ClientTestServer) updateDownloadReq(w http.ResponseWriter, r *http.Request) {
	log.Infof("got update download request %v", r)
	cts.UpdateDownload.Called = true

	if !isMethod(http.MethodGet, w, r) {
		return
	}

}
Beispiel #10
0
func LoadConfig(configFile string) (*menderConfig, error) {
	var confFromFile menderConfig

	if err := readConfigFile(&confFromFile, configFile); err != nil {
		// Some error occured while loading config file.
		// Use default configuration.
		log.Infof("Error loading configuration from file: %s (%s)", configFile, err.Error())
		return nil, err
	}

	if confFromFile.DeviceKey == "" {
		log.Infof("device key path not configured, fallback to default %s",
			defaultKeyFile)
		confFromFile.DeviceKey = defaultKeyFile
	}

	return &confFromFile, nil
}
Beispiel #11
0
// This will be run manually from command line ONLY
func doRootfs(device installer.UInstaller, args runOptionsType, dt string) error {
	var image io.ReadCloser
	var imageSize int64
	var err error
	var upclient client.Updater

	if args == (runOptionsType{}) {
		return errors.New("rootfs called without needed parameters")
	}

	log.Debug("Starting device update.")

	updateLocation := *args.imageFile
	if strings.HasPrefix(updateLocation, "http:") ||
		strings.HasPrefix(updateLocation, "https:") {
		log.Infof("Performing remote update from: [%s].", updateLocation)

		var ac *client.ApiClient
		// we are having remote update
		ac, err = client.New(args.Config)
		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, updateLocation)
		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]", updateLocation)
		image, imageSize, err = FetchUpdateFromFile(updateLocation)

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

	if image == nil || err != nil {
		return errors.Wrapf(err, "rootfs: error while updating image from command line")
	}
	defer image.Close()

	return installer.Install(image, dt, device)
}
Beispiel #12
0
func (m *mender) doBootstrap() menderError {
	if !m.authMgr.HasKey() || m.forceBootstrap {
		log.Infof("device keys not present or bootstrap forced, generating")
		if err := m.authMgr.GenerateKey(); err != nil {
			return NewFatalError(err)
		}

	}

	m.forceBootstrap = false

	return nil
}
Beispiel #13
0
// wait and return true if wait was completed (false if canceled)
func (cs *CancellableState) Wait(wait time.Duration) bool {
	ticker := time.NewTicker(wait)

	defer ticker.Stop()
	select {
	case <-ticker.C:
		log.Debugf("wait complete")
		return true
	case <-cs.cancel:
		log.Infof("wait canceled")
	}

	return false
}
Beispiel #14
0
func (d *device) Rollback() error {
	// first get inactive partition
	inactivePartition, err := d.getInactivePartition()
	if err != nil {
		return err
	}
	log.Infof("setting partition for rollback: %s", inactivePartition)

	err = d.WriteEnv(BootVars{"mender_boot_part": inactivePartition, "upgrade_available": "0"})
	if err != nil {
		return err
	}
	log.Debug("Marking inactive partition as a boot candidate successful.")
	return nil
}
Beispiel #15
0
func InstallRootfs(device UInstaller, dt string) parser.DataHandlerFunc {
	return func(r io.Reader, dev string, uf parser.UpdateFile) error {
		if dev != dt {
			return errors.Errorf("unexpected device type [%v], expected to see [%v]",
				dev, dt)
		}
		log.Infof("installing update %v of size %v", uf.Name, uf.Size)
		err := device.InstallUpdate(ioutil.NopCloser(r), uf.Size)
		if err != nil {
			log.Errorf("update image installation failed: %v", err)
			return err
		}
		return device.EnableUpdatedPartition()
	}
}
Beispiel #16
0
func (cts *ClientTestServer) deploymentsReq(w http.ResponseWriter, r *http.Request) {
	log.Infof("got deployments log/status request %v", r)
	p := r.URL.Path
	s := strings.TrimPrefix(p, "/api/devices/0.1/deployments/device/deployments/")
	if s == p {
		// unchanged, was no prefix?
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	log.Infof("request for %v", s)

	idwhat := strings.SplitN(s, "/", 2)
	id := idwhat[0]
	what := idwhat[1]

	switch {
	case what == "log":
		cts.logReq(w, r, id)
	case what == "status":
		cts.statusReq(w, r, id)
	default:
		w.WriteHeader(http.StatusBadRequest)
	}
}
Beispiel #17
0
func (d *device) InstallUpdate(image io.ReadCloser, size int64) error {

	log.Debugf("Trying to install update of size: %d", size)
	if image == nil || size < 0 {
		return errors.New("Have invalid update. Aborting.")
	}

	inactivePartition, err := d.GetInactive()
	if err != nil {
		return err
	}

	b := &BlockDevice{Path: inactivePartition}

	if bsz, err := b.Size(); err != nil {
		log.Errorf("failed to read size of block device %s: %v",
			inactivePartition, err)
		return err
	} else if bsz < uint64(size) {
		log.Errorf("update (%v bytes) is larger than the size of device %s (%v bytes)",
			size, inactivePartition, bsz)
		return syscall.ENOSPC
	}

	w, err := io.Copy(b, image)
	if err != nil {
		log.Errorf("failed to write image data to device %v: %v",
			inactivePartition, err)
	}

	log.Infof("wrote %v/%v bytes of update to device %v",
		w, size, inactivePartition)

	if cerr := b.Close(); cerr != nil {
		log.Errorf("closing device %v failed: %v", inactivePartition, cerr)
		if err != nil {
			return cerr
		}
	}

	return err
}
Beispiel #18
0
func NewClientTestServer() *ClientTestServer {
	cts := &ClientTestServer{}

	mux := http.NewServeMux()
	mux.HandleFunc("/api/devices/0.1/authentication/auth_requests", cts.authReq)
	mux.HandleFunc("/api/devices/0.1/inventory/device/attributes", cts.inventoryReq)
	mux.HandleFunc("/api/devices/0.1/deployments/device/update", cts.updateReq)
	// mux.HandleFunc("/api/devices/0.1/deployments/device/deployments/%s/log", cts.logReq)
	// mux.HandleFunc("/api/devices/0.1/deployments/device/deployments/%s/status", cts.statusReq)
	mux.HandleFunc("/api/devices/0.1/deployments/device/deployments/", cts.deploymentsReq)
	mux.HandleFunc("/api/devices/0.1/download", cts.updateDownloadReq)
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		log.Infof("fallback request handler, request %v", r)
		w.WriteHeader(http.StatusBadRequest)
	})

	srv := httptest.NewServer(mux)
	cts.Server = srv

	return cts
}
Beispiel #19
0
func (uv *UpdateVerifyState) Handle(ctx *StateContext, c Controller) (State, bool) {

	// start deployment logging
	if err := DeploymentLogger.Enable(uv.update.ID); err != nil {
		return NewUpdateErrorState(NewTransientError(err), uv.update), false
	}

	log.Debug("handle update verify state")

	// look at the update flag
	has, haserr := c.HasUpgrade()
	if haserr != nil {
		log.Errorf("has upgrade check failed: %v", haserr)
		me := NewFatalError(errors.Wrapf(haserr, "failed to perform 'has upgrade' check"))
		return NewUpdateErrorState(me, uv.update), false
	}

	if has {
		if uv.update.Image.YoctoID == c.GetCurrentImageID() {
			log.Infof("successfully running with new image %v", c.GetCurrentImageID())
			// update info and has upgrade flag are there, we're running the new
			// update, everything looks good, proceed with committing
			return NewUpdateCommitState(uv.update), false
		} else {
			// seems like we're running in a different image than expected from update
			// information, best report an error
			log.Errorf("running with image %v, expected updated image %v",
				c.GetCurrentImageID(), uv.update.Image.YoctoID)
			return NewUpdateStatusReportState(uv.update, client.StatusFailure), false
		}
	}

	// HasUpgrade() returned false
	// most probably booting new image failed and u-boot rolledback to
	// previous image
	log.Errorf("update info for deployment %v present, but update flag is not set;"+
		" running rollback image (previous active partition)",
		uv.update.ID)
	return NewUpdateStatusReportState(uv.update, client.StatusFailure), false
}
Beispiel #20
0
func (cts *ClientTestServer) updateReq(w http.ResponseWriter, r *http.Request) {
	log.Infof("got update request %v", r)
	cts.Update.Called = true

	if !isMethod(http.MethodGet, w, r) {
		return
	}

	if !cts.verifyAuth(w, r) {
		return
	}

	switch {
	case cts.Update.Unauthorized == true:
		w.WriteHeader(http.StatusUnauthorized)
	case cts.Update.Has == false:
		w.WriteHeader(http.StatusNoContent)
	case cts.Update.Has == true:
		w.WriteHeader(http.StatusOK)

		if cts.Update.Data.ID == "" {
			cts.Update.Data.ID = "foo"
		}
		if cts.Update.Data.Image.ID == "" {
			cts.Update.Data.Image.ID = "foo"
		}
		if cts.Update.Data.Image.YoctoID == "" {
			cts.Update.Data.Image.YoctoID = "yocto-foo"
		}
		if cts.Update.Data.Image.URI == "" {
			cts.Update.Data.Image.URI = cts.URL + "/download"
		}
		if cts.Update.Data.Image.Checksum == "" {
			cts.Update.Data.Image.Checksum = "123"
		}
		w.Header().Set("Content-Type", "application/json")
		writeJSON(w, &cts.Update.Data)
	}
}
Beispiel #21
0
func (cts *ClientTestServer) authReq(w http.ResponseWriter, r *http.Request) {
	log.Infof("got auth request %v", r)
	cts.Auth.Called = true

	if !isMethod(http.MethodPost, w, r) {
		return
	}

	if !isContentType("application/json", w, r) {
		return
	}

	if cts.Auth.Authorize {
		w.WriteHeader(http.StatusOK)
		if cts.Auth.Token != nil {
			w.Header().Set("Content-Type", "text/plain")
			w.Write(cts.Auth.Token)
		}
	} else {
		w.WriteHeader(http.StatusUnauthorized)
	}

}
Beispiel #22
0
func (m *mender) SetState(s State) {
	log.Infof("Mender state: %v -> %v", m.state.Id(), s.Id())
	m.state = s
}