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"
33 func (m *XappManager) Initialize(h Helmer, cm ConfigMapper) {
38 m.router = mux.NewRouter().StrictSlash(true)
40 resources := []Resource{
41 {"GET", "/ric/v1/health/alive", m.getHealthStatus},
42 {"GET", "/ric/v1/health/ready", m.getHealthStatus},
44 {"GET", "/ric/v1/xapps", m.getAllXapps},
45 {"GET", "/ric/v1/xapps/search", m.searchAllXapps},
46 {"GET", "/ric/v1/xapps/{name}", m.getXappByName},
47 {"GET", "/ric/v1/xapps/{name}/instances/{id}", m.getXappInstanceByName},
48 {"POST", "/ric/v1/xapps", m.deployXapp},
49 {"DELETE", "/ric/v1/xapps/{name}", m.undeployXapp},
51 {"GET", "/ric/v1/subscriptions", m.getSubscriptions},
52 {"POST", "/ric/v1/subscriptions", m.addSubscription},
53 {"GET", "/ric/v1/subscriptions/{id}", m.getSubscription},
54 {"DELETE", "/ric/v1/subscriptions/{id}", m.deleteSubscription},
55 {"PUT", "/ric/v1/subscriptions/{id}", m.updateSubscription},
57 {"GET", "/ric/v1/config", m.getConfig},
58 {"POST", "/ric/v1/config", m.createConfig},
59 {"PUT", "/ric/v1/config", m.updateConfig},
60 {"DELETE", "/ric/v1/config", m.deleteConfig},
61 {"DELETE", "/ric/v1/config/{name}", m.deleteSingleConfig},
64 for _, resource := range resources {
65 handler := LogRestRequests(resource.HandlerFunc)
66 //handler = m.serviceChecker(handler)
67 m.router.Methods(resource.Method).Path(resource.Url).Handler(handler)
73 func (m *XappManager) finalize(h Helmer) {
74 m.sd = SubscriptionDispatcher{}
83 func (m *XappManager) serviceChecker(inner http.Handler) http.Handler {
84 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
85 if r.URL.RequestURI() == "/ric/v1/health/alive" || m.ready == true {
88 respondWithJSON(w, http.StatusServiceUnavailable, nil)
94 func (m *XappManager) getHealthStatus(w http.ResponseWriter, r *http.Request) {
95 respondWithJSON(w, http.StatusOK, nil)
98 func (m *XappManager) Run() {
99 host := viper.GetString("local.host")
103 log.Printf("Xapp manager started ... serving on %s\n", host)
105 log.Fatal(http.ListenAndServe(host, m.router))
108 func (m *XappManager) getXappByName(w http.ResponseWriter, r *http.Request) {
109 xappName, ok := getResourceId(r, w, "name")
114 if xapp, err := m.helm.Status(xappName); err == nil {
115 respondWithJSON(w, http.StatusOK, xapp)
117 respondWithError(w, http.StatusNotFound, err.Error())
121 func (m *XappManager) getXappInstanceByName(w http.ResponseWriter, r *http.Request) {
122 xappName, ok := getResourceId(r, w, "name")
127 xapp, err := m.helm.Status(xappName)
129 respondWithError(w, http.StatusNotFound, err.Error())
133 xappInstanceName, ok := getResourceId(r, w, "id")
138 for _, v := range xapp.Instances {
139 if v.Name == xappInstanceName {
140 respondWithJSON(w, http.StatusOK, v)
144 Logger.Error("Xapp instance not found - url=%s", r.URL.RequestURI())
146 respondWithError(w, http.StatusNotFound, "Xapp instance not found")
149 func (m *XappManager) getAllXapps(w http.ResponseWriter, r *http.Request) {
150 xapps, err := m.helm.StatusAll()
152 respondWithError(w, http.StatusInternalServerError, err.Error())
156 respondWithJSON(w, http.StatusOK, xapps)
159 func (m *XappManager) searchAllXapps(w http.ResponseWriter, r *http.Request) {
160 respondWithJSON(w, http.StatusOK, m.helm.SearchAll())
163 func (m *XappManager) deployXapp(w http.ResponseWriter, r *http.Request) {
165 Logger.Error("No xapp data found in request body - url=%s", r.URL.RequestURI())
166 respondWithError(w, http.StatusMethodNotAllowed, "No xapp data!")
171 if err := json.NewDecoder(r.Body).Decode(&cm); err != nil {
172 Logger.Error("Invalid xapp data in request body - url=%s", r.URL.RequestURI())
173 respondWithError(w, http.StatusMethodNotAllowed, "Invalid xapp data!")
178 xapp, err := m.helm.Install(cm)
180 respondWithError(w, http.StatusInternalServerError, err.Error())
184 respondWithJSON(w, http.StatusCreated, xapp)
186 m.sd.Publish(xapp, EventType("created"))
189 func (m *XappManager) undeployXapp(w http.ResponseWriter, r *http.Request) {
190 xappName, ok := getResourceId(r, w, "name")
195 xapp, err := m.helm.Delete(xappName)
197 respondWithError(w, http.StatusInternalServerError, err.Error())
201 respondWithJSON(w, http.StatusNoContent, nil)
203 m.sd.Publish(xapp, EventType("deleted"))
206 // API: resthook handlers
207 func (m *XappManager) getSubscriptions(w http.ResponseWriter, r *http.Request) {
208 respondWithJSON(w, http.StatusOK, m.sd.GetAll())
211 func (m *XappManager) getSubscription(w http.ResponseWriter, r *http.Request) {
212 if id, ok := getResourceId(r, w, "id"); ok == true {
213 if s, ok := m.sd.Get(id); ok {
214 respondWithJSON(w, http.StatusOK, s)
216 Logger.Error("Subscription not found - url=%s", r.URL.RequestURI())
217 respondWithError(w, http.StatusNotFound, "Subscription not found")
222 func (m *XappManager) deleteSubscription(w http.ResponseWriter, r *http.Request) {
223 if id, ok := getResourceId(r, w, "id"); ok == true {
224 if _, ok := m.sd.Delete(id); ok {
225 respondWithJSON(w, http.StatusNoContent, nil)
227 Logger.Error("Subscription not found - url=%s", r.URL.RequestURI())
228 respondWithError(w, http.StatusNotFound, "Subscription not found")
233 func (m *XappManager) addSubscription(w http.ResponseWriter, r *http.Request) {
234 var req SubscriptionReq
235 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
236 Logger.Error("Invalid request payload - url=%s", r.URL.RequestURI())
237 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
242 respondWithJSON(w, http.StatusCreated, m.sd.Add(req))
245 func (m *XappManager) updateSubscription(w http.ResponseWriter, r *http.Request) {
246 if id, ok := getResourceId(r, w, "id"); ok == true {
247 var req SubscriptionReq
248 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
249 Logger.Error("Invalid request payload - url=%s", r.URL.RequestURI())
250 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
255 if s, ok := m.sd.Update(id, req); ok {
256 respondWithJSON(w, http.StatusOK, s)
258 Logger.Error("Subscription not found - url=%s", r.URL.RequestURI())
259 respondWithError(w, http.StatusNotFound, "Subscription not found")
264 func (m *XappManager) notifyClients() {
265 xapps, err := m.helm.StatusAll()
267 Logger.Info("Couldn't fetch xapps status information: %v", err.Error())
271 m.sd.notifyClients(xapps, "updated")
274 func (m *XappManager) getConfig(w http.ResponseWriter, r *http.Request) {
275 cfg := m.cm.UploadConfig()
276 respondWithJSON(w, http.StatusOK, cfg)
279 func (m *XappManager) createConfig(w http.ResponseWriter, r *http.Request) {
281 if parseConfig(w, r, &c) != nil {
285 if errList, err := m.cm.CreateConfigMap(c); err != nil {
286 if err.Error() != "Validation failed!" {
287 respondWithError(w, http.StatusInternalServerError, err.Error())
289 respondWithJSON(w, http.StatusUnprocessableEntity, errList)
293 respondWithJSON(w, http.StatusCreated, c.Metadata)
296 func (m *XappManager) updateConfig(w http.ResponseWriter, r *http.Request) {
298 if parseConfig(w, r, &c) != nil {
302 if errList, err := m.cm.UpdateConfigMap(c); err != nil {
303 if err.Error() != "Validation failed!" {
304 respondWithError(w, http.StatusInternalServerError, err.Error())
306 respondWithJSON(w, http.StatusInternalServerError, errList)
310 respondWithJSON(w, http.StatusOK, c.Metadata)
313 func (m *XappManager) deleteSingleConfig(w http.ResponseWriter, r *http.Request) {
314 xappName, ok := getResourceId(r, w, "name")
319 md := ConfigMetadata{Name: xappName, Namespace: m.cm.GetNamespace(""), ConfigName: xappName + "-appconfig"}
320 m.delConfig(w, XAppConfig{Metadata: md})
323 func (m *XappManager) deleteConfig(w http.ResponseWriter, r *http.Request) {
325 if parseConfig(w, r, &c) != nil {
332 func (m *XappManager) delConfig(w http.ResponseWriter, c XAppConfig) {
333 if _, err := m.cm.DeleteConfigMap(c); err != nil {
334 respondWithError(w, http.StatusInternalServerError, err.Error())
337 respondWithJSON(w, http.StatusNoContent, nil)
341 func respondWithError(w http.ResponseWriter, code int, message string) {
342 respondWithJSON(w, code, map[string]string{"error": message})
345 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
346 w.Header().Set("Content-Type", "application/json")
349 response, _ := json.Marshal(payload)
354 func getResourceId(r *http.Request, w http.ResponseWriter, pattern string) (id string, ok bool) {
355 if id, ok = mux.Vars(r)[pattern]; ok != true {
356 Logger.Error("Couldn't resolve name/id from the request URL")
357 respondWithError(w, http.StatusMethodNotAllowed, "Couldn't resolve name/id from the request URL")
363 func parseConfig(w http.ResponseWriter, r *http.Request, req *XAppConfig) error {
364 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
365 Logger.Error("Invalid request payload - url=%s", r.URL.RequestURI())
366 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
367 return errors.New("Invalid payload")