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 ==================================================================================
26 "github.com/spf13/viper"
37 // For testing purpose
43 type ReadyCB func(interface{})
44 type ShutdownCB func()
47 // XApp is an application instance
55 Subscription *Subscriber
59 readyCbParams interface{}
66 return Rmr != nil && Rmr.IsReady() && Sdl != nil && Sdl.IsReady()
69 func SetReadyCB(cb ReadyCB, params interface{}) {
71 readyCbParams = params
74 func XappReadyCb(params interface{}) {
75 Alarm = NewAlarmClient(viper.GetString("moId"), viper.GetString("name"))
77 readyCb(readyCbParams)
81 func SetShutdownCB(cb ShutdownCB) {
85 func XappShutdownCb() {
86 if err := doDeregister(); err != nil {
87 Logger.Info("xApp deregistration failed: %v, terminating ungracefully!", err)
89 Logger.Info("xApp deregistration successfull!")
92 if shutdownCb != nil {
99 time.Sleep(5 * time.Second)
100 if !IsHealthProbeReady() {
101 Logger.Info("Application='%s' is not ready yet, waiting ...", viper.GetString("name"))
105 Logger.Debug("Application='%s' is now up and ready, continue with registration ...", viper.GetString("name"))
106 if err := doRegister(); err == nil {
107 Logger.Info("Registration done, proceeding with startup ...")
113 func getService(host, service string) string {
114 appnamespace := os.Getenv("APP_NAMESPACE")
115 if appnamespace == "" {
116 appnamespace = DEFAULT_XAPP_NS
119 svc := fmt.Sprintf(service, strings.ToUpper(appnamespace), strings.ToUpper(host))
120 url := strings.Split(os.Getenv(strings.Replace(svc, "-", "_", -1)), "//")
127 func getPltNamespace(envName, defVal string) string {
128 pltnamespace := os.Getenv("PLT_NAMESPACE")
129 if pltnamespace == "" {
130 pltnamespace = defVal
136 func doPost(pltNs, url string, msg []byte, status int) error {
137 resp, err := http.Post(fmt.Sprintf(url, pltNs, pltNs), "application/json", bytes.NewBuffer(msg))
138 if err != nil || resp == nil || resp.StatusCode != status {
139 Logger.Info("http.Post to '%s' failed with error: %v", fmt.Sprintf(url, pltNs, pltNs), err)
142 Logger.Info("Post to '%s' done, status:%v", fmt.Sprintf(url, pltNs, pltNs), resp.Status)
147 func doRegister() error {
148 host, _ := os.Hostname()
149 xappname := viper.GetString("name")
150 xappversion := viper.GetString("version")
151 pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
153 httpEp, rmrEp := getService(host, SERVICE_HTTP), getService(host, SERVICE_RMR)
154 if httpEp == "" || rmrEp == "" {
155 Logger.Warn("Couldn't resolve service endpoints: httpEp=%s rmrEp=%s", httpEp, rmrEp)
159 requestBody, err := json.Marshal(map[string]string{
161 "httpEndpoint": httpEp,
162 "rmrEndpoint": rmrEp,
163 "appInstanceName": xappname,
164 "appVersion": xappversion,
165 "configPath": CONFIG_PATH,
169 Logger.Error("json.Marshal failed with error: %v", err)
173 return doPost(pltNs, REGISTER_PATH, requestBody, http.StatusCreated)
176 func doDeregister() error {
177 if !IsHealthProbeReady() {
181 name, _ := os.Hostname()
182 xappname := viper.GetString("name")
183 pltNs := getPltNamespace("PLT_NAMESPACE", DEFAULT_PLT_NS)
185 requestBody, err := json.Marshal(map[string]string{
187 "appInstanceName": xappname,
191 Logger.Error("json.Marshal failed with error: %v", err)
195 return doPost(pltNs, DEREGISTER_PATH, requestBody, http.StatusNoContent)
198 func InstallSignalHandler() {
200 // Signal handlers to really exit program.
201 // shutdownCb can hang until application has
202 // made all needed gracefull shutdown actions
203 // hardcoded limit for shutdown is 20 seconds
205 interrupt := make(chan os.Signal, 1)
206 signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
207 //signal handler function
209 for range interrupt {
210 if atomic.CompareAndSwapInt32(&shutdownFlag, 0, 1) {
214 sentry := make(chan struct{})
223 case <-time.After(time.Duration(timeout) * time.Second):
224 Logger.Info("xapp-frame shutdown callback took more than %d seconds", timeout)
226 Logger.Info("xapp-frame shutdown callback handled within %d seconds", timeout)
231 newCnt := atomic.AddInt32(&shutdownCnt, 1)
232 Logger.Info("xapp-frame shutdown already ongoing. Forced exit counter %d/%d ", newCnt, 5)
234 Logger.Info("xapp-frame shutdown forced exit")
245 // Load xapp configuration
246 Logger = LoadConfig()
248 if viper.IsSet("controls.logger.level") {
249 Logger.SetLevel(viper.GetInt("controls.logger.level"))
251 Logger.SetLevel(viper.GetInt("logger.level"))
254 if !viper.IsSet("controls.logger.noFormat") || !viper.GetBool("controls.logger.noFormat") {
258 Resource = NewRouter()
259 Config = Configurator{}
260 Metric = NewMetrics(viper.GetString("metrics.url"), viper.GetString("metrics.namespace"), Resource.router)
261 Subscription = NewSubscriber(viper.GetString("controls.subscription.host"), viper.GetInt("controls.subscription.timeout"))
262 Sdl = NewSDLClient(viper.GetString("controls.db.namespace"))
263 Rnib = NewRNIBClient()
266 InstallSignalHandler()
269 func RunWithParams(c MessageConsumer, sdlcheck bool) {
272 Rmr.SetReadyCB(XappReadyCb, nil)
274 host := fmt.Sprintf(":%d", GetPortData("http").Port)
275 go http.ListenAndServe(host, Resource.router)
276 Logger.Info(fmt.Sprintf("Xapp started, listening on: %s", host))
286 func Run(c MessageConsumer) {
287 RunWithParams(c, true)