blob: bf01b56006c74daddf7c445982cbbb68462c7504 [file] [log] [blame]
package impl
import (
"fmt"
"os"
"path/filepath"
"strings"
vsecurity "veyron/security"
vflag "veyron/security/flag"
"veyron/security/serialization"
inode "veyron/services/mgmt/node"
"veyron/services/mgmt/node/config"
"veyron2/ipc"
"veyron2/rt"
"veyron2/security"
"veyron2/services/mgmt/node"
"veyron2/verror"
"veyron2/vlog"
)
// internalState wraps state shared between different node manager
// invocations.
type internalState struct {
callback *callbackState
updating *updatingState
}
// dispatcher holds the state of the node manager dispatcher.
type dispatcher struct {
auth security.Authorizer
// internal holds the state that persists across RPC method invocations.
internal *internalState
// config holds the node manager's (immutable) configuration state.
config *config.State
}
const (
appsSuffix = "apps"
nodeSuffix = "nm"
configSuffix = "cfg"
)
var (
errInvalidSuffix = verror.BadArgf("invalid suffix")
errOperationFailed = verror.Internalf("operation failed")
errInProgress = verror.Existsf("operation in progress")
errIncompatibleUpdate = verror.BadArgf("update failed: mismatching app title")
errUpdateNoOp = verror.NotFoundf("no different version available")
errNotExist = verror.NotFoundf("object does not exist")
errInvalidOperation = verror.BadArgf("invalid operation")
errInvalidBlessing = verror.BadArgf("invalid claim blessing")
)
// NewDispatcher is the node manager dispatcher factory.
func NewDispatcher(config *config.State) (*dispatcher, error) {
if err := config.Validate(); err != nil {
return nil, fmt.Errorf("Invalid config %v: %v", config, err)
}
d := &dispatcher{
internal: &internalState{
callback: newCallbackState(config.Name),
updating: newUpdatingState(),
},
config: config,
}
// Prefer ACLs in the nodemanager data directory if they exist.
if data, sig, err := d.getACLFiles(os.O_RDONLY); err != nil {
if d.auth = vflag.NewAuthorizerOrDie(); d.auth == nil {
// If there are no specified ACLs we grant nodemanager access to all
// principal until it is claimed.
d.auth = vsecurity.NewACLAuthorizer(vsecurity.OpenACL())
}
} else {
defer data.Close()
defer sig.Close()
reader, err := serialization.NewVerifyingReader(data, sig, rt.R().Identity().PublicKey())
if err != nil {
return nil, fmt.Errorf("Failed to read nodemanager ACL file:%v", err)
}
acl, err := vsecurity.LoadACL(reader)
if err != nil {
return nil, fmt.Errorf("Failed to load nodemanager ACL:%v", err)
}
d.auth = vsecurity.NewACLAuthorizer(acl)
}
return d, nil
}
func (d *dispatcher) getACLFiles(flag int) (aclData *os.File, aclSig *os.File, err error) {
nodedata := filepath.Join(d.config.Root, "node-manager", "node-data")
perm := os.FileMode(0700)
if err = os.MkdirAll(nodedata, perm); err != nil {
return
}
if aclData, err = os.OpenFile(filepath.Join(nodedata, "acl.nodemanager"), flag, perm); err != nil {
return
}
if aclSig, err = os.OpenFile(filepath.Join(nodedata, "acl.signature"), flag, perm); err != nil {
return
}
return
}
func (d *dispatcher) claimNodeManager(id security.PublicID) error {
// TODO(gauthamt): Should we start trusting these identity providers?
if id.Names() == nil {
vlog.Errorf("Identity provider for device claimer is not trusted")
return errOperationFailed
}
rt.R().PublicIDStore().Add(id, security.AllPrincipals)
// Create ACLs to transfer nodemanager permissions to the provided identity.
acl := security.ACL{In: make(map[security.BlessingPattern]security.LabelSet)}
for _, name := range id.Names() {
acl.In[security.BlessingPattern(name)] = security.AllLabels
}
d.auth = vsecurity.NewACLAuthorizer(acl)
// Write out the ACLs so that it will persist across restarts.
data, sig, err := d.getACLFiles(os.O_CREATE | os.O_RDWR)
if err != nil {
vlog.Errorf("Failed to create ACL files:%v", err)
return errOperationFailed
}
writer, err := serialization.NewSigningWriteCloser(data, sig, rt.R().Identity(), nil)
if err != nil {
vlog.Errorf("Failed to create NewSigningWriteCloser:%v", err)
return errOperationFailed
}
if err = vsecurity.SaveACL(writer, acl); err != nil {
vlog.Errorf("Failed to SaveACL:%v", err)
return errOperationFailed
}
if err = writer.Close(); err != nil {
vlog.Errorf("Failed to Close() SigningWriteCloser:%v", err)
return errOperationFailed
}
return nil
}
// DISPATCHER INTERFACE IMPLEMENTATION
func (d *dispatcher) Lookup(suffix, method string) (ipc.Invoker, security.Authorizer, error) {
components := strings.Split(suffix, "/")
for i := 0; i < len(components); i++ {
if len(components[i]) == 0 {
components = append(components[:i], components[i+1:]...)
i--
}
}
if len(components) == 0 {
return nil, nil, errInvalidSuffix
}
// The implementation of the node manager is split up into several
// invokers, which are instantiated depending on the receiver name
// prefix.
var receiver interface{}
switch components[0] {
case nodeSuffix:
receiver = node.NewServerNode(&nodeInvoker{
callback: d.internal.callback,
updating: d.internal.updating,
config: d.config,
disp: d,
})
case appsSuffix:
receiver = node.NewServerApplication(&appInvoker{
callback: d.internal.callback,
config: d.config,
suffix: components[1:],
})
case configSuffix:
if len(components) != 2 {
return nil, nil, errInvalidSuffix
}
receiver = inode.NewServerConfig(&configInvoker{
callback: d.internal.callback,
suffix: components[1],
})
default:
return nil, nil, errInvalidSuffix
}
return ipc.ReflectInvoker(receiver), d.auth, nil
}