github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/firewaller/firewaller.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package firewaller
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names"
     9  
    10  	"github.com/juju/juju/apiserver/common"
    11  	"github.com/juju/juju/apiserver/params"
    12  	"github.com/juju/juju/network"
    13  	"github.com/juju/juju/state"
    14  	"github.com/juju/juju/state/watcher"
    15  )
    16  
    17  func init() {
    18  	// Version 0 is no longer supported.
    19  	common.RegisterStandardFacade("Firewaller", 2, NewFirewallerAPI)
    20  }
    21  
    22  // FirewallerAPI provides access to the Firewaller API facade.
    23  type FirewallerAPI struct {
    24  	*common.LifeGetter
    25  	*common.ModelWatcher
    26  	*common.AgentEntityWatcher
    27  	*common.UnitsWatcher
    28  	*common.ModelMachinesWatcher
    29  	*common.InstanceIdGetter
    30  
    31  	st            *state.State
    32  	resources     *common.Resources
    33  	authorizer    common.Authorizer
    34  	accessUnit    common.GetAuthFunc
    35  	accessService common.GetAuthFunc
    36  	accessMachine common.GetAuthFunc
    37  	accessEnviron common.GetAuthFunc
    38  }
    39  
    40  // NewFirewallerAPI creates a new server-side FirewallerAPI facade.
    41  func NewFirewallerAPI(
    42  	st *state.State,
    43  	resources *common.Resources,
    44  	authorizer common.Authorizer,
    45  ) (*FirewallerAPI, error) {
    46  	if !authorizer.AuthModelManager() {
    47  		// Firewaller must run as environment manager.
    48  		return nil, common.ErrPerm
    49  	}
    50  	// Set up the various authorization checkers.
    51  	accessEnviron := common.AuthFuncForTagKind(names.ModelTagKind)
    52  	accessUnit := common.AuthFuncForTagKind(names.UnitTagKind)
    53  	accessService := common.AuthFuncForTagKind(names.ServiceTagKind)
    54  	accessMachine := common.AuthFuncForTagKind(names.MachineTagKind)
    55  	accessUnitOrService := common.AuthEither(accessUnit, accessService)
    56  	accessUnitServiceOrMachine := common.AuthEither(accessUnitOrService, accessMachine)
    57  
    58  	// Life() is supported for units, services or machines.
    59  	lifeGetter := common.NewLifeGetter(
    60  		st,
    61  		accessUnitServiceOrMachine,
    62  	)
    63  	// ModelConfig() and WatchForModelConfigChanges() are allowed
    64  	// with unrestriced access.
    65  	modelWatcher := common.NewModelWatcher(
    66  		st,
    67  		resources,
    68  		authorizer,
    69  	)
    70  	// Watch() is supported for services only.
    71  	entityWatcher := common.NewAgentEntityWatcher(
    72  		st,
    73  		resources,
    74  		accessService,
    75  	)
    76  	// WatchUnits() is supported for machines.
    77  	unitsWatcher := common.NewUnitsWatcher(st,
    78  		resources,
    79  		accessMachine,
    80  	)
    81  	// WatchModelMachines() is allowed with unrestricted access.
    82  	machinesWatcher := common.NewModelMachinesWatcher(
    83  		st,
    84  		resources,
    85  		authorizer,
    86  	)
    87  	// InstanceId() is supported for machines.
    88  	instanceIdGetter := common.NewInstanceIdGetter(
    89  		st,
    90  		accessMachine,
    91  	)
    92  
    93  	return &FirewallerAPI{
    94  		LifeGetter:           lifeGetter,
    95  		ModelWatcher:         modelWatcher,
    96  		AgentEntityWatcher:   entityWatcher,
    97  		UnitsWatcher:         unitsWatcher,
    98  		ModelMachinesWatcher: machinesWatcher,
    99  		InstanceIdGetter:     instanceIdGetter,
   100  		st:                   st,
   101  		resources:            resources,
   102  		authorizer:           authorizer,
   103  		accessUnit:           accessUnit,
   104  		accessService:        accessService,
   105  		accessMachine:        accessMachine,
   106  		accessEnviron:        accessEnviron,
   107  	}, nil
   108  }
   109  
   110  // WatchOpenedPorts returns a new StringsWatcher for each given
   111  // environment tag.
   112  func (f *FirewallerAPI) WatchOpenedPorts(args params.Entities) (params.StringsWatchResults, error) {
   113  	result := params.StringsWatchResults{
   114  		Results: make([]params.StringsWatchResult, len(args.Entities)),
   115  	}
   116  	if len(args.Entities) == 0 {
   117  		return result, nil
   118  	}
   119  	canWatch, err := f.accessEnviron()
   120  	if err != nil {
   121  		return params.StringsWatchResults{}, errors.Trace(err)
   122  	}
   123  	for i, entity := range args.Entities {
   124  		tag, err := names.ParseTag(entity.Tag)
   125  		if err != nil {
   126  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   127  			continue
   128  		}
   129  		if !canWatch(tag) {
   130  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   131  			continue
   132  		}
   133  		watcherId, initial, err := f.watchOneEnvironOpenedPorts(tag)
   134  		if err != nil {
   135  			result.Results[i].Error = common.ServerError(err)
   136  			continue
   137  		}
   138  		result.Results[i].StringsWatcherId = watcherId
   139  		result.Results[i].Changes = initial
   140  	}
   141  	return result, nil
   142  }
   143  
   144  func (f *FirewallerAPI) watchOneEnvironOpenedPorts(tag names.Tag) (string, []string, error) {
   145  	// NOTE: tag is ignored, as there is only one environment in the
   146  	// state DB. Once this changes, change the code below accordingly.
   147  	watch := f.st.WatchOpenedPorts()
   148  	// Consume the initial event and forward it to the result.
   149  	if changes, ok := <-watch.Changes(); ok {
   150  		return f.resources.Register(watch), changes, nil
   151  	}
   152  	return "", nil, watcher.EnsureErr(watch)
   153  }
   154  
   155  // GetMachinePorts returns the port ranges opened on a machine for the specified
   156  // subnet as a map mapping port ranges to the tags of the units that opened
   157  // them.
   158  func (f *FirewallerAPI) GetMachinePorts(args params.MachinePortsParams) (params.MachinePortsResults, error) {
   159  	result := params.MachinePortsResults{
   160  		Results: make([]params.MachinePortsResult, len(args.Params)),
   161  	}
   162  	canAccess, err := f.accessMachine()
   163  	if err != nil {
   164  		return params.MachinePortsResults{}, err
   165  	}
   166  	for i, param := range args.Params {
   167  		machineTag, err := names.ParseMachineTag(param.MachineTag)
   168  		if err != nil {
   169  			result.Results[i].Error = common.ServerError(err)
   170  			continue
   171  		}
   172  		var subnetTag names.SubnetTag
   173  		if param.SubnetTag != "" {
   174  			subnetTag, err = names.ParseSubnetTag(param.SubnetTag)
   175  			if err != nil {
   176  				result.Results[i].Error = common.ServerError(err)
   177  				continue
   178  			}
   179  		}
   180  		machine, err := f.getMachine(canAccess, machineTag)
   181  		if err != nil {
   182  			result.Results[i].Error = common.ServerError(err)
   183  			continue
   184  		}
   185  		ports, err := machine.OpenedPorts(subnetTag.Id())
   186  		if err != nil {
   187  			result.Results[i].Error = common.ServerError(err)
   188  			continue
   189  		}
   190  		if ports != nil {
   191  			portRangeMap := ports.AllPortRanges()
   192  			var portRanges []network.PortRange
   193  			for portRange := range portRangeMap {
   194  				portRanges = append(portRanges, portRange)
   195  			}
   196  			network.SortPortRanges(portRanges)
   197  
   198  			for _, portRange := range portRanges {
   199  				unitTag := names.NewUnitTag(portRangeMap[portRange]).String()
   200  				result.Results[i].Ports = append(result.Results[i].Ports,
   201  					params.MachinePortRange{
   202  						UnitTag:   unitTag,
   203  						PortRange: params.FromNetworkPortRange(portRange),
   204  					})
   205  			}
   206  		}
   207  	}
   208  	return result, nil
   209  }
   210  
   211  // GetMachineActiveSubnets returns the tags of the all subnets that each machine
   212  // (in args) has open ports on.
   213  func (f *FirewallerAPI) GetMachineActiveSubnets(args params.Entities) (params.StringsResults, error) {
   214  	result := params.StringsResults{
   215  		Results: make([]params.StringsResult, len(args.Entities)),
   216  	}
   217  	canAccess, err := f.accessMachine()
   218  	if err != nil {
   219  		return params.StringsResults{}, err
   220  	}
   221  	for i, entity := range args.Entities {
   222  		machineTag, err := names.ParseMachineTag(entity.Tag)
   223  		if err != nil {
   224  			result.Results[i].Error = common.ServerError(err)
   225  			continue
   226  		}
   227  		machine, err := f.getMachine(canAccess, machineTag)
   228  		if err != nil {
   229  			result.Results[i].Error = common.ServerError(err)
   230  			continue
   231  		}
   232  		ports, err := machine.AllPorts()
   233  		if err != nil {
   234  			result.Results[i].Error = common.ServerError(err)
   235  			continue
   236  		}
   237  		for _, port := range ports {
   238  			subnetID := port.SubnetID()
   239  			if subnetID != "" && !names.IsValidSubnet(subnetID) {
   240  				// The error message below will look like e.g. `ports for
   241  				// machine "0", subnet "bad" not valid`.
   242  				err = errors.NotValidf("%s", ports)
   243  				result.Results[i].Error = common.ServerError(err)
   244  				continue
   245  			} else if subnetID != "" && names.IsValidSubnet(subnetID) {
   246  				subnetTag := names.NewSubnetTag(subnetID).String()
   247  				result.Results[i].Result = append(result.Results[i].Result, subnetTag)
   248  				continue
   249  			}
   250  			// TODO(dimitern): Empty subnet CIDRs for ports are still OK until
   251  			// we can enforce it across all providers.
   252  			result.Results[i].Result = append(result.Results[i].Result, "")
   253  		}
   254  	}
   255  	return result, nil
   256  }
   257  
   258  // GetExposed returns the exposed flag value for each given service.
   259  func (f *FirewallerAPI) GetExposed(args params.Entities) (params.BoolResults, error) {
   260  	result := params.BoolResults{
   261  		Results: make([]params.BoolResult, len(args.Entities)),
   262  	}
   263  	canAccess, err := f.accessService()
   264  	if err != nil {
   265  		return params.BoolResults{}, err
   266  	}
   267  	for i, entity := range args.Entities {
   268  		tag, err := names.ParseServiceTag(entity.Tag)
   269  		if err != nil {
   270  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   271  			continue
   272  		}
   273  		service, err := f.getService(canAccess, tag)
   274  		if err == nil {
   275  			result.Results[i].Result = service.IsExposed()
   276  		}
   277  		result.Results[i].Error = common.ServerError(err)
   278  	}
   279  	return result, nil
   280  }
   281  
   282  // GetAssignedMachine returns the assigned machine tag (if any) for
   283  // each given unit.
   284  func (f *FirewallerAPI) GetAssignedMachine(args params.Entities) (params.StringResults, error) {
   285  	result := params.StringResults{
   286  		Results: make([]params.StringResult, len(args.Entities)),
   287  	}
   288  	canAccess, err := f.accessUnit()
   289  	if err != nil {
   290  		return params.StringResults{}, err
   291  	}
   292  	for i, entity := range args.Entities {
   293  		tag, err := names.ParseUnitTag(entity.Tag)
   294  		if err != nil {
   295  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   296  			continue
   297  		}
   298  		unit, err := f.getUnit(canAccess, tag)
   299  		if err == nil {
   300  			var machineId string
   301  			machineId, err = unit.AssignedMachineId()
   302  			if err == nil {
   303  				result.Results[i].Result = names.NewMachineTag(machineId).String()
   304  			}
   305  		}
   306  		result.Results[i].Error = common.ServerError(err)
   307  	}
   308  	return result, nil
   309  }
   310  
   311  func (f *FirewallerAPI) getEntity(canAccess common.AuthFunc, tag names.Tag) (state.Entity, error) {
   312  	if !canAccess(tag) {
   313  		return nil, common.ErrPerm
   314  	}
   315  	return f.st.FindEntity(tag)
   316  }
   317  
   318  func (f *FirewallerAPI) getUnit(canAccess common.AuthFunc, tag names.UnitTag) (*state.Unit, error) {
   319  	entity, err := f.getEntity(canAccess, tag)
   320  	if err != nil {
   321  		return nil, err
   322  	}
   323  	// The authorization function guarantees that the tag represents a
   324  	// unit.
   325  	return entity.(*state.Unit), nil
   326  }
   327  
   328  func (f *FirewallerAPI) getService(canAccess common.AuthFunc, tag names.ServiceTag) (*state.Service, error) {
   329  	entity, err := f.getEntity(canAccess, tag)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  	// The authorization function guarantees that the tag represents a
   334  	// service.
   335  	return entity.(*state.Service), nil
   336  }
   337  
   338  func (f *FirewallerAPI) getMachine(canAccess common.AuthFunc, tag names.MachineTag) (*state.Machine, error) {
   339  	entity, err := f.getEntity(canAccess, tag)
   340  	if err != nil {
   341  		return nil, err
   342  	}
   343  	// The authorization function guarantees that the tag represents a
   344  	// machine.
   345  	return entity.(*state.Machine), nil
   346  }