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/{name}", m.getXappByName},
46 {"GET", "/ric/v1/xapps/{name}/instances/{id}", m.getXappInstanceByName},
47 {"POST", "/ric/v1/xapps", m.deployXapp},
48 {"DELETE", "/ric/v1/xapps/{name}", m.undeployXapp},
50 {"GET", "/ric/v1/subscriptions", m.getSubscriptions},
51 {"POST", "/ric/v1/subscriptions", m.addSubscription},
52 {"GET", "/ric/v1/subscriptions/{id}", m.getSubscription},
53 {"DELETE", "/ric/v1/subscriptions/{id}", m.deleteSubscription},
54 {"PUT", "/ric/v1/subscriptions/{id}", m.updateSubscription},
56 {"GET", "/ric/v1/config", m.getConfig},
57 {"POST", "/ric/v1/config", m.createConfig},
58 {"PUT", "/ric/v1/config", m.updateConfig},
59 {"DELETE", "/ric/v1/config", m.deleteConfig},
60 {"DELETE", "/ric/v1/config/{name}", m.deleteSingleConfig},
63 for _, resource := range resources {
64 handler := LogRestRequests(resource.HandlerFunc)
65 //handler = m.serviceChecker(handler)
66 m.router.Methods(resource.Method).Path(resource.Url).Handler(handler)
72 func (m *XappManager) finalize(h Helmer) {
73 m.sd = SubscriptionDispatcher{}
82 func (m *XappManager) serviceChecker(inner http.Handler) http.Handler {
83 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
84 if r.URL.RequestURI() == "/ric/v1/health/alive" || m.ready == true {
87 respondWithJSON(w, http.StatusServiceUnavailable, nil)
93 func (m *XappManager) getHealthStatus(w http.ResponseWriter, r *http.Request) {
94 respondWithJSON(w, http.StatusOK, nil)
97 func (m *XappManager) Run() {
98 host := viper.GetString("local.host")
102 log.Printf("Xapp manager started ... serving on %s\n", host)
104 log.Fatal(http.ListenAndServe(host, m.router))
107 func (m *XappManager) getXappByName(w http.ResponseWriter, r *http.Request) {
108 xappName, ok := getResourceId(r, w, "name")
113 if xapp, err := m.helm.Status(xappName); err == nil {
114 respondWithJSON(w, http.StatusOK, xapp)
116 respondWithError(w, http.StatusNotFound, err.Error())
120 func (m *XappManager) getXappInstanceByName(w http.ResponseWriter, r *http.Request) {
121 xappName, ok := getResourceId(r, w, "name")
126 xapp, err := m.helm.Status(xappName)
128 respondWithError(w, http.StatusNotFound, err.Error())
132 xappInstanceName, ok := getResourceId(r, w, "id")
137 for _, v := range xapp.Instances {
138 if v.Name == xappInstanceName {
139 respondWithJSON(w, http.StatusOK, v)
143 Logger.Error("Xapp instance not found - url=%s", r.URL.RequestURI())
145 respondWithError(w, http.StatusNotFound, "Xapp instance not found")
148 func (m *XappManager) getAllXapps(w http.ResponseWriter, r *http.Request) {
149 xapps, err := m.helm.StatusAll()
151 respondWithError(w, http.StatusInternalServerError, err.Error())
155 respondWithJSON(w, http.StatusOK, xapps)
158 func (m *XappManager) deployXapp(w http.ResponseWriter, r *http.Request) {
160 Logger.Error("No xapp data found in request body - url=%s", r.URL.RequestURI())
161 respondWithError(w, http.StatusMethodNotAllowed, "No xapp data!")
166 if err := json.NewDecoder(r.Body).Decode(&cm); err != nil {
167 Logger.Error("Invalid xapp data in request body - url=%s", r.URL.RequestURI())
168 respondWithError(w, http.StatusMethodNotAllowed, "Invalid xapp data!")
173 xapp, err := m.helm.Install(cm)
175 respondWithError(w, http.StatusInternalServerError, err.Error())
179 respondWithJSON(w, http.StatusCreated, xapp)
181 m.sd.Publish(xapp, EventType("created"))
184 func (m *XappManager) undeployXapp(w http.ResponseWriter, r *http.Request) {
185 xappName, ok := getResourceId(r, w, "name")
190 xapp, err := m.helm.Delete(xappName)
192 respondWithError(w, http.StatusInternalServerError, err.Error())
196 respondWithJSON(w, http.StatusNoContent, nil)
198 m.sd.Publish(xapp, EventType("deleted"))
201 // API: resthook handlers
202 func (m *XappManager) getSubscriptions(w http.ResponseWriter, r *http.Request) {
203 respondWithJSON(w, http.StatusOK, m.sd.GetAll())
206 func (m *XappManager) getSubscription(w http.ResponseWriter, r *http.Request) {
207 if id, ok := getResourceId(r, w, "id"); ok == true {
208 if s, ok := m.sd.Get(id); ok {
209 respondWithJSON(w, http.StatusOK, s)
211 Logger.Error("Subscription not found - url=%s", r.URL.RequestURI())
212 respondWithError(w, http.StatusNotFound, "Subscription not found")
217 func (m *XappManager) deleteSubscription(w http.ResponseWriter, r *http.Request) {
218 if id, ok := getResourceId(r, w, "id"); ok == true {
219 if _, ok := m.sd.Delete(id); ok {
220 respondWithJSON(w, http.StatusNoContent, nil)
222 Logger.Error("Subscription not found - url=%s", r.URL.RequestURI())
223 respondWithError(w, http.StatusNotFound, "Subscription not found")
228 func (m *XappManager) addSubscription(w http.ResponseWriter, r *http.Request) {
229 var req SubscriptionReq
230 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
231 Logger.Error("Invalid request payload - url=%s", r.URL.RequestURI())
232 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
237 respondWithJSON(w, http.StatusCreated, m.sd.Add(req))
240 func (m *XappManager) updateSubscription(w http.ResponseWriter, r *http.Request) {
241 if id, ok := getResourceId(r, w, "id"); ok == true {
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 if s, ok := m.sd.Update(id, req); ok {
251 respondWithJSON(w, http.StatusOK, s)
253 Logger.Error("Subscription not found - url=%s", r.URL.RequestURI())
254 respondWithError(w, http.StatusNotFound, "Subscription not found")
259 func (m *XappManager) notifyClients() {
260 xapps, err := m.helm.StatusAll()
262 Logger.Info("Couldn't fetch xapps status information: %v", err.Error())
266 m.sd.notifyClients(xapps, "updated")
269 func (m *XappManager) getConfig(w http.ResponseWriter, r *http.Request) {
270 cfg := m.cm.UploadConfig()
271 respondWithJSON(w, http.StatusOK, cfg)
274 func (m *XappManager) createConfig(w http.ResponseWriter, r *http.Request) {
276 if parseConfig(w, r, &c) != nil {
280 if errList, err := m.cm.CreateConfigMap(c); err != nil {
281 if err.Error() != "Validation failed!" {
282 respondWithError(w, http.StatusInternalServerError, err.Error())
284 respondWithJSON(w, http.StatusUnprocessableEntity, errList)
288 respondWithJSON(w, http.StatusCreated, c.Metadata)
291 func (m *XappManager) updateConfig(w http.ResponseWriter, r *http.Request) {
293 if parseConfig(w, r, &c) != nil {
297 if errList, err := m.cm.UpdateConfigMap(c); err != nil {
298 if err.Error() != "Validation failed!" {
299 respondWithError(w, http.StatusInternalServerError, err.Error())
301 respondWithJSON(w, http.StatusInternalServerError, errList)
305 respondWithJSON(w, http.StatusOK, c.Metadata)
308 func (m *XappManager) deleteSingleConfig(w http.ResponseWriter, r *http.Request) {
309 xappName, ok := getResourceId(r, w, "name")
314 md := ConfigMetadata{Name: xappName, Namespace: m.cm.GetNamespace(""), ConfigName: xappName + "-appconfig"}
315 m.delConfig(w, XAppConfig{Metadata: md})
318 func (m *XappManager) deleteConfig(w http.ResponseWriter, r *http.Request) {
320 if parseConfig(w, r, &c) != nil {
327 func (m *XappManager) delConfig(w http.ResponseWriter, c XAppConfig) {
328 if _, err := m.cm.DeleteConfigMap(c); err != nil {
329 respondWithError(w, http.StatusInternalServerError, err.Error())
332 respondWithJSON(w, http.StatusNoContent, nil)
336 func respondWithError(w http.ResponseWriter, code int, message string) {
337 respondWithJSON(w, code, map[string]string{"error": message})
340 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
341 w.Header().Set("Content-Type", "application/json")
344 response, _ := json.Marshal(payload)
349 func getResourceId(r *http.Request, w http.ResponseWriter, pattern string) (id string, ok bool) {
350 if id, ok = mux.Vars(r)[pattern]; ok != true {
351 Logger.Error("Couldn't resolve name/id from the request URL")
352 respondWithError(w, http.StatusMethodNotAllowed, "Couldn't resolve name/id from the request URL")
358 func parseConfig(w http.ResponseWriter, r *http.Request, req *XAppConfig) error {
359 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
360 Logger.Error("Invalid request payload - url=%s", r.URL.RequestURI())
361 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
362 return errors.New("Invalid payload")