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 ==================================================================================
25 "github.com/gorilla/mux"
26 "github.com/spf13/viper"
34 func (m *XappManager) Initialize(h Helmer, cm ConfigMapper) {
39 m.router = mux.NewRouter().StrictSlash(true)
41 resources := []Resource{
42 {"GET", "/ric/v1/health/alive", m.getHealthStatus},
43 {"GET", "/ric/v1/health/ready", m.getHealthStatus},
45 {"GET", "/ric/v1/xapps", m.getAllXapps},
46 {"GET", "/ric/v1/xapps/search", m.searchAllXapps},
47 {"GET", "/ric/v1/xapps/{name}", m.getXappByName},
48 {"GET", "/ric/v1/xapps/{name}/instances/{id}", m.getXappInstanceByName},
49 {"POST", "/ric/v1/xapps", m.deployXapp},
50 {"DELETE", "/ric/v1/xapps/{name}", m.undeployXapp},
52 {"GET", "/ric/v1/subscriptions", m.getSubscriptions},
53 {"POST", "/ric/v1/subscriptions", m.addSubscription},
54 {"GET", "/ric/v1/subscriptions/{id}", m.getSubscription},
55 {"DELETE", "/ric/v1/subscriptions/{id}", m.deleteSubscription},
56 {"PUT", "/ric/v1/subscriptions/{id}", m.updateSubscription},
58 {"GET", "/ric/v1/config", m.getConfig},
59 {"POST", "/ric/v1/config", m.createConfig},
60 {"PUT", "/ric/v1/config", m.updateConfig},
61 {"DELETE", "/ric/v1/config", m.deleteConfig},
62 {"DELETE", "/ric/v1/config/{name}", m.deleteSingleConfig},
65 for _, resource := range resources {
66 handler := LogRestRequests(resource.HandlerFunc)
67 //handler = m.serviceChecker(handler)
68 m.router.Methods(resource.Method).Path(resource.Url).Handler(handler)
74 func (m *XappManager) finalize(h Helmer) {
75 m.sd = SubscriptionDispatcher{}
84 func (m *XappManager) serviceChecker(inner http.Handler) http.Handler {
85 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
86 if r.URL.RequestURI() == "/ric/v1/health/alive" || m.ready == true {
89 respondWithJSON(w, http.StatusServiceUnavailable, nil)
95 func (m *XappManager) getHealthStatus(w http.ResponseWriter, r *http.Request) {
96 respondWithJSON(w, http.StatusOK, nil)
99 func (m *XappManager) Run() {
100 host := viper.GetString("local.host")
104 log.Printf("Xapp manager started ... serving on %s\n", host)
106 log.Fatal(http.ListenAndServe(host, m.router))
109 func (m *XappManager) getXappByName(w http.ResponseWriter, r *http.Request) {
110 xappName, ok := getResourceId(r, w, "name")
115 if xapp, err := m.helm.Status(xappName); err == nil {
116 respondWithJSON(w, http.StatusOK, xapp)
118 respondWithError(w, http.StatusNotFound, err.Error())
122 func (m *XappManager) getXappInstanceByName(w http.ResponseWriter, r *http.Request) {
123 xappName, ok := getResourceId(r, w, "name")
128 xapp, err := m.helm.Status(xappName)
130 respondWithError(w, http.StatusNotFound, err.Error())
134 xappInstanceName, ok := getResourceId(r, w, "id")
139 for _, v := range xapp.Instances {
140 if v.Name == xappInstanceName {
141 respondWithJSON(w, http.StatusOK, v)
145 Logger.Error("Xapp instance not found - url=%s", r.URL.RequestURI())
147 respondWithError(w, http.StatusNotFound, "Xapp instance not found")
150 func (m *XappManager) getAllXapps(w http.ResponseWriter, r *http.Request) {
151 xapps, err := m.helm.StatusAll()
153 respondWithError(w, http.StatusInternalServerError, err.Error())
157 respondWithJSON(w, http.StatusOK, xapps)
160 func (m *XappManager) searchAllXapps(w http.ResponseWriter, r *http.Request) {
161 respondWithJSON(w, http.StatusOK, m.helm.SearchAll())
164 func (m *XappManager) deployXapp(w http.ResponseWriter, r *http.Request) {
166 Logger.Error("No xapp data found in request body - url=%s", r.URL.RequestURI())
167 respondWithError(w, http.StatusMethodNotAllowed, "No xapp data!")
172 if err := json.NewDecoder(r.Body).Decode(&cm); err != nil {
173 Logger.Error("Invalid xapp data in request body - url=%s", r.URL.RequestURI())
174 respondWithError(w, http.StatusMethodNotAllowed, "Invalid xapp data!")
179 xapp, err := m.helm.Install(cm)
181 respondWithError(w, http.StatusInternalServerError, err.Error())
185 for i := 0; i < 3; i++ {
186 if xapp, err = m.helm.Status(xapp.Name); xapp.Instances != nil {
189 time.Sleep(time.Duration(5) * time.Second)
192 respondWithJSON(w, http.StatusCreated, xapp)
194 m.sd.Publish(xapp, EventType("created"))
197 func (m *XappManager) undeployXapp(w http.ResponseWriter, r *http.Request) {
198 xappName, ok := getResourceId(r, w, "name")
203 xapp, err := m.helm.Delete(xappName)
205 respondWithError(w, http.StatusInternalServerError, err.Error())
209 respondWithJSON(w, http.StatusNoContent, nil)
211 m.sd.Publish(xapp, EventType("deleted"))
214 // API: resthook handlers
215 func (m *XappManager) getSubscriptions(w http.ResponseWriter, r *http.Request) {
216 respondWithJSON(w, http.StatusOK, m.sd.GetAll())
219 func (m *XappManager) getSubscription(w http.ResponseWriter, r *http.Request) {
220 if id, ok := getResourceId(r, w, "id"); ok == true {
221 if s, ok := m.sd.Get(id); ok {
222 respondWithJSON(w, http.StatusOK, s)
224 Logger.Error("Subscription not found - url=%s", r.URL.RequestURI())
225 respondWithError(w, http.StatusNotFound, "Subscription not found")
230 func (m *XappManager) deleteSubscription(w http.ResponseWriter, r *http.Request) {
231 if id, ok := getResourceId(r, w, "id"); ok == true {
232 if _, ok := m.sd.Delete(id); ok {
233 respondWithJSON(w, http.StatusNoContent, nil)
235 Logger.Error("Subscription not found - url=%s", r.URL.RequestURI())
236 respondWithError(w, http.StatusNotFound, "Subscription not found")
241 func (m *XappManager) addSubscription(w http.ResponseWriter, r *http.Request) {
242 var req SubscriptionReq
243 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
244 Logger.Error("Invalid request payload - url=%s", r.URL.RequestURI())
245 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
250 respondWithJSON(w, http.StatusCreated, m.sd.Add(req))
253 func (m *XappManager) updateSubscription(w http.ResponseWriter, r *http.Request) {
254 if id, ok := getResourceId(r, w, "id"); ok == true {
255 var req SubscriptionReq
256 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
257 Logger.Error("Invalid request payload - url=%s", r.URL.RequestURI())
258 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
263 if s, ok := m.sd.Update(id, req); ok {
264 respondWithJSON(w, http.StatusOK, s)
266 Logger.Error("Subscription not found - url=%s", r.URL.RequestURI())
267 respondWithError(w, http.StatusNotFound, "Subscription not found")
272 func (m *XappManager) notifyClients() {
273 xapps, err := m.helm.StatusAll()
275 Logger.Info("Couldn't fetch xapps status information: %v", err.Error())
279 m.sd.notifyClients(xapps, "updated")
282 func (m *XappManager) getConfig(w http.ResponseWriter, r *http.Request) {
283 cfg := m.cm.UploadConfig()
284 respondWithJSON(w, http.StatusOK, cfg)
287 func (m *XappManager) createConfig(w http.ResponseWriter, r *http.Request) {
289 if parseConfig(w, r, &c) != nil {
293 if errList, err := m.cm.CreateConfigMap(c); err != nil {
294 if err.Error() != "Validation failed!" {
295 respondWithError(w, http.StatusInternalServerError, err.Error())
297 respondWithJSON(w, http.StatusUnprocessableEntity, errList)
301 respondWithJSON(w, http.StatusCreated, c.Metadata)
304 func (m *XappManager) updateConfig(w http.ResponseWriter, r *http.Request) {
306 if parseConfig(w, r, &c) != nil {
310 if errList, err := m.cm.UpdateConfigMap(c); err != nil {
311 if err.Error() != "Validation failed!" {
312 respondWithError(w, http.StatusInternalServerError, err.Error())
314 respondWithJSON(w, http.StatusInternalServerError, errList)
318 respondWithJSON(w, http.StatusOK, c.Metadata)
321 func (m *XappManager) deleteSingleConfig(w http.ResponseWriter, r *http.Request) {
322 xappName, ok := getResourceId(r, w, "name")
327 md := ConfigMetadata{Name: xappName, Namespace: m.cm.GetNamespace(""), ConfigName: xappName + "-appconfig"}
328 m.delConfig(w, XAppConfig{Metadata: md})
331 func (m *XappManager) deleteConfig(w http.ResponseWriter, r *http.Request) {
333 if parseConfig(w, r, &c) != nil {
340 func (m *XappManager) delConfig(w http.ResponseWriter, c XAppConfig) {
341 if _, err := m.cm.DeleteConfigMap(c); err != nil {
342 respondWithError(w, http.StatusInternalServerError, err.Error())
345 respondWithJSON(w, http.StatusNoContent, nil)
349 func respondWithError(w http.ResponseWriter, code int, message string) {
350 respondWithJSON(w, code, map[string]string{"error": message})
353 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
354 w.Header().Set("Content-Type", "application/json")
357 response, _ := json.Marshal(payload)
362 func getResourceId(r *http.Request, w http.ResponseWriter, pattern string) (id string, ok bool) {
363 if id, ok = mux.Vars(r)[pattern]; ok != true {
364 Logger.Error("Couldn't resolve name/id from the request URL")
365 respondWithError(w, http.StatusMethodNotAllowed, "Couldn't resolve name/id from the request URL")
371 func parseConfig(w http.ResponseWriter, r *http.Request, req *XAppConfig) error {
372 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
373 Logger.Error("Invalid request payload - url=%s", r.URL.RequestURI())
374 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
375 return errors.New("Invalid payload")