2 ==================================================================================
3 Copyright (c) 2019 AT&T Intellectual Property.
4 Copyright (c) 2019 Nokia
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 ==================================================================================
35 "github.com/spf13/viper"
38 // For testing purpose go version 1.13 ->
44 type ReadyCB func(interface{})
45 type ShutdownCB func()
48 // XApp is an application instance
56 Subscription *Subscriber
60 readyCbParams interface{}
67 return Rmr != nil && Rmr.IsReady() && Sdl != nil && Sdl.IsReady()
70 func SetReadyCB(cb ReadyCB, params interface{}) {
72 readyCbParams = params
75 func XappReadyCb(params interface{}) {
76 Alarm = NewAlarmClient(viper.GetString("moId"), viper.GetString("name"))
78 readyCb(readyCbParams)
82 func SetShutdownCB(cb ShutdownCB) {
86 func XappShutdownCb() {
87 if err := doDeregister(); err != nil {
88 Logger.Info("xApp deregistration failed: %v, terminating ungracefully!", err)
90 Logger.Info("xApp deregistration successfull!")
93 if shutdownCb != nil {
100 time.Sleep(5 * time.Second)
101 if !IsHealthProbeReady() {
102 Logger.Info("Application='%s' is not ready yet, waiting ...", viper.GetString("name"))
106 Logger.Debug("Application='%s' is now up and ready, continue with registration ...", viper.GetString("name"))
107 if err := doRegister(); err == nil {
108 Logger.Info("Registration done, proceeding with startup ...")
114 func getService(host, service string) string {
115 appnamespace := os.Getenv("APP_NAMESPACE")
116 if appnamespace == "" {
117 appnamespace = DEFAULT_XAPP_NS
120 svc := fmt.Sprintf(service, strings.ToUpper(appnamespace), strings.ToUpper(host))
121 url := strings.Split(os.Getenv(strings.Replace(svc, "-", "_", -1)), "//")
123 Logger.Info("getService: %+v %+v", svc, url)
130 func getPltNamespace(envName, defVal string) string {
131 pltnamespace := os.Getenv("PLT_NAMESPACE")
132 if pltnamespace == "" {
133 pltnamespace = defVal
139 func doPost(pltNs, url string, msg []byte, status int) error {
140 resp, err := http.Post(fmt.Sprintf(url, pltNs, pltNs), "application/json", bytes.NewBuffer(msg))
141 if err != nil || resp == nil || resp.StatusCode != status {
142 Logger.Info("http.Post to '%s' failed with error: %v", fmt.Sprintf(url, pltNs, pltNs), err)
145 Logger.Info("Post to '%s' done, status:%v", fmt.Sprintf(url, pltNs, pltNs), resp.Status)
150 func doRegister() error {
151 host, _ := os.Hostname()
152 xappname := viper.GetString("name")
153 xappversion := viper.GetString("version")
154 pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
156 httpEp, rmrEp := getService(host, SERVICE_HTTP), getService(host, SERVICE_RMR)
157 if httpEp == "" || rmrEp == "" {
158 Logger.Warn("Couldn't resolve service endpoints: httpEp=%s rmrEp=%s", httpEp, rmrEp)
162 requestBody, err := json.Marshal(map[string]string{
164 "httpEndpoint": httpEp,
165 "rmrEndpoint": rmrEp,
166 "appInstanceName": xappname,
167 "appVersion": xappversion,
168 "configPath": CONFIG_PATH,
172 Logger.Error("json.Marshal failed with error: %v", err)
176 return doPost(pltNs, REGISTER_PATH, requestBody, http.StatusCreated)
179 func doDeregister() error {
180 if !IsHealthProbeReady() {
184 name, _ := os.Hostname()
185 xappname := viper.GetString("name")
186 pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
188 requestBody, err := json.Marshal(map[string]string{
190 "appInstanceName": xappname,
194 Logger.Error("json.Marshal failed with error: %v", err)
198 return doPost(pltNs, DEREGISTER_PATH, requestBody, http.StatusNoContent)
201 func InstallSignalHandler() {
203 // Signal handlers to really exit program.
204 // shutdownCb can hang until application has
205 // made all needed gracefull shutdown actions
206 // hardcoded limit for shutdown is 20 seconds
208 interrupt := make(chan os.Signal, 1)
209 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
210 //signal handler function
212 for range interrupt {
213 if atomic.CompareAndSwapInt32(&shutdownFlag, 0, 1) {
217 sentry := make(chan struct{})
226 case <-time.After(time.Duration(timeout) * time.Second):
227 Logger.Info("xapp-frame shutdown callback took more than %d seconds", timeout)
229 Logger.Info("xapp-frame shutdown callback handled within %d seconds", timeout)
234 newCnt := atomic.AddInt32(&shutdownCnt, 1)
235 Logger.Info("xapp-frame shutdown already ongoing. Forced exit counter %d/%d ", newCnt, 5)
237 Logger.Info("xapp-frame shutdown forced exit")
248 // Load xapp configuration
249 Logger = LoadConfig()
251 if viper.IsSet("controls.logger.level") {
252 Logger.SetLevel(viper.GetInt("controls.logger.level"))
254 Logger.SetLevel(viper.GetInt("logger.level"))
257 if !viper.IsSet("controls.logger.noFormat") || !viper.GetBool("controls.logger.noFormat") {
261 Resource = NewRouter()
262 Config = Configurator{}
263 Metric = NewMetrics(viper.GetString("metrics.url"), viper.GetString("metrics.namespace"), Resource.router)
264 Subscription = NewSubscriber(viper.GetString("controls.subscription.host"), viper.GetInt("controls.subscription.timeout"))
265 Sdl = NewSDLClient(viper.GetString("controls.db.namespace"))
266 Rnib = NewRNIBClient()
269 InstallSignalHandler()
272 func RunWithParams(c MessageConsumer, sdlcheck bool) {
275 Rmr.SetReadyCB(XappReadyCb, nil)
277 host := fmt.Sprintf(":%d", GetPortData("http").Port)
278 go http.ListenAndServe(host, Resource.router)
279 Logger.Info(fmt.Sprintf("Xapp started, listening on: %s", host))
289 func Run(c MessageConsumer) {
290 RunWithParams(c, true)