github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/facades/controller/caasfirewaller/firewaller.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasfirewaller
     5  
     6  import (
     7  	"sort"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names/v5"
    11  
    12  	"github.com/juju/juju/apiserver/common"
    13  	charmscommon "github.com/juju/juju/apiserver/common/charms"
    14  	apiservererrors "github.com/juju/juju/apiserver/errors"
    15  	"github.com/juju/juju/apiserver/facade"
    16  	"github.com/juju/juju/core/network"
    17  	"github.com/juju/juju/rpc/params"
    18  	"github.com/juju/juju/state/watcher"
    19  )
    20  
    21  type Facade struct {
    22  	*common.LifeGetter
    23  	*common.AgentEntityWatcher
    24  	resources       facade.Resources
    25  	state           CAASFirewallerState
    26  	charmInfoAPI    *charmscommon.CharmInfoAPI
    27  	appCharmInfoAPI *charmscommon.ApplicationCharmInfoAPI
    28  }
    29  
    30  func newFacadeLegacy(
    31  	resources facade.Resources,
    32  	authorizer facade.Authorizer,
    33  	st CAASFirewallerState,
    34  	charmInfoAPI *charmscommon.CharmInfoAPI,
    35  	appCharmInfoAPI *charmscommon.ApplicationCharmInfoAPI,
    36  ) (*Facade, error) {
    37  	if !authorizer.AuthController() {
    38  		return nil, apiservererrors.ErrPerm
    39  	}
    40  	accessApplication := common.AuthFuncForTagKind(names.ApplicationTagKind)
    41  	return &Facade{
    42  		LifeGetter: common.NewLifeGetter(
    43  			st, common.AuthAny(
    44  				common.AuthFuncForTagKind(names.ApplicationTagKind),
    45  				common.AuthFuncForTagKind(names.UnitTagKind),
    46  			),
    47  		),
    48  		AgentEntityWatcher: common.NewAgentEntityWatcher(
    49  			st,
    50  			resources,
    51  			accessApplication,
    52  		),
    53  		resources:       resources,
    54  		state:           st,
    55  		charmInfoAPI:    charmInfoAPI,
    56  		appCharmInfoAPI: appCharmInfoAPI,
    57  	}, nil
    58  }
    59  
    60  // CharmInfo returns information about the requested charm.
    61  func (f *Facade) CharmInfo(args params.CharmURL) (params.Charm, error) {
    62  	return f.charmInfoAPI.CharmInfo(args)
    63  }
    64  
    65  // ApplicationCharmInfo returns information about an application's charm.
    66  func (f *Facade) ApplicationCharmInfo(args params.Entity) (params.Charm, error) {
    67  	return f.appCharmInfoAPI.ApplicationCharmInfo(args)
    68  }
    69  
    70  // IsExposed returns whether the specified applications are exposed.
    71  func (f *Facade) IsExposed(args params.Entities) (params.BoolResults, error) {
    72  	results := params.BoolResults{
    73  		Results: make([]params.BoolResult, len(args.Entities)),
    74  	}
    75  	for i, arg := range args.Entities {
    76  		exposed, err := f.isExposed(f.state, arg.Tag)
    77  		if err != nil {
    78  			results.Results[i].Error = apiservererrors.ServerError(err)
    79  			continue
    80  		}
    81  		results.Results[i].Result = exposed
    82  	}
    83  	return results, nil
    84  }
    85  
    86  func (f *Facade) isExposed(backend CAASFirewallerState, tagString string) (bool, error) {
    87  	tag, err := names.ParseApplicationTag(tagString)
    88  	if err != nil {
    89  		return false, errors.Trace(err)
    90  	}
    91  	app, err := backend.Application(tag.Id())
    92  	if err != nil {
    93  		return false, errors.Trace(err)
    94  	}
    95  	return app.IsExposed(), nil
    96  }
    97  
    98  // ApplicationsConfig returns the config for the specified applications.
    99  func (f *Facade) ApplicationsConfig(args params.Entities) (params.ApplicationGetConfigResults, error) {
   100  	results := params.ApplicationGetConfigResults{
   101  		Results: make([]params.ConfigResult, len(args.Entities)),
   102  	}
   103  	for i, arg := range args.Entities {
   104  		result, err := f.getApplicationConfig(arg.Tag)
   105  		results.Results[i].Config = result
   106  		results.Results[i].Error = apiservererrors.ServerError(err)
   107  	}
   108  	return results, nil
   109  }
   110  
   111  func (f *Facade) getApplicationConfig(tagString string) (map[string]interface{}, error) {
   112  	tag, err := names.ParseApplicationTag(tagString)
   113  	if err != nil {
   114  		return nil, errors.Trace(err)
   115  	}
   116  	app, err := f.state.Application(tag.Id())
   117  	if err != nil {
   118  		return nil, errors.Trace(err)
   119  	}
   120  	return app.ApplicationConfig()
   121  }
   122  
   123  // WatchApplications starts a StringsWatcher to watch applications
   124  // deployed to this model.
   125  func (f *Facade) WatchApplications() (params.StringsWatchResult, error) {
   126  	watch := f.state.WatchApplications()
   127  	// Consume the initial event and forward it to the result.
   128  	if changes, ok := <-watch.Changes(); ok {
   129  		return params.StringsWatchResult{
   130  			StringsWatcherId: f.resources.Register(watch),
   131  			Changes:          changes,
   132  		}, nil
   133  	}
   134  	return params.StringsWatchResult{}, watcher.EnsureErr(watch)
   135  }
   136  
   137  // FacadeSidecar provides access to the CAASFirewaller API facade for sidecar applications.
   138  type FacadeSidecar struct {
   139  	*Facade
   140  
   141  	accessModel common.GetAuthFunc
   142  }
   143  
   144  func newFacadeSidecar(
   145  	resources facade.Resources,
   146  	authorizer facade.Authorizer,
   147  	st CAASFirewallerState,
   148  	commonCharmsAPI *charmscommon.CharmInfoAPI,
   149  	appCharmInfoAPI *charmscommon.ApplicationCharmInfoAPI,
   150  ) (*FacadeSidecar, error) {
   151  	if !authorizer.AuthController() {
   152  		return nil, apiservererrors.ErrPerm
   153  	}
   154  	accessApplication := common.AuthFuncForTagKind(names.ApplicationTagKind)
   155  
   156  	return &FacadeSidecar{
   157  		accessModel: common.AuthFuncForTagKind(names.ModelTagKind),
   158  		Facade: &Facade{
   159  			LifeGetter: common.NewLifeGetter(
   160  				st, common.AuthAny(
   161  					common.AuthFuncForTagKind(names.ApplicationTagKind),
   162  					common.AuthFuncForTagKind(names.UnitTagKind),
   163  				),
   164  			),
   165  			AgentEntityWatcher: common.NewAgentEntityWatcher(
   166  				st,
   167  				resources,
   168  				accessApplication,
   169  			),
   170  			resources:       resources,
   171  			state:           st,
   172  			charmInfoAPI:    commonCharmsAPI,
   173  			appCharmInfoAPI: appCharmInfoAPI,
   174  		},
   175  	}, nil
   176  }
   177  
   178  // WatchOpenedPorts returns a new StringsWatcher for each given
   179  // model tag.
   180  func (f *FacadeSidecar) WatchOpenedPorts(args params.Entities) (params.StringsWatchResults, error) {
   181  	result := params.StringsWatchResults{
   182  		Results: make([]params.StringsWatchResult, len(args.Entities)),
   183  	}
   184  	if len(args.Entities) == 0 {
   185  		return result, nil
   186  	}
   187  	canWatch, err := f.accessModel()
   188  	if err != nil {
   189  		return params.StringsWatchResults{}, errors.Trace(err)
   190  	}
   191  	for i, entity := range args.Entities {
   192  		tag, err := names.ParseTag(entity.Tag)
   193  		if err != nil {
   194  			result.Results[i].Error = apiservererrors.ServerError(apiservererrors.ErrPerm)
   195  			continue
   196  		}
   197  		if !canWatch(tag) {
   198  			result.Results[i].Error = apiservererrors.ServerError(apiservererrors.ErrPerm)
   199  			continue
   200  		}
   201  		watcherID, initial, err := f.watchOneModelOpenedPorts(tag)
   202  		if err != nil {
   203  			result.Results[i].Error = apiservererrors.ServerError(err)
   204  			continue
   205  		}
   206  		result.Results[i].StringsWatcherId = watcherID
   207  		result.Results[i].Changes = initial
   208  	}
   209  	return result, nil
   210  }
   211  
   212  func (f *FacadeSidecar) watchOneModelOpenedPorts(tag names.Tag) (string, []string, error) {
   213  	// NOTE: tag is ignored, as there is only one model in the
   214  	// state DB. Once this changes, change the code below accordingly.
   215  	watch := f.state.WatchOpenedPorts()
   216  	// Consume the initial event and forward it to the result.
   217  	if changes, ok := <-watch.Changes(); ok {
   218  		return f.resources.Register(watch), changes, nil
   219  	}
   220  	return "", nil, watcher.EnsureErr(watch)
   221  }
   222  
   223  // GetOpenedPorts returns all the opened ports for each given application tag.
   224  func (f *FacadeSidecar) GetOpenedPorts(arg params.Entity) (params.ApplicationOpenedPortsResults, error) {
   225  	result := params.ApplicationOpenedPortsResults{
   226  		Results: make([]params.ApplicationOpenedPortsResult, 1),
   227  	}
   228  
   229  	appTag, err := names.ParseApplicationTag(arg.Tag)
   230  	if err != nil {
   231  		result.Results[0].Error = apiservererrors.ServerError(err)
   232  		return result, nil
   233  	}
   234  
   235  	app, err := f.state.Application(appTag.Id())
   236  	if err != nil {
   237  		result.Results[0].Error = apiservererrors.ServerError(err)
   238  		return result, nil
   239  	}
   240  	openedPortRanges, err := app.OpenedPortRanges()
   241  	if err != nil {
   242  		result.Results[0].Error = apiservererrors.ServerError(err)
   243  		return result, nil
   244  	}
   245  	for endpointName, pgs := range openedPortRanges {
   246  		result.Results[0].ApplicationPortRanges = append(
   247  			result.Results[0].ApplicationPortRanges,
   248  			f.applicationOpenedPortsForEndpoint(endpointName, pgs),
   249  		)
   250  	}
   251  	sort.Slice(result.Results[0].ApplicationPortRanges, func(i, j int) bool {
   252  		// For test.
   253  		return result.Results[0].ApplicationPortRanges[i].Endpoint < result.Results[0].ApplicationPortRanges[j].Endpoint
   254  	})
   255  	return result, nil
   256  }
   257  
   258  func (f *FacadeSidecar) applicationOpenedPortsForEndpoint(endpointName string, pgs []network.PortRange) params.ApplicationOpenedPorts {
   259  	network.SortPortRanges(pgs)
   260  	o := params.ApplicationOpenedPorts{
   261  		Endpoint:   endpointName,
   262  		PortRanges: make([]params.PortRange, len(pgs)),
   263  	}
   264  	for i, pg := range pgs {
   265  		o.PortRanges[i] = params.FromNetworkPortRange(pg)
   266  	}
   267  	return o
   268  }