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 }