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},
62 for _, resource := range resources {
63 handler := Logger(resource.HandlerFunc)
64 //handler = m.serviceChecker(handler)
65 m.router.Methods(resource.Method).Path(resource.Url).Handler(handler)
71 func (m *XappManager) finalize(h Helmer) {
72 m.sd = SubscriptionDispatcher{}
81 func (m *XappManager) serviceChecker(inner http.Handler) http.Handler {
82 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
83 if r.URL.RequestURI() == "/ric/v1/health/alive" || m.ready == true {
86 respondWithJSON(w, http.StatusServiceUnavailable, nil)
92 func (m *XappManager) getHealthStatus(w http.ResponseWriter, r *http.Request) {
93 respondWithJSON(w, http.StatusOK, nil)
96 func (m *XappManager) Run() {
97 host := viper.GetString("local.host")
101 log.Printf("Xapp manager started ... serving on %s\n", host)
103 log.Fatal(http.ListenAndServe(host, m.router))
106 func (m *XappManager) getXappByName(w http.ResponseWriter, r *http.Request) {
107 xappName, ok := getResourceId(r, w, "name")
112 if xapp, err := m.helm.Status(xappName); err == nil {
113 respondWithJSON(w, http.StatusOK, xapp)
115 respondWithError(w, http.StatusNotFound, err.Error())
119 func (m *XappManager) getXappInstanceByName(w http.ResponseWriter, r *http.Request) {
120 xappName, ok := getResourceId(r, w, "name")
125 xapp, err := m.helm.Status(xappName)
127 respondWithError(w, http.StatusNotFound, err.Error())
131 xappInstanceName, ok := getResourceId(r, w, "id")
136 for _, v := range xapp.Instances {
137 if v.Name == xappInstanceName {
138 respondWithJSON(w, http.StatusOK, v)
142 mdclog(MdclogErr, "Xapp instance not found - url="+r.URL.RequestURI())
144 respondWithError(w, http.StatusNotFound, "Xapp instance not found")
147 func (m *XappManager) getAllXapps(w http.ResponseWriter, r *http.Request) {
148 xapps, err := m.helm.StatusAll()
150 respondWithError(w, http.StatusInternalServerError, err.Error())
154 respondWithJSON(w, http.StatusOK, xapps)
157 func (m *XappManager) deployXapp(w http.ResponseWriter, r *http.Request) {
159 mdclog(MdclogErr, "No xapp data found in request body - url="+r.URL.RequestURI())
160 respondWithError(w, http.StatusMethodNotAllowed, "No xapp data!")
165 if err := json.NewDecoder(r.Body).Decode(&cm); err != nil {
166 mdclog(MdclogErr, "Invalid xapp data in request body - url="+r.URL.RequestURI())
167 respondWithError(w, http.StatusMethodNotAllowed, "Invalid xapp data!")
172 xapp, err := m.helm.Install(cm)
174 respondWithError(w, http.StatusInternalServerError, err.Error())
178 respondWithJSON(w, http.StatusCreated, xapp)
180 m.sd.Publish(xapp, EventType("created"))
183 func (m *XappManager) undeployXapp(w http.ResponseWriter, r *http.Request) {
184 xappName, ok := getResourceId(r, w, "name")
189 xapp, err := m.helm.Delete(xappName)
191 respondWithError(w, http.StatusInternalServerError, err.Error())
195 respondWithJSON(w, http.StatusNoContent, nil)
197 m.sd.Publish(xapp, EventType("deleted"))
200 // API: resthook handlers
201 func (m *XappManager) getSubscriptions(w http.ResponseWriter, r *http.Request) {
202 respondWithJSON(w, http.StatusOK, m.sd.GetAll())
205 func (m *XappManager) getSubscription(w http.ResponseWriter, r *http.Request) {
206 if id, ok := getResourceId(r, w, "id"); ok == true {
207 if s, ok := m.sd.Get(id); ok {
208 respondWithJSON(w, http.StatusOK, s)
210 mdclog(MdclogErr, "Subscription not found - url="+r.URL.RequestURI())
211 respondWithError(w, http.StatusNotFound, "Subscription not found")
216 func (m *XappManager) deleteSubscription(w http.ResponseWriter, r *http.Request) {
217 if id, ok := getResourceId(r, w, "id"); ok == true {
218 if _, ok := m.sd.Delete(id); ok {
219 respondWithJSON(w, http.StatusNoContent, nil)
221 mdclog(MdclogErr, "Subscription not found - url="+r.URL.RequestURI())
222 respondWithError(w, http.StatusNotFound, "Subscription not found")
227 func (m *XappManager) addSubscription(w http.ResponseWriter, r *http.Request) {
228 var req SubscriptionReq
229 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
230 mdclog(MdclogErr, "Invalid request payload - url="+r.URL.RequestURI())
231 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
236 respondWithJSON(w, http.StatusCreated, m.sd.Add(req))
239 func (m *XappManager) updateSubscription(w http.ResponseWriter, r *http.Request) {
240 if id, ok := getResourceId(r, w, "id"); ok == true {
241 var req SubscriptionReq
242 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
243 mdclog(MdclogErr, "Invalid request payload - url="+r.URL.RequestURI())
244 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
249 if s, ok := m.sd.Update(id, req); ok {
250 respondWithJSON(w, http.StatusOK, s)
252 mdclog(MdclogErr, "Subscription not found - url="+r.URL.RequestURI())
253 respondWithError(w, http.StatusNotFound, "Subscription not found")
258 func (m *XappManager) notifyClients() {
259 xapps, err := m.helm.StatusAll()
261 mdclog(MdclogInfo, "Couldn't fetch xapps status information"+err.Error())
265 m.sd.notifyClients(xapps, "updated")
268 func (m *XappManager) getConfig(w http.ResponseWriter, r *http.Request) {
269 cfg := m.cm.UploadConfig()
270 respondWithJSON(w, http.StatusOK, cfg)
273 func (m *XappManager) createConfig(w http.ResponseWriter, r *http.Request) {
275 if parseConfig(w, r, &c) != nil {
279 if errList, err := m.cm.CreateConfigMap(c); err != nil {
280 if err.Error() != "Validation failed!" {
281 respondWithError(w, http.StatusInternalServerError, err.Error())
283 respondWithJSON(w, http.StatusUnprocessableEntity, errList)
287 respondWithJSON(w, http.StatusCreated, nil)
290 func (m *XappManager) updateConfig(w http.ResponseWriter, r *http.Request) {
292 if parseConfig(w, r, &c) != nil {
296 if errList, err := m.cm.UpdateConfigMap(c); err != nil {
297 if err.Error() != "Validation failed!" {
298 respondWithError(w, http.StatusInternalServerError, err.Error())
300 respondWithJSON(w, http.StatusUnprocessableEntity, errList)
304 respondWithJSON(w, http.StatusOK, nil)
307 func (m *XappManager) deleteConfig(w http.ResponseWriter, r *http.Request) {
309 if parseConfig(w, r, &c) != nil {
313 if _, err := m.cm.DeleteConfigMap(c); err != nil {
314 respondWithError(w, http.StatusInternalServerError, err.Error())
317 respondWithJSON(w, http.StatusNotFound, nil)
321 func respondWithError(w http.ResponseWriter, code int, message string) {
322 respondWithJSON(w, code, map[string]string{"error": message})
325 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
326 w.Header().Set("Content-Type", "application/json")
329 response, _ := json.Marshal(payload)
334 func getResourceId(r *http.Request, w http.ResponseWriter, pattern string) (id string, ok bool) {
335 if id, ok = mux.Vars(r)[pattern]; ok != true {
336 mdclog(MdclogErr, "Couldn't resolve name/id from the request URL")
337 respondWithError(w, http.StatusMethodNotAllowed, "Couldn't resolve name/id from the request URL")
343 func parseConfig(w http.ResponseWriter, r *http.Request, req *XAppConfig) error {
344 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
345 mdclog(MdclogErr, "Invalid request payload - url="+r.URL.RequestURI())
346 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
347 return errors.New("Invalid payload")