Update RMR library Gerrit repo
[ric-plt/appmgr.git] / src / api.go
1 /*
2 ==================================================================================
3   Copyright (c) 2019 AT&T Intellectual Property.
4   Copyright (c) 2019 Nokia
5
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
9
10        http://www.apache.org/licenses/LICENSE-2.0
11
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 ==================================================================================
18 */
19
20 package main
21
22 import (
23     "encoding/json"
24     "github.com/gorilla/mux"
25     "github.com/spf13/viper"
26     "log"
27     "net/http"
28 )
29
30 // API functions
31
32 func (m *XappManager) Initialize(h Helmer) *mux.Router {
33     m.sd = SubscriptionDispatcher{}
34     m.sd.Initialize()
35     m.helm = h
36     m.router = mux.NewRouter().StrictSlash(true)
37
38     resources := []Resource{
39         Resource{"GET", "/ric/v1/health", m.getHealthStatus},
40
41         Resource{"GET", "/ric/v1/xapps", m.getAllXapps},
42         Resource{"GET", "/ric/v1/xapps/{name}", m.getXappByName},
43         Resource{"GET", "/ric/v1/xapps/{name}/instances/{id}", m.getXappInstanceByName},
44         Resource{"POST", "/ric/v1/xapps", m.deployXapp},
45         Resource{"DELETE", "/ric/v1/xapps/{name}", m.undeployXapp},
46
47         Resource{"GET", "/ric/v1/subscriptions", m.getSubscriptions},
48         Resource{"POST", "/ric/v1/subscriptions", m.addSubscription},
49         Resource{"GET", "/ric/v1/subscriptions/{id}", m.getSubscription},
50         Resource{"DELETE", "/ric/v1/subscriptions/{id}", m.deleteSubscription},
51         Resource{"PUT", "/ric/v1/subscriptions/{id}", m.updateSubscription},
52     }
53
54     for _, resource := range resources {
55         handler := Logger(resource.HandlerFunc)
56         m.router.Methods(resource.Method).Path(resource.Url).Handler(handler)
57     }
58
59     return m.router
60 }
61
62 // Health monitoring
63 func (m *XappManager) getHealthStatus(w http.ResponseWriter, r *http.Request) {
64     respondWithJSON(w, http.StatusOK, nil)
65 }
66
67 // API: XAPP handlers
68 func (m *XappManager) Run() {
69     host := viper.GetString("local.host")
70     if host == "" {
71         host = ":8080"
72     }
73     log.Printf("Xapp manager started ... serving on %s\n", host)
74
75     log.Fatal(http.ListenAndServe(host, m.router))
76 }
77
78 func (m *XappManager) getXappByName(w http.ResponseWriter, r *http.Request) {
79     xappName, ok := getResourceId(r, w, "name")
80     if ok != true {
81         return
82     }
83
84     if xapp, err := m.helm.Status(xappName); err == nil {
85         respondWithJSON(w, http.StatusOK, xapp)
86     } else {
87         respondWithError(w, http.StatusNotFound, err.Error())
88     }
89 }
90
91 func (m *XappManager) getXappInstanceByName(w http.ResponseWriter, r *http.Request) {
92     xappName, ok := getResourceId(r, w, "name")
93     if ok != true {
94         return
95     }
96
97     xapp, err := m.helm.Status(xappName)
98     if err != nil {
99         respondWithError(w, http.StatusNotFound, err.Error())
100         return
101     }
102
103     xappInstanceName, ok := getResourceId(r, w, "id")
104     if ok != true {
105         return
106     }
107
108     for _, v := range xapp.Instances {
109         if v.Name == xappInstanceName {
110             respondWithJSON(w, http.StatusOK, v)
111             return
112         }
113     }
114     mdclog(Mdclog_err, "Xapp instance not found - url=" + r.URL.RequestURI())
115
116     respondWithError(w, http.StatusNotFound, "Xapp instance not found")
117 }
118
119 func (m *XappManager) getAllXapps(w http.ResponseWriter, r *http.Request) {
120     xapps, err := m.helm.StatusAll()
121     if err != nil {
122         respondWithError(w, http.StatusInternalServerError, err.Error())
123         return
124     }
125
126     respondWithJSON(w, http.StatusOK, xapps)
127 }
128
129 func (m *XappManager) deployXapp(w http.ResponseWriter, r *http.Request) {
130     if r.Body == nil {
131         mdclog(Mdclog_err, "No xapp data found in request body - url=" + r.URL.RequestURI())
132         respondWithError(w, http.StatusMethodNotAllowed, "No xapp data!")
133         return
134     }
135
136     var xapp Xapp
137     if err := json.NewDecoder(r.Body).Decode(&xapp); err != nil {
138         mdclog(Mdclog_err, "Invalid xapp data in request body - url=" + r.URL.RequestURI())
139         respondWithError(w, http.StatusMethodNotAllowed, "Invalid xapp data!")
140         return
141     }
142     defer r.Body.Close()
143
144     xapp, err := m.helm.Install(xapp.Name)
145     if err != nil {
146         respondWithError(w, http.StatusInternalServerError, err.Error())
147         return
148     }
149
150     respondWithJSON(w, http.StatusCreated, xapp)
151
152     m.sd.Publish(xapp, EventType("created"))
153 }
154
155 func (m *XappManager) undeployXapp(w http.ResponseWriter, r *http.Request) {
156     xappName, ok := getResourceId(r, w, "name")
157     if ok != true {
158         return
159     }
160
161     xapp, err := m.helm.Delete(xappName)
162     if err != nil {
163         respondWithError(w, http.StatusInternalServerError, err.Error())
164         return
165     }
166
167     respondWithJSON(w, http.StatusNoContent, nil)
168
169     m.sd.Publish(xapp, EventType("deleted"))
170 }
171
172 // API: resthook handlers
173 func (m *XappManager) getSubscriptions(w http.ResponseWriter, r *http.Request) {
174     respondWithJSON(w, http.StatusOK, m.sd.GetAll())
175 }
176
177 func (m *XappManager) getSubscription(w http.ResponseWriter, r *http.Request) {
178     if id, ok := getResourceId(r, w, "id"); ok == true {
179         if s, ok := m.sd.Get(id); ok {
180             respondWithJSON(w, http.StatusOK, s)
181         } else {
182             mdclog(Mdclog_err, "Subscription not found - url=" + r.URL.RequestURI())
183             respondWithError(w, http.StatusNotFound, "Subscription not found")
184         }
185     }
186 }
187
188 func (m *XappManager) deleteSubscription(w http.ResponseWriter, r *http.Request) {
189     if id, ok := getResourceId(r, w, "id"); ok == true {
190         if _, ok := m.sd.Delete(id); ok {
191             respondWithJSON(w, http.StatusNoContent, nil)
192         } else {
193             mdclog(Mdclog_err, "Subscription not found - url=" + r.URL.RequestURI())
194             respondWithError(w, http.StatusNotFound, "Subscription not found")
195         }
196     }
197 }
198
199 func (m *XappManager) addSubscription(w http.ResponseWriter, r *http.Request) {
200     var req SubscriptionReq
201     if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
202         mdclog(Mdclog_err, "Invalid request payload - url=" + r.URL.RequestURI())
203         respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
204         return
205     }
206     defer r.Body.Close()
207
208     respondWithJSON(w, http.StatusCreated, m.sd.Add(req))
209 }
210
211 func (m *XappManager) updateSubscription(w http.ResponseWriter, r *http.Request) {
212     if id, ok := getResourceId(r, w, "id"); ok == true {
213         var req SubscriptionReq
214         if r.Body == nil || json.NewDecoder(r.Body).Decode(&req) != nil {
215             mdclog(Mdclog_err, "Invalid request payload - url=" + r.URL.RequestURI())
216             respondWithError(w, http.StatusMethodNotAllowed, "Invalid request payload")
217             return
218         }
219         defer r.Body.Close()
220
221         if s, ok := m.sd.Update(id, req); ok {
222             respondWithJSON(w, http.StatusOK, s)
223         } else {
224             mdclog(Mdclog_err, "Subscription not found - url=" + r.URL.RequestURI())
225             respondWithError(w, http.StatusNotFound, "Subscription not found")
226         }
227     }
228 }
229
230 // Helper functions
231 func respondWithError(w http.ResponseWriter, code int, message string) {
232     respondWithJSON(w, code, map[string]string{"error": message})
233 }
234
235 func respondWithJSON(w http.ResponseWriter, code int, payload interface{}) {
236     w.Header().Set("Content-Type", "application/json")
237     w.WriteHeader(code)
238     if payload != nil {
239         response, _ := json.Marshal(payload)
240         w.Write(response)
241     }
242 }
243
244 func getResourceId(r *http.Request, w http.ResponseWriter, pattern string) (id string, ok bool) {
245     if id, ok = mux.Vars(r)[pattern]; ok != true {
246         mdclog(Mdclog_err, "Couldn't resolve name/id from the request URL")
247         respondWithError(w, http.StatusMethodNotAllowed, "Couldn't resolve name/id from the request URL")
248         return
249     }
250     return
251 }