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) {
34 m.router = mux.NewRouter().StrictSlash(true)
36 resources := []Resource{
37 {"GET", "/ric/v1/health/alive", m.getHealthStatus},
38 {"GET", "/ric/v1/health/ready", m.getHealthStatus},
40 {"GET", "/ric/v1/xapps", m.getAllXapps},
41 {"GET", "/ric/v1/xapps/{name}", m.getXappByName},
42 {"GET", "/ric/v1/xapps/{name}/instances/{id}", m.getXappInstanceByName},
43 {"POST", "/ric/v1/xapps", m.deployXapp},
44 {"DELETE", "/ric/v1/xapps/{name}", m.undeployXapp},
46 {"GET", "/ric/v1/subscriptions", m.getSubscriptions},
47 {"POST", "/ric/v1/subscriptions", m.addSubscription},
48 {"GET", "/ric/v1/subscriptions/{id}", m.getSubscription},
49 {"DELETE", "/ric/v1/subscriptions/{id}", m.deleteSubscription},
50 {"PUT", "/ric/v1/subscriptions/{id}", m.updateSubscription},
52 {"GET", "/ric/v1/config", m.getConfig},
53 {"POST", "/ric/v1/config", m.createConfig},
54 {"DELETE", "/ric/v1/config", m.deleteConfig},
57 for _, resource := range resources {
58 handler := Logger(resource.HandlerFunc)
59 //handler = m.serviceChecker(handler)
60 m.router.Methods(resource.Method).Path(resource.Url).Handler(handler)
66 func (m *XappManager) finalize(h Helmer) {
67 m.sd = SubscriptionDispatcher{}
77 func (m *XappManager) serviceChecker(inner http.Handler) http.Handler {
78 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
79 if r.URL.RequestURI() == "/ric/v1/health/alive" || m.ready == true {
82 respondWithJSON(w, http.StatusServiceUnavailable, nil)
88 func (m *XappManager) getHealthStatus(w http.ResponseWriter, r *http.Request) {
89 respondWithJSON(w, http.StatusOK, nil)
92 func (m *XappManager) Run() {
93 host := viper.GetString("local.host")
97 log.Printf("Xapp manager started ... serving on %s\n", host)
99 log.Fatal(http.ListenAndServe(host, m.router))
102 func (m *XappManager) getXappByName(w http.ResponseWriter, r *http.Request) {
103 xappName, ok := getResourceId(r, w, "name")
108 if xapp, err := m.helm.Status(xappName); err == nil {
109 respondWithJSON(w, http.StatusOK, xapp)
111 respondWithError(w, http.StatusNotFound, err.Error())
115 func (m *XappManager) getXappInstanceByName(w http.ResponseWriter, r *http.Request) {
116 xappName, ok := getResourceId(r, w, "name")
121 xapp, err := m.helm.Status(xappName)
123 respondWithError(w, http.StatusNotFound, err.Error())
127 xappInstanceName, ok := getResourceId(r, w, "id")
132 for _, v := range xapp.Instances {
133 if v.Name == xappInstanceName {
134 respondWithJSON(w, http.StatusOK, v)
138 mdclog(MdclogErr, "Xapp instance not found - url="+r.URL.RequestURI())
140 respondWithError(w, http.StatusNotFound, "Xapp instance not found")
143 func (m *XappManager) getAllXapps(w http.ResponseWriter, r *http.Request) {
144 xapps, err := m.helm.StatusAll()
146 respondWithError(w, http.StatusInternalServerError, err.Error())
150 respondWithJSON(w, http.StatusOK, xapps)
153 func (m *XappManager) deployXapp(w http.ResponseWriter, r *http.Request) {
155 mdclog(MdclogErr, "No xapp data found in request body - url="+r.URL.RequestURI())
156 respondWithError(w, http.StatusMethodNotAllowed, "No xapp data!")
160 var cm ConfigMetadata
161 if err := json.NewDecoder(r.Body).Decode(&cm); err != nil {
162 mdclog(MdclogErr, "Invalid xapp data in request body - url="+r.URL.RequestURI())
163 respondWithError(w, http.StatusMethodNotAllowed, "Invalid xapp data!")
168 xapp, err := m.helm.Install(cm)
170 respondWithError(w, http.StatusInternalServerError, err.Error())
174 respondWithJSON(w, http.StatusCreated, xapp)
176 m.sd.Publish(xapp, EventType("created"))
179 func (m *XappManager) undeployXapp(w http.ResponseWriter, r *http.Request) {
180 xappName, ok := getResourceId(r, w, "name")
185 xapp, err := m.helm.Delete(xappName)
187 respondWithError(w, http.StatusInternalServerError, err.Error())
191 respondWithJSON(w, http.StatusNoContent, nil)
193 m.sd.Publish(xapp, EventType("deleted"))
196 // API: resthook handlers
197 func (m *XappManager) getSubscriptions(w http.ResponseWriter, r *http.Request) {
198 respondWithJSON(w, http.StatusOK, m.sd.GetAll())
201 func (m *XappManager) getSubscription(w http.ResponseWriter, r *http.Request) {
202 if id, ok := getResourceId(r, w, "id"); ok == true {
203 if s, ok := m.sd.Get(id); ok {
204 respondWithJSON(w, http.StatusOK, s)
206 mdclog(MdclogErr, "Subscription not found - url="+r.URL.RequestURI())
207 respondWithError(w, http.StatusNotFound, "Subscription not found")
212 func (m *XappManager) deleteSubscription(w http.ResponseWriter, r *http.Request) {
213 if id, ok := getResourceId(r, w, "id"); ok == true {
214 if _, ok := m.sd.Delete(id); ok {
215 respondWithJSON(w, http.StatusNoContent, nil)
217 mdclog(MdclogErr, "Subscription not found - url="+r.URL.RequestURI())
218 respondWithError(w, http.StatusNotFound, "Subscription not found")
223 func (m *XappManager) addSubscription(w http.ResponseWriter, r *http.Request) {
224 var req SubscriptionReq
225 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
226 mdclog(MdclogErr, "Invalid request payload - url="+r.URL.RequestURI())
227 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
232 respondWithJSON(w, http.StatusCreated, m.sd.Add(req))
235 func (m *XappManager) updateSubscription(w http.ResponseWriter, r *http.Request) {
236 if id, ok := getResourceId(r, w, "id"); ok == true {
237 var req SubscriptionReq
238 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
239 mdclog(MdclogErr, "Invalid request payload - url="+r.URL.RequestURI())
240 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
245 if s, ok := m.sd.Update(id, req); ok {
246 respondWithJSON(w, http.StatusOK, s)
248 mdclog(MdclogErr, "Subscription not found - url="+r.URL.RequestURI())
249 respondWithError(w, http.StatusNotFound, "Subscription not found")
254 func (m *XappManager) notifyClients() {
255 xapps, err := m.helm.StatusAll()
257 mdclog(MdclogInfo, "Couldn't fetch xapps status information"+err.Error())
261 m.sd.notifyClients(xapps, "updated")
264 func (m *XappManager) getConfig(w http.ResponseWriter, r *http.Request) {
265 respondWithJSON(w, http.StatusOK, UploadConfig())
268 func (m *XappManager) createConfig(w http.ResponseWriter, r *http.Request) {
270 if parseConfig(w, r, &c) != nil {
274 if err := CreateConfigMap(c); err != nil {
275 respondWithError(w, http.StatusInternalServerError, err.Error())
278 respondWithJSON(w, http.StatusCreated, nil)
281 func (m *XappManager) deleteConfig(w http.ResponseWriter, r *http.Request) {
283 if parseConfig(w, r, &c) != nil {
287 if _, err := DeleteConfigMap(c); err != nil {
288 respondWithError(w, http.StatusInternalServerError, err.Error())
291 respondWithJSON(w, http.StatusNotFound, nil)
295 func respondWithError(w http.ResponseWriter, code int, message string) {
296 respondWithJSON(w, code, map[string]string{"error": message})
299 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
300 w.Header().Set("Content-Type", "application/json")
303 response, _ := json.Marshal(payload)
308 func getResourceId(r *http.Request, w http.ResponseWriter, pattern string) (id string, ok bool) {
309 if id, ok = mux.Vars(r)[pattern]; ok != true {
310 mdclog(MdclogErr, "Couldn't resolve name/id from the request URL")
311 respondWithError(w, http.StatusMethodNotAllowed, "Couldn't resolve name/id from the request URL")
317 func parseConfig(w http.ResponseWriter, r *http.Request, req *XAppConfig) error {
318 if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
319 mdclog(MdclogErr, "Invalid request payload - url="+r.URL.RequestURI())
320 respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
321 return errors.New("Invalid payload")