func processMachines(idToMachines map[string][]*state.Machine) map[string]params.MachineStatus { machinesMap := make(map[string]params.MachineStatus) cache := make(map[string]params.MachineStatus) for id, machines := range idToMachines { if len(machines) <= 0 { continue } // Element 0 is assumed to be the top-level machine. hostStatus := makeMachineStatus(machines[0]) machinesMap[id] = hostStatus cache[id] = hostStatus for _, machine := range machines[1:] { parent, ok := cache[state.ParentId(machine.Id())] if !ok { panic("We've broken an assumpution.") } status := makeMachineStatus(machine) parent.Containers[machine.Id()] = status cache[machine.Id()] = status } } return machinesMap }
// NewNetworkerAPI creates a new client-side Networker API facade. func NewNetworkerAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*NetworkerAPI, error) { if !authorizer.AuthMachineAgent() { return nil, common.ErrPerm } getAuthFunc := func() (common.AuthFunc, error) { authEntityTag := authorizer.GetAuthTag().String() return func(tag string) bool { if tag == authEntityTag { // A machine agent can always access its own machine. return true } t, err := names.ParseMachineTag(tag) if err != nil { // Only machine tags are allowed. return false } id := t.Id() for parentId := state.ParentId(id); parentId != ""; parentId = state.ParentId(parentId) { // Until a top-level machine is reached. // TODO(dfc) comparing the two interfaces caused a compiler crash with // gcc version 4.9.0 (Ubuntu 4.9.0-7ubuntu1). Work around the issue // by comparing by string value. if names.NewMachineTag(parentId).String() == authEntityTag { // All containers with the authenticated machine as a // parent are accessible by it. return true } } // Not found authorized machine agent among ancestors of the current one. return false }, nil } return &NetworkerAPI{ st: st, resources: resources, authorizer: authorizer, getAuthFunc: getAuthFunc, }, nil }
func (s *AssignSuite) assertAssignUnitToNewMachineContainerConstraint(c *gc.C) { unit, err := s.wordpress.AddUnit() c.Assert(err, jc.ErrorIsNil) err = unit.AssignToNewMachine() c.Assert(err, jc.ErrorIsNil) machineId := s.assertAssignedUnit(c, unit) c.Assert(state.ParentId(machineId), gc.Not(gc.Equals), "") c.Assert(state.ContainerTypeFromId(machineId), gc.Equals, instance.LXC) }
// NewProvisionerAPI creates a new server-side ProvisionerAPI facade. func NewProvisionerAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*ProvisionerAPI, error) { if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() { return nil, common.ErrPerm } getAuthFunc := func() (common.AuthFunc, error) { isEnvironManager := authorizer.AuthEnvironManager() isMachineAgent := authorizer.AuthMachineAgent() authEntityTag := authorizer.GetAuthTag() return func(tag names.Tag) bool { if isMachineAgent && tag == authEntityTag { // A machine agent can always access its own machine. return true } switch tag := tag.(type) { case names.MachineTag: parentId := state.ParentId(tag.Id()) if parentId == "" { // All top-level machines are accessible by the // environment manager. return isEnvironManager } // All containers with the authenticated machine as a // parent are accessible by it. // TODO(dfc) sometimes authEntity tag is nil, which is fine because nil is // only equal to nil, but it suggests someone is passing an authorizer // with a nil tag. return isMachineAgent && names.NewMachineTag(parentId) == authEntityTag default: return false } }, nil } env, err := st.Environment() if err != nil { return nil, err } urlGetter := common.NewToolsURLGetter(env.UUID(), st) return &ProvisionerAPI{ Remover: common.NewRemover(st, false, getAuthFunc), StatusSetter: common.NewStatusSetter(st, getAuthFunc), StatusGetter: common.NewStatusGetter(st, getAuthFunc), DeadEnsurer: common.NewDeadEnsurer(st, getAuthFunc), PasswordChanger: common.NewPasswordChanger(st, getAuthFunc), LifeGetter: common.NewLifeGetter(st, getAuthFunc), StateAddresser: common.NewStateAddresser(st), APIAddresser: common.NewAPIAddresser(st, resources), EnvironWatcher: common.NewEnvironWatcher(st, resources, authorizer), EnvironMachinesWatcher: common.NewEnvironMachinesWatcher(st, resources, authorizer), InstanceIdGetter: common.NewInstanceIdGetter(st, getAuthFunc), ToolsFinder: common.NewToolsFinder(st, st, urlGetter), st: st, resources: resources, authorizer: authorizer, getAuthFunc: getAuthFunc, }, nil }
// NewNetworkerAPI creates a new server-side Networker API facade. func NewNetworkerAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*NetworkerAPI, error) { if !authorizer.AuthMachineAgent() { return nil, common.ErrPerm } getAuthFunc := func() (common.AuthFunc, error) { authEntityTag := authorizer.GetAuthTag() return func(tag names.Tag) bool { if tag == authEntityTag { // A machine agent can always access its own machine. return true } if _, ok := tag.(names.MachineTag); !ok { // Only machine tags are allowed. return false } id := tag.Id() for parentId := state.ParentId(id); parentId != ""; parentId = state.ParentId(parentId) { // Until a top-level machine is reached. // TODO (thumper): remove the names.Tag conversion when gccgo // implements concrete-type-to-interface comparison correctly. if names.Tag(names.NewMachineTag(parentId)) == authEntityTag { // All containers with the authenticated machine as a // parent are accessible by it. return true } } // Not found authorized machine agent among ancestors of the current one. return false }, nil } return &NetworkerAPI{ st: st, resources: resources, authorizer: authorizer, getAuthFunc: getAuthFunc, }, nil }
// NewProvisionerAPI creates a new server-side ProvisionerAPI facade. func NewProvisionerAPI( st *state.State, resources *common.Resources, authorizer common.Authorizer, ) (*ProvisionerAPI, error) { if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() { return nil, common.ErrPerm } getAuthFunc := func() (common.AuthFunc, error) { isEnvironManager := authorizer.AuthEnvironManager() isMachineAgent := authorizer.AuthMachineAgent() authEntityTag := authorizer.GetAuthTag() return func(tag string) bool { if isMachineAgent && tag == authEntityTag { // A machine agent can always access its own machine. return true } t, err := names.ParseTag(tag, names.MachineTagKind) if err != nil { return false } parentId := state.ParentId(t.Id()) if parentId == "" { // All top-level machines are accessible by the // environment manager. return isEnvironManager } // All containers with the authenticated machine as a // parent are accessible by it. return isMachineAgent && names.NewMachineTag(parentId).String() == authEntityTag }, nil } // Both provisioner types can watch the environment. getCanWatch := common.AuthAlways(true) // Only the environment provisioner can read secrets. getCanReadSecrets := common.AuthAlways(authorizer.AuthEnvironManager()) return &ProvisionerAPI{ Remover: common.NewRemover(st, false, getAuthFunc), StatusSetter: common.NewStatusSetter(st, getAuthFunc), DeadEnsurer: common.NewDeadEnsurer(st, getAuthFunc), PasswordChanger: common.NewPasswordChanger(st, getAuthFunc), LifeGetter: common.NewLifeGetter(st, getAuthFunc), StateAddresser: common.NewStateAddresser(st), APIAddresser: common.NewAPIAddresser(st, resources), ToolsGetter: common.NewToolsGetter(st, getAuthFunc), EnvironWatcher: common.NewEnvironWatcher(st, resources, getCanWatch, getCanReadSecrets), EnvironMachinesWatcher: common.NewEnvironMachinesWatcher(st, resources, getCanReadSecrets), InstanceIdGetter: common.NewInstanceIdGetter(st, getAuthFunc), st: st, resources: resources, authorizer: authorizer, getAuthFunc: getAuthFunc, getCanWatchMachines: getCanReadSecrets, }, nil }
// NewNetworkerAPI creates a new client-side Networker API facade. func NewNetworkerAPI( st *state.State, _ *common.Resources, authorizer common.Authorizer, ) (*NetworkerAPI, error) { if !authorizer.AuthMachineAgent() { return nil, common.ErrPerm } getAuthFunc := func() (common.AuthFunc, error) { authEntityTag := authorizer.GetAuthTag() return func(tag string) bool { if tag == authEntityTag { // A machine agent can always access its own machine. return true } t, err := names.ParseTag(tag, names.MachineTagKind) if err != nil { // Only machine tags are allowed. return false } id := t.Id() for parentId := state.ParentId(id); parentId != ""; parentId = state.ParentId(parentId) { // Until a top-level machine is reached. if names.NewMachineTag(parentId).String() == authEntityTag { // All containers with the authenticated machine as a // parent are accessible by it. return true } } // Not found authorized machine agent among ancestors of the current one. return false }, nil } return &NetworkerAPI{ st: st, authorizer: authorizer, getAuthFunc: getAuthFunc, }, nil }
func (s *AssignSuite) assertAssignUnitNewPolicyNoContainer(c *gc.C) { _, err := s.State.AddMachine("quantal", state.JobHostUnits) // available machine c.Assert(err, jc.ErrorIsNil) unit, err := s.wordpress.AddUnit() c.Assert(err, jc.ErrorIsNil) err = s.State.AssignUnit(unit, state.AssignNew) c.Assert(err, jc.ErrorIsNil) assertMachineCount(c, s.State, 2) id, err := unit.AssignedMachineId() c.Assert(err, jc.ErrorIsNil) c.Assert(state.ParentId(id), gc.Equals, "") }
// fetchUnitMachineIds returns a set of IDs for machines that // the specified units reside on, and those machines' ancestors. func fetchUnitMachineIds(units map[string]map[string]*state.Unit) (set.Strings, error) { machineIds := make(set.Strings) for _, svcUnitMap := range units { for _, unit := range svcUnitMap { if !unit.IsPrincipal() { continue } mid, err := unit.AssignedMachineId() if err != nil { return nil, err } for mid != "" { machineIds.Add(mid) mid = state.ParentId(mid) } } } return machineIds, nil }
func (context *statusContext) processMachine(machines []*state.Machine, host *api.MachineStatus, startIndex int) (nextIndex int) { nextIndex = startIndex + 1 currentHost := host var previousContainer *api.MachineStatus for nextIndex < len(machines) { machine := machines[nextIndex] container := context.makeMachineStatus(machine) if currentHost.Id == state.ParentId(machine.Id()) { currentHost.Containers[machine.Id()] = container previousContainer = &container nextIndex++ } else { if state.NestingLevel(machine.Id()) > state.NestingLevel(previousContainer.Id) { nextIndex = context.processMachine(machines, previousContainer, nextIndex-1) } else { break } } } return }
// StartInstance is specified in the InstanceBroker interface. func (e *environ) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, []network.Info, error) { defer delay() machineId := args.MachineConfig.MachineId logger.Infof("dummy startinstance, machine %s", machineId) if err := e.checkBroken("StartInstance"); err != nil { return nil, nil, nil, err } estate, err := e.state() if err != nil { return nil, nil, nil, err } estate.mu.Lock() defer estate.mu.Unlock() if args.MachineConfig.MachineNonce == "" { return nil, nil, nil, fmt.Errorf("cannot start instance: missing machine nonce") } if _, ok := e.Config().CACert(); !ok { return nil, nil, nil, fmt.Errorf("no CA certificate in environment configuration") } if args.MachineConfig.MongoInfo.Tag != names.NewMachineTag(machineId) { return nil, nil, nil, fmt.Errorf("entity tag must match started machine") } if args.MachineConfig.APIInfo.Tag != names.NewMachineTag(machineId) { return nil, nil, nil, fmt.Errorf("entity tag must match started machine") } logger.Infof("would pick tools from %s", args.Tools) series := args.Tools.OneSeries() idString := fmt.Sprintf("%s-%d", e.name, estate.maxId) addrs := network.NewAddresses(idString+".dns", "127.0.0.1") if estate.preferIPv6 { addrs = append(addrs, network.NewAddress(fmt.Sprintf("fc00::%x", estate.maxId+1), network.ScopeUnknown)) } logger.Debugf("StartInstance addresses: %v", addrs) i := &dummyInstance{ id: instance.Id(idString), addresses: addrs, ports: make(map[network.Port]bool), machineId: machineId, series: series, firewallMode: e.Config().FirewallMode(), state: estate, } var hc *instance.HardwareCharacteristics // To match current system capability, only provide hardware characteristics for // environ machines, not containers. if state.ParentId(machineId) == "" { // We will just assume the instance hardware characteristics exactly matches // the supplied constraints (if specified). hc = &instance.HardwareCharacteristics{ Arch: args.Constraints.Arch, Mem: args.Constraints.Mem, RootDisk: args.Constraints.RootDisk, CpuCores: args.Constraints.CpuCores, CpuPower: args.Constraints.CpuPower, Tags: args.Constraints.Tags, } // Fill in some expected instance hardware characteristics if constraints not specified. if hc.Arch == nil { arch := "amd64" hc.Arch = &arch } if hc.Mem == nil { mem := uint64(1024) hc.Mem = &mem } if hc.RootDisk == nil { disk := uint64(8192) hc.RootDisk = &disk } if hc.CpuCores == nil { cores := uint64(1) hc.CpuCores = &cores } } // Simulate networks added when requested. networks := append(args.Constraints.IncludeNetworks(), args.MachineConfig.Networks...) networkInfo := make([]network.Info, len(networks)) for i, netName := range networks { if strings.HasPrefix(netName, "bad-") { // Simulate we didn't get correct information for the network. networkInfo[i] = network.Info{ ProviderId: network.Id(netName), NetworkName: netName, CIDR: "invalid", } } else { networkInfo[i] = network.Info{ ProviderId: network.Id(netName), NetworkName: netName, CIDR: fmt.Sprintf("0.%d.2.0/24", i+1), InterfaceName: fmt.Sprintf("eth%d", i), VLANTag: i, MACAddress: fmt.Sprintf("aa:bb:cc:dd:ee:f%d", i), } } } estate.insts[i.id] = i estate.maxId++ estate.ops <- OpStartInstance{ Env: e.name, MachineId: machineId, MachineNonce: args.MachineConfig.MachineNonce, Constraints: args.Constraints, Networks: args.MachineConfig.Networks, NetworkInfo: networkInfo, Instance: i, Info: args.MachineConfig.MongoInfo, APIInfo: args.MachineConfig.APIInfo, Secret: e.ecfg().secret(), } return i, hc, networkInfo, nil }
// StartInstance is specified in the InstanceBroker interface. func (e *environ) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { defer delay() machineId := args.InstanceConfig.MachineId logger.Infof("dummy startinstance, machine %s", machineId) if err := e.checkBroken("StartInstance"); err != nil { return nil, err } estate, err := e.state() if err != nil { return nil, err } estate.mu.Lock() defer estate.mu.Unlock() // check if an error has been injected on the transientErrorInjection channel (testing purposes) select { case injectedError := <-transientErrorInjection: return nil, injectedError default: } if args.InstanceConfig.MachineNonce == "" { return nil, errors.New("cannot start instance: missing machine nonce") } if _, ok := e.Config().CACert(); !ok { return nil, errors.New("no CA certificate in environment configuration") } if args.InstanceConfig.MongoInfo.Tag != names.NewMachineTag(machineId) { return nil, errors.New("entity tag must match started machine") } if args.InstanceConfig.APIInfo.Tag != names.NewMachineTag(machineId) { return nil, errors.New("entity tag must match started machine") } logger.Infof("would pick tools from %s", args.Tools) series := args.Tools.OneSeries() idString := fmt.Sprintf("%s-%d", e.name, estate.maxId) addrs := network.NewAddresses(idString+".dns", "127.0.0.1") if estate.preferIPv6 { addrs = append(addrs, network.NewAddress(fmt.Sprintf("fc00::%x", estate.maxId+1))) } logger.Debugf("StartInstance addresses: %v", addrs) i := &dummyInstance{ id: instance.Id(idString), addresses: addrs, ports: make(map[network.PortRange]bool), machineId: machineId, series: series, firewallMode: e.Config().FirewallMode(), state: estate, } var hc *instance.HardwareCharacteristics // To match current system capability, only provide hardware characteristics for // environ machines, not containers. if state.ParentId(machineId) == "" { // We will just assume the instance hardware characteristics exactly matches // the supplied constraints (if specified). hc = &instance.HardwareCharacteristics{ Arch: args.Constraints.Arch, Mem: args.Constraints.Mem, RootDisk: args.Constraints.RootDisk, CpuCores: args.Constraints.CpuCores, CpuPower: args.Constraints.CpuPower, Tags: args.Constraints.Tags, } // Fill in some expected instance hardware characteristics if constraints not specified. if hc.Arch == nil { arch := "amd64" hc.Arch = &arch } if hc.Mem == nil { mem := uint64(1024) hc.Mem = &mem } if hc.RootDisk == nil { disk := uint64(8192) hc.RootDisk = &disk } if hc.CpuCores == nil { cores := uint64(1) hc.CpuCores = &cores } } // Simulate networks added when requested. networks := append(args.Constraints.IncludeNetworks(), args.InstanceConfig.Networks...) networkInfo := make([]network.InterfaceInfo, len(networks)) for i, netName := range networks { if strings.HasPrefix(netName, "bad-") { // Simulate we didn't get correct information for the network. networkInfo[i] = network.InterfaceInfo{ ProviderId: network.Id(netName), NetworkName: netName, CIDR: "invalid", } } else if strings.HasPrefix(netName, "invalid-") { // Simulate we got invalid information for the network. networkInfo[i] = network.InterfaceInfo{ ProviderId: network.Id(netName), NetworkName: "$$" + netName, CIDR: fmt.Sprintf("0.%d.2.0/24", i+1), } } else { networkInfo[i] = network.InterfaceInfo{ ProviderId: network.Id(netName), NetworkName: netName, CIDR: fmt.Sprintf("0.%d.2.0/24", i+1), InterfaceName: fmt.Sprintf("eth%d", i), VLANTag: i, MACAddress: fmt.Sprintf("aa:bb:cc:dd:ee:f%d", i), } } // TODO(dimitern) Add the rest of the network.InterfaceInfo // fields when we can use them. } // Simulate creating volumes when requested. volumes := make([]storage.Volume, len(args.Volumes)) for i, v := range args.Volumes { persistent, _ := v.Attributes[storage.Persistent].(bool) volumes[i] = storage.Volume{ Tag: names.NewVolumeTag(strconv.Itoa(i + 1)), VolumeInfo: storage.VolumeInfo{ Size: v.Size, Persistent: persistent, }, } } estate.insts[i.id] = i estate.maxId++ estate.ops <- OpStartInstance{ Env: e.name, MachineId: machineId, MachineNonce: args.InstanceConfig.MachineNonce, PossibleTools: args.Tools, Constraints: args.Constraints, Networks: args.InstanceConfig.Networks, NetworkInfo: networkInfo, Volumes: volumes, Instance: i, Jobs: args.InstanceConfig.Jobs, Info: args.InstanceConfig.MongoInfo, APIInfo: args.InstanceConfig.APIInfo, AgentEnvironment: args.InstanceConfig.AgentEnvironment, Secret: e.ecfg().secret(), } return &environs.StartInstanceResult{ Instance: i, Hardware: hc, NetworkInfo: networkInfo, }, nil }
// StartInstance is specified in the InstanceBroker interface. func (e *environ) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { defer delay() machineId := args.InstanceConfig.MachineId logger.Infof("dummy startinstance, machine %s", machineId) if err := e.checkBroken("StartInstance"); err != nil { return nil, err } estate, err := e.state() if err != nil { return nil, err } estate.mu.Lock() defer estate.mu.Unlock() // check if an error has been injected on the transientErrorInjection channel (testing purposes) select { case injectedError := <-transientErrorInjection: return nil, injectedError default: } if args.InstanceConfig.MachineNonce == "" { return nil, errors.New("cannot start instance: missing machine nonce") } if _, ok := e.Config().CACert(); !ok { return nil, errors.New("no CA certificate in model configuration") } if args.InstanceConfig.MongoInfo.Tag != names.NewMachineTag(machineId) { return nil, errors.New("entity tag must match started machine") } if args.InstanceConfig.APIInfo.Tag != names.NewMachineTag(machineId) { return nil, errors.New("entity tag must match started machine") } logger.Infof("would pick tools from %s", args.Tools) series := args.Tools.OneSeries() idString := fmt.Sprintf("%s-%d", e.name, estate.maxId) addrs := network.NewAddresses(idString+".dns", "127.0.0.1") if estate.preferIPv6 { addrs = append(addrs, network.NewAddress(fmt.Sprintf("fc00::%x", estate.maxId+1))) } logger.Debugf("StartInstance addresses: %v", addrs) i := &dummyInstance{ id: instance.Id(idString), addresses: addrs, ports: make(map[network.PortRange]bool), machineId: machineId, series: series, firewallMode: e.Config().FirewallMode(), state: estate, } var hc *instance.HardwareCharacteristics // To match current system capability, only provide hardware characteristics for // environ machines, not containers. if state.ParentId(machineId) == "" { // We will just assume the instance hardware characteristics exactly matches // the supplied constraints (if specified). hc = &instance.HardwareCharacteristics{ Arch: args.Constraints.Arch, Mem: args.Constraints.Mem, RootDisk: args.Constraints.RootDisk, CpuCores: args.Constraints.CpuCores, CpuPower: args.Constraints.CpuPower, Tags: args.Constraints.Tags, } // Fill in some expected instance hardware characteristics if constraints not specified. if hc.Arch == nil { arch := "amd64" hc.Arch = &arch } if hc.Mem == nil { mem := uint64(1024) hc.Mem = &mem } if hc.RootDisk == nil { disk := uint64(8192) hc.RootDisk = &disk } if hc.CpuCores == nil { cores := uint64(1) hc.CpuCores = &cores } } // Simulate subnetsToZones gets populated when spaces given in constraints. spaces := args.Constraints.IncludeSpaces() var subnetsToZones map[network.Id][]string for isp := range spaces { // Simulate 2 subnets per space. if subnetsToZones == nil { subnetsToZones = make(map[network.Id][]string) } for isn := 0; isn < 2; isn++ { providerId := fmt.Sprintf("subnet-%d", isp+isn) zone := fmt.Sprintf("zone%d", isp+isn) subnetsToZones[network.Id(providerId)] = []string{zone} } } // Simulate creating volumes when requested. volumes := make([]storage.Volume, len(args.Volumes)) for iv, v := range args.Volumes { persistent, _ := v.Attributes["persistent"].(bool) volumes[iv] = storage.Volume{ Tag: names.NewVolumeTag(strconv.Itoa(iv + 1)), VolumeInfo: storage.VolumeInfo{ Size: v.Size, Persistent: persistent, }, } } estate.insts[i.id] = i estate.maxId++ estate.ops <- OpStartInstance{ Env: e.name, MachineId: machineId, MachineNonce: args.InstanceConfig.MachineNonce, PossibleTools: args.Tools, Constraints: args.Constraints, SubnetsToZones: subnetsToZones, Volumes: volumes, Instance: i, Jobs: args.InstanceConfig.Jobs, Info: args.InstanceConfig.MongoInfo, APIInfo: args.InstanceConfig.APIInfo, AgentEnvironment: args.InstanceConfig.AgentEnvironment, Secret: e.ecfg().secret(), } return &environs.StartInstanceResult{ Instance: i, Hardware: hc, }, nil }
// NewStorageProvisionerAPI creates a new server-side StorageProvisionerAPI facade. func NewStorageProvisionerAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*StorageProvisionerAPI, error) { if !authorizer.AuthMachineAgent() { return nil, common.ErrPerm } canAccessStorageMachine := func(tag names.MachineTag, allowEnvironManager bool) bool { authEntityTag := authorizer.GetAuthTag() if tag == authEntityTag { // Machine agents can access volumes // scoped to their own machine. return true } parentId := state.ParentId(tag.Id()) if parentId == "" { return allowEnvironManager && authorizer.AuthEnvironManager() } // All containers with the authenticated // machine as a parent are accessible by it. return names.NewMachineTag(parentId) == authEntityTag } getScopeAuthFunc := func() (common.AuthFunc, error) { return func(tag names.Tag) bool { switch tag := tag.(type) { case names.EnvironTag: // Environment managers can access all volumes // and filesystems scoped to the environment. isEnvironManager := authorizer.AuthEnvironManager() return isEnvironManager && tag == st.EnvironTag() case names.MachineTag: return canAccessStorageMachine(tag, false) default: return false } }, nil } canAccessStorageEntity := func(tag names.Tag, allowMachines bool) bool { switch tag := tag.(type) { case names.VolumeTag: machineTag, ok := names.VolumeMachine(tag) if ok { return canAccessStorageMachine(machineTag, false) } return authorizer.AuthEnvironManager() case names.FilesystemTag: machineTag, ok := names.FilesystemMachine(tag) if ok { return canAccessStorageMachine(machineTag, false) } return authorizer.AuthEnvironManager() case names.MachineTag: return allowMachines && canAccessStorageMachine(tag, true) default: return false } } getStorageEntityAuthFunc := func() (common.AuthFunc, error) { return func(tag names.Tag) bool { return canAccessStorageEntity(tag, false) }, nil } getLifeAuthFunc := func() (common.AuthFunc, error) { return func(tag names.Tag) bool { return canAccessStorageEntity(tag, true) }, nil } getAttachmentAuthFunc := func() (func(names.MachineTag, names.Tag) bool, error) { // getAttachmentAuthFunc returns a function that validates // access by the authenticated user to an attachment. return func(machineTag names.MachineTag, attachmentTag names.Tag) bool { // Machine agents can access their own machine, and // machines contained. Environment managers can access // top-level machines. if !canAccessStorageMachine(machineTag, true) { return false } // Environment managers can access environment-scoped // volumes and volumes scoped to their own machines. // Other machine agents can access volumes regardless // of their scope. if !authorizer.AuthEnvironManager() { return true } var machineScope names.MachineTag var hasMachineScope bool switch attachmentTag := attachmentTag.(type) { case names.VolumeTag: machineScope, hasMachineScope = names.VolumeMachine(attachmentTag) case names.FilesystemTag: machineScope, hasMachineScope = names.FilesystemMachine(attachmentTag) } return !hasMachineScope || machineScope == authorizer.GetAuthTag() }, nil } getMachineAuthFunc := func() (common.AuthFunc, error) { return func(tag names.Tag) bool { if tag, ok := tag.(names.MachineTag); ok { return canAccessStorageMachine(tag, true) } return false }, nil } getBlockDevicesAuthFunc := func() (common.AuthFunc, error) { return func(tag names.Tag) bool { if tag, ok := tag.(names.MachineTag); ok { return canAccessStorageMachine(tag, false) } return false }, nil } stateInterface := getState(st) settings := getSettingsManager(st) return &StorageProvisionerAPI{ LifeGetter: common.NewLifeGetter(stateInterface, getLifeAuthFunc), DeadEnsurer: common.NewDeadEnsurer(stateInterface, getStorageEntityAuthFunc), EnvironWatcher: common.NewEnvironWatcher(stateInterface, resources, authorizer), InstanceIdGetter: common.NewInstanceIdGetter(st, getMachineAuthFunc), StatusSetter: common.NewStatusSetter(st, getStorageEntityAuthFunc), st: stateInterface, settings: settings, resources: resources, authorizer: authorizer, getScopeAuthFunc: getScopeAuthFunc, getStorageEntityAuthFunc: getStorageEntityAuthFunc, getAttachmentAuthFunc: getAttachmentAuthFunc, getMachineAuthFunc: getMachineAuthFunc, getBlockDevicesAuthFunc: getBlockDevicesAuthFunc, }, nil }