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 ==================================================================================
24 "github.com/gorilla/mux"
25 "github.com/spf13/viper"
32 func (m *XappManager) Initialize(h Helmer) {
34 m.sd = SubscriptionDispatcher{}
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/{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},
58 for _, resource := range resources {
59 handler := Logger(resource.HandlerFunc)
60 //handler = m.serviceChecker(handler)
61 m.router.Methods(resource.Method).Path(resource.Url).Handler(handler)
67 func (m *XappManager) finalize(h Helmer) {
68 m.sd = SubscriptionDispatcher{}
78 func (m *XappManager) serviceChecker(inner http.Handler) http.Handler {
79 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
80 if r.URL.RequestURI() == "/ric/v1/health/alive" || m.ready == true {
83 respondWithJSON(w, http.StatusServiceUnavailable, nil)
89 func (m *XappManager) getHealthStatus(w http.ResponseWriter, r *http.Request) {
90 respondWithJSON(w, http.StatusOK, nil)
93 func (m *XappManager) Run() {
94 host := viper.GetString("local.host")
98 log.Printf("Xapp manager started ... serving on %s\n", host)
100 log.Fatal(http.ListenAndServe(host, m.router))
103 func (m *XappManager) getXappByName(w http.ResponseWriter, r *http.Request) {
104 xappName, ok := getResourceId(r, w, "name")
109 if xapp, err := m.helm.Status(xappName); err == nil {
110 respondWithJSON(w, http.StatusOK, xapp)
112 respondWithError(w, http.StatusNotFound, err.Error())
116 func (m *XappManager) getXappInstanceByName(w http.ResponseWriter, r *http.Request) {
117 xappName, ok := getResourceId(r, w, "name")
122 xapp, err := m.helm.Status(xappName)
124 respondWithError(w, http.StatusNotFound, err.Error())
128 xappInstanceName, ok := getResourceId(r, w, "id")
133 for _, v := range xapp.Instances {
134 if v.Name == xappInstanceName {
135 respondWithJSON(w, http.StatusOK, v)
139 mdclog(MdclogErr, "Xapp instance not found - url="+r.URL.RequestURI())
141 respondWithError(w, http.StatusNotFound, "Xapp instance not found")
144 func (m *XappManager) getAllXapps(w http.ResponseWriter, r *http.Request) {
145 xapps, err := m.helm.StatusAll()
147 respondWithError(w, http.StatusInternalServerError, err.Error())
151 respondWithJSON(w, http.StatusOK, xapps)
154 func (m *XappManager) deployXapp(w http.ResponseWriter, r *http.Request) {
156 mdclog(MdclogErr, "No xapp data found in request body - url="+r.URL.RequestURI())
157 respondWithError(w, http.StatusMethodNotAllowed, "No xapp data!")
162 if err := json.NewDecoder(r.Body).Decode(&xapp); err != nil {
163 mdclog(MdclogErr, "Invalid xapp data in request body - url="+r.URL.RequestURI())
164 respondWithError(w, http.StatusMethodNotAllowed, "Invalid xapp data!")
169 xapp, err := m.helm.Install(xapp.Name)
171 respondWithError(w, http.StatusInternalServerError, err.Error())
175 respondWithJSON(w, http.StatusCreated, xapp)
177 m.sd.Publish(xapp, EventType("created"))
180 func (m *XappManager) undeployXapp(w http.ResponseWriter, r *http.Request) {
181 xappName, ok := getResourceId(r, w, "name")
186 xapp, err := m.helm.Delete(xappName)
188 respondWithError(w, http.StatusInternalServerError, err.Error())
192 respondWithJSON(w, http.StatusNoContent, nil)
194 m.sd.Publish(xapp, EventType("deleted"))
197 // API: resthook handlers
198 func (m *XappManager) getSubscriptions(w http.ResponseWriter, r *http.Request) {
199 respondWithJSON(w, http.StatusOK, m.sd.GetAll())
202 func (m *XappManager) getSubscription(w http.ResponseWriter, r *http.Request) {
203 if id, ok := getResourceId(r, w, "id"); ok == true {
204 if s, ok := m.sd.Get(id); ok {
205 respondWithJSON(w, http.StatusOK, s)
207 mdclog(MdclogErr, "Subscription not found - url="+r.URL.RequestURI())
208 respondWithError(w, http.StatusNotFound, "Subscription not found")
213 func (m *XappManager) deleteSubscription(w http.ResponseWriter, r *http.Request) {
214 if id, ok := getResourceId(r, w, "id"); ok == true {
215 if _, ok := m.sd.Delete(id); ok {
216 respondWithJSON(w, http.StatusNoContent, nil)
218 mdclog(MdclogErr, "Subscription not found - url="+r.URL.RequestURI())
219 respondWithError(w, http.StatusNotFound, "Subscription not found")
224 func (m *XappManager) addSubscription(w http.ResponseWriter, r *http.Request) {
225 var req SubscriptionReq
226 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
227 mdclog(MdclogErr, "Invalid request payload - url="+r.URL.RequestURI())
228 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
233 respondWithJSON(w, http.StatusCreated, m.sd.Add(req))
236 func (m *XappManager) updateSubscription(w http.ResponseWriter, r *http.Request) {
237 if id, ok := getResourceId(r, w, "id"); ok == true {
238 var req SubscriptionReq
239 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
240 mdclog(MdclogErr, "Invalid request payload - url="+r.URL.RequestURI())
241 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
246 if s, ok := m.sd.Update(id, req); ok {
247 respondWithJSON(w, http.StatusOK, s)
249 mdclog(MdclogErr, "Subscription not found - url="+r.URL.RequestURI())
250 respondWithError(w, http.StatusNotFound, "Subscription not found")
255 func (m *XappManager) notifyClients() {
256 xapps, err := m.helm.StatusAll()
258 mdclog(MdclogInfo, "Couldn't fetch xapps status information"+err.Error())
262 m.sd.notifyClients(xapps, "updated")
266 func respondWithError(w http.ResponseWriter, code int, message string) {
267 respondWithJSON(w, code, map[string]string{"error": message})
270 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
271 w.Header().Set("Content-Type", "application/json")
274 response, _ := json.Marshal(payload)
279 func getResourceId(r *http.Request, w http.ResponseWriter, pattern string) (id string, ok bool) {
280 if id, ok = mux.Vars(r)[pattern]; ok != true {
281 mdclog(MdclogErr, "Couldn't resolve name/id from the request URL")
282 respondWithError(w, http.StatusMethodNotAllowed, "Couldn't resolve name/id from the request URL")