blob: d72ecc2ff96bb15cfd180a35c59d324143c4fbde [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package device defines interfaces for managing devices and their
// applications.
package device
import (
"time"
"v.io/v23/security"
"v.io/v23/security/access"
"v.io/v23/services/application"
"v.io/v23/services/binary"
"v.io/v23/services/permissions"
"v.io/v23/services/tidyable"
)
// TODO(caprita): Merge with v23/config and v.io/x/ref/lib/exec/config.go.
// Config specifies app configuration that overrides what's in the envelope.
type Config map[string]string
// InstallationState describes the states that an installation can be in at any
// time.
type InstallationState enum {
Active
Uninstalled
}
// InstanceState describes the states that an instance can be in at any
// time.
type InstanceState enum {
Launching
Running
Dying
NotRunning
Updating
Deleted
}
// Application can be used to manage applications on a device. This interface
// will be invoked using an object name that identifies the application and its
// installations and instances where applicable.
//
// An application is defined by a title. An application can have multiple
// installations on a device. The installations are grouped under the same
// application, but are otherwise independent of each other. Each installation
// can have zero or more instances (which can be running or not). The instances
// are independent of each other, and do not share state (like local storage).
// Interaction among instances should occur via Vanadium RPC, facilitated by the
// local mounttable.
//
// The device manager supports versioning of applications. Each installation
// maintains a tree of versions, where a version is defined by a specific
// envelope. The tree structure comes from 'previous version' references: each
// version (except the initial installation version) maintains a reference to
// the version that preceded it. The installation maintains a current version
// reference that is used for new instances. Each update operation on the
// installation creates a new version, sets the previous reference of the new
// version to the current version, and then updates the current version to refer
// to the new version. Each revert operation on the installation sets the
// current version to the previous version of the current version. Each
// instance maintains a current version reference that is used to run the
// instance. The initial version of the instance is set to the current version
// of the installation at the time of instantiation. Each update operation on
// the instance updates the instance's current version to the current version of
// the installation. Each revert operation on the instance updates the
// instance's current version to the previous version of the instance's version.
//
// The Application interface methods can be divided based on their intended
// receiver:
//
// 1) Method receiver is an application:
// - Install()
//
// 2) Method receiver is an application installation:
// - Instantiate()
// - Uninstall()
//
// 3) Method receiver is an application instance:
// - Run()
// - Kill()
// - Delete()
//
// 4) Method receiver is an application installation or instance:
// - Update()
// - Revert()
//
// The following methods complement one another:
// - Install() and Uninstall()
// - Instantiate() and Delete()
// - Run() and Kill()
// - Update() and Revert()
//
//
//
// Examples:
//
// Install Google Maps on the device.
// device/apps.Install("/google.com/appstore/maps", nil, nil) --> "google maps/0"
//
// Create and start an instance of the previously installed maps application
// installation.
// device/apps/google maps/0.Instantiate() --> { "0" }
// device/apps/google maps/0/0.Run()
//
// Create and start a second instance of the previously installed maps
// application installation.
// device/apps/google maps/0.Instantiate() --> { "1" }
// device/apps/google maps/0/1.Run()
//
// Kill and delete the first instance previously started.
// device/apps/google maps/0/0.Kill()
// device/apps/google maps/0/0.Delete()
//
// Install a second Google Maps installation.
// device/apps.Install("/google.com/appstore/maps", nil, nil) --> "google maps/1"
//
// Update the second maps installation to the latest version available.
// device/apps/google maps/1.Update()
//
// Update the first maps installation to a specific version.
// device/apps/google maps/0.UpdateTo("/google.com/appstore/beta/maps")
//
// Finally, an application installation instance can be in one of three abstract
// states: 1) "does not exist/deleted", 2) "running", or 3) "not-running". The
// interface methods transition between these abstract states using the
// following state machine:
//
// apply(Instantiate(), "does not exist") = "not-running"
// apply(Run(), "not-running") = "running"
// apply(Kill(), "running") = "not-running"
// apply(Delete(), "not-running") = "deleted"
type Application interface {
// Object provides GetPermissions/SetPermissions methods to read/modify
// AccessLists for the Application methods. After a device has been
// claimed, only the claimant will be able to modify the AccessLists for
// the device.
permissions.Object
// TODO(caprita): Rather than overriding config and package piecemeal,
// consider providing an envelope override during install.
// Install installs the application identified by the first argument and
// returns an object name suffix that identifies the new installation.
//
// The name argument should be an object name for an application
// envelope. The service it identifies must implement
// repository.Application, and is expected to return either the
// requested version (if the object name encodes a specific version), or
// otherwise the latest available version, as appropriate. This object
// name will be used by default by the Update method, as a source for
// updated application envelopes (can be overriden by setting
// AppOriginConfigKey in the config).
//
// The config argument specifies config settings that will take
// precedence over those present in the application envelope.
//
// The packages argument specifies packages to be installed in addition
// to those specified in the envelope. If a package in the envelope has
// the same key, the package in the packages argument takes precedence.
//
// The returned suffix, when appended to the name used to reach the
// receiver for Install, can be used to control the installation object.
// The suffix will contain the title of the application as a prefix,
// which can then be used to control all the installations of the given
// application.
// TODO(rjkroege): Use customized labels.
Install(name string, config Config, packages application.Packages) (string | error) {access.Write}
// Uninstall uninstalls an application installation.
// The installation must be in state Active.
Uninstall() error {access.Admin}
// Instantiate creates an instance of an application installation.
// The installation must be in state Active.
//
// The server sends the application instance's Public Key on the stream.
// When the client receives the Public Key it must send Blessings back
// to the server. When the instance is created, the server returns the
// instance name to the client.
//
// Client Server
// "object".Instantiate() -->
// <-- InstancePublicKey
// AppBlessings -->
// <-- return InstanceName
Instantiate() stream<BlessClientMessage, BlessServerMessage> (string | error) {access.Read}
// TODO(caprita): Add a new method Shutdown for Device instead of using
// Delete.
// Delete deletes an instance. Once deleted, the instance cannot be
// revived.
// The instance must be in state NotRunning.
//
// If called against a Device, causes the Device to shut itself down.
Delete() error {access.Admin}
// Run begins execution of an application instance.
// The instance must be in state NotRunning.
Run() error {access.Write}
// Kill attempts a clean shutdown an of application instance.
// The instance must be in state Running.
//
// If the deadline is non-zero and the instance in question is still
// running after the given deadline, shutdown of the instance is
// enforced.
//
// If called against a Device, causes the Device to stop itself (which
// may or may not result in a restart depending on the device manager
// setup).
Kill(deadline time.Duration) error {access.Write}
// Update updates an application installation's version to a new version
// created from the envelope at the object name provided during Install.
// If the new application envelope contains a different application
// title, the update does not occur, and an error is returned. The
// installation must be in state Active.
//
// Update updates an application instance's version to the current
// installation version. The instance must be in state NotRunning.
Update() error {access.Admin}
// TODO(caprita): Decide if we keep this in v0.1. If we do, we may want
// to use it over the origin override mechanism in the config, to
// specify a new origin for the app installation.
// UpdateTo updates the application installation(s) to the application
// specified by the object name argument. If the new application
// envelope contains a different application title, the update does not
// occur, and an error is returned.
// The installation must be in state Active.
UpdateTo(name string) error {access.Admin}
// Revert reverts an application installation's version to the previous
// version of its current version. The installation must be in state
// Active.
//
// Revert reverts an application instance's version to the previous
// version of its current version. The instance must be in state
// NotRunning.
Revert() error {access.Admin}
// Debug returns debug information about the application installation or
// instance. This is generally highly implementation-specific, and
// presented in an unstructured form. No guarantees are given about the
// stability of the format, and parsing it programmatically is
// specifically discouraged.
Debug() (string | error) {access.Debug}
// Status returns structured information about the application
// installation or instance.
Status() (Status | error) {access.Read}
}
// Status is returned by the Application Status method.
type Status union {
Instance InstanceStatus
Installation InstallationStatus
Device DeviceStatus
}
// InstallationStatus specifies the Status returned by the Application Status
// method for installation objects.
type InstallationStatus struct {
State InstallationState
Version string
}
// InstanceStatus specifies the Status returned by the Application Status method
// for instance objects.
type InstanceStatus struct {
State InstanceState
Version string
}
// DeviceStatus specifies the Status returned by the Application Status method
// for the device service object.
type DeviceStatus struct {
State InstanceState
Version string
}
// BlessServerMessage is the data type that is streamed from the server to the
// client during an Instantiate method call.
// This is a union to enable backward compatible changes.
type BlessServerMessage union {
// The public key of the instance being blessed. The client must return
// blessings for this key.
InstancePublicKey []byte
}
// BlessClientMessage is the data type that is streamed from the client to the
// server during a Instantiate method call.
// This is a union to enable backward compatible changes.
type BlessClientMessage union {
// Blessings for the application instance.
AppBlessings security.WireBlessings
}
// Description enumerates the profiles that a Device supports.
type Description struct {
// Profiles is a set of names of supported profiles. Each name can
// either be an object name that resolves to a Profile, or can be the
// profile's label, e.g.:
// "profiles/google/cluster/diskfull"
// "linux-media"
//
// Profiles for devices can be provided by hand, but they can also be
// automatically derived by examining the device.
Profiles set[string]
}
// Association is a tuple containing an association between a Vanadium
// identity and a system account name.
type Association struct {
IdentityName string
AccountName string
}
// Claimable represents an uninitialized device with no owner
// (i.e., a device that has no blessings).
//
// Claim is used to claim ownership by blessing the device's private key.
// Devices that have provided a pairing token to the claimer through an
// out-of-band communication channel (eg: display/email) would expect this
// pairing token to be replayed by the claimer.
//
// Once claimed, the device will export the "Device" interface and all methods
// will be restricted to the claimer.
//
// The blessings that the device is to be claimed with is provided
// via the ipc.Granter option in Go.
type Claimable interface {
Claim(pairingToken string) error {access.Admin}
}
// Device can be used to manage a device remotely using an object name that
// identifies it.
type Device interface {
// Each method of the Application interface invoked at the device
// level applies to all applications installed on the device (and
// their installations and instances where applicable).
Application
// The device manager is tidyable.
tidyable.Tidyable
// Describe generates a description of the device.
Describe() (Description | error) {access.Admin}
// IsRunnable checks if the device can execute the given binary.
IsRunnable(description binary.Description) (bool | error) {access.Admin}
// Reset resets the device. If the deadline is non-zero and the device
// in question is still running after the given deadline expired,
// reset of the device is enforced.
Reset(deadline time.Duration) error {access.Admin}
// AssociateAccount associates a local system account name with the provided
// Vanadium identities. It replaces the existing association if one already exists for that
// identity. Setting an AccountName to "" removes the association for each
// listed identity.
AssociateAccount(identityNames []string, accountName string) error {access.Admin}
// ListAssociations returns all of the associations between Vanadium identities
// and system names.
ListAssociations() ([]Association | error) {access.Admin}
}