blob: 5735d8964646040699ca2bc722fb77895c9215a1 [file] [log] [blame]
DenisGNoonancbac30a2023-10-24 09:43:52 +01001// -
2// ========================LICENSE_START=================================
3// O-RAN-SC
4// %%
5// Copyright (C) 2023-2024: OpenInfra Foundation Europe
6// %%
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License at
10//
11// http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18// ========================LICENSE_END===================================
19//
20
21package publishservice
22
23import (
24 "context"
25 "fmt"
26 "net/http"
27 "path"
28
29 echo "github.com/labstack/echo/v4"
30 log "github.com/sirupsen/logrus"
31
32 "oransc.org/nonrtric/servicemanager/internal/common29122"
33 publishapi "oransc.org/nonrtric/servicemanager/internal/publishserviceapi"
34)
35
36type PublishService struct {
37 KongDomain string;
38 KongProtocol string;
DenisGNoonancbac30a2023-10-24 09:43:52 +010039 KongControlPlanePort common29122.Port;
DenisGNoonanfdb05902024-04-19 18:22:31 +010040 KongControlPlaneIPv4 common29122.Ipv4Addr;
41 KongDataPlaneIPv4 common29122.Ipv4Addr;
42 KongDataPlanePort common29122.Port;
DenisGNoonancbac30a2023-10-24 09:43:52 +010043 CapifProtocol string;
44 CapifIPv4 common29122.Ipv4Addr;
45 CapifPort common29122.Port;
46}
47
48// Creates a service that implements both the PublishRegister and the publishserviceapi.ServerInterface interfaces.
DenisGNoonanfdb05902024-04-19 18:22:31 +010049func NewPublishService(
50 kongDomain string,
51 kongProtocol string,
52 kongControlPlaneIPv4 common29122.Ipv4Addr,
53 kongControlPlanePort common29122.Port,
54 kongDataPlaneIPv4 common29122.Ipv4Addr,
55 kongDataPlanePort common29122.Port,
56 capifProtocol string,
57 capifIPv4 common29122.Ipv4Addr,
58 capifPort common29122.Port) *PublishService {
DenisGNoonancbac30a2023-10-24 09:43:52 +010059 return &PublishService{
60 KongDomain : kongDomain,
61 KongProtocol : kongProtocol,
DenisGNoonanfdb05902024-04-19 18:22:31 +010062 KongControlPlaneIPv4 : kongControlPlaneIPv4,
DenisGNoonancbac30a2023-10-24 09:43:52 +010063 KongControlPlanePort : kongControlPlanePort,
DenisGNoonanfdb05902024-04-19 18:22:31 +010064 KongDataPlaneIPv4 : kongDataPlaneIPv4,
65 KongDataPlanePort : kongDataPlanePort,
DenisGNoonancbac30a2023-10-24 09:43:52 +010066 CapifProtocol : capifProtocol,
67 CapifIPv4 : capifIPv4,
68 CapifPort : capifPort,
69 }
70}
71
72// Publish a new API.
73func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) error {
74 log.Tracef("entering PostApfIdServiceApis apfId %s", apfId)
DenisGNoonanfdb05902024-04-19 18:22:31 +010075 log.Debugf("PostApfIdServiceApis KongControlPlaneIPv4 %s", ps.KongControlPlaneIPv4)
76 log.Debugf("PostApfIdServiceApis KongDataPlaneIPv4 %s", ps.KongDataPlaneIPv4)
DenisGNoonancbac30a2023-10-24 09:43:52 +010077
78 capifcoreUrl := fmt.Sprintf("%s://%s:%d/published-apis/v1/", ps.CapifProtocol, ps.CapifIPv4, ps.CapifPort)
79 client, err := publishapi.NewClientWithResponses(capifcoreUrl)
80 if err != nil {
81 return err
82 }
83
84 var (
85 ctxHandler context.Context
86 cancel context.CancelFunc
87 )
88 ctxHandler, cancel = context.WithCancel(context.Background())
89 defer cancel()
90
91 newServiceAPIDescription, err := getServiceFromRequest(ctx)
92 if err != nil {
93 return err
94 }
95
96 newServiceAPIDescription.PrepareNewService()
97
DenisGNoonanfdb05902024-04-19 18:22:31 +010098 statusCode, err := newServiceAPIDescription.RegisterKong(
99 ps.KongDomain,
100 ps.KongProtocol,
101 ps.KongControlPlaneIPv4,
102 ps.KongControlPlanePort,
103 ps.KongDataPlaneIPv4,
104 ps.KongDataPlanePort,
105 apfId)
DenisGNoonan6bb64462024-08-06 16:50:42 +0100106
107 log.Trace("After RegisterKong")
108
109 if err != nil {
DenisGNoonancbac30a2023-10-24 09:43:52 +0100110 msg := err.Error()
DenisGNoonan6bb64462024-08-06 16:50:42 +0100111 log.Errorf("PostApfIdServiceApis, error on RegisterKong %s", msg)
112 return sendCoreError(ctx, statusCode, msg)
113 }
114 if statusCode != http.StatusCreated {
115 // We can return with http.StatusForbidden if there is a http.StatusConflict detected by Kong
116 msg := "error detected by Kong"
117 log.Errorf(msg)
DenisGNoonancbac30a2023-10-24 09:43:52 +0100118 return sendCoreError(ctx, statusCode, msg)
119 }
120
121 bodyServiceAPIDescription := publishapi.PostApfIdServiceApisJSONRequestBody(newServiceAPIDescription)
122 var rsp *publishapi.PostApfIdServiceApisResponse
123
124 log.Trace("calling PostApfIdServiceApisWithResponse")
125 rsp, err = client.PostApfIdServiceApisWithResponse(ctxHandler, apfId, bodyServiceAPIDescription)
126
127 if err != nil {
128 msg := err.Error()
129 log.Errorf("error on PostApfIdServiceApisWithResponse %s", msg)
130 return sendCoreError(ctx, http.StatusInternalServerError, msg)
131 }
132
133 if rsp.StatusCode() != http.StatusCreated {
134 msg := string(rsp.Body)
135 log.Debugf("PostApfIdServiceApisWithResponse status code %d", rsp.StatusCode())
136 log.Debugf("PostApfIdServiceApisWithResponse error %s", msg)
137 if rsp.StatusCode() == http.StatusForbidden || rsp.StatusCode() == http.StatusBadRequest {
DenisGNoonanfdb05902024-04-19 18:22:31 +0100138 newServiceAPIDescription.UnregisterKong(ps.KongDomain, ps.KongProtocol, ps.KongControlPlaneIPv4, ps.KongControlPlanePort)
DenisGNoonancbac30a2023-10-24 09:43:52 +0100139 }
140 return sendCoreError(ctx, rsp.StatusCode(), msg)
141 }
142
143 rspServiceAPIDescription := *rsp.JSON201
144 apiId := *rspServiceAPIDescription.ApiId
145
146 uri := ctx.Request().Host + ctx.Request().URL.String()
147 ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, apiId))
148
149 err = ctx.JSON(http.StatusCreated, rspServiceAPIDescription)
150 if err != nil {
151 return err // Tell Echo that our handler failed
152 }
153
154 return nil
155}
156
157
158// Unpublish a published service API.
159func (ps *PublishService) DeleteApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error {
160 log.Tracef("entering DeleteApfIdServiceApisServiceApiId apfId %s serviceApiId %s", apfId, serviceApiId)
161
162 capifcoreUrl := fmt.Sprintf("%s://%s:%d/published-apis/v1/", ps.CapifProtocol, ps.CapifIPv4, ps.CapifPort)
163 client, err := publishapi.NewClientWithResponses(capifcoreUrl)
164 if err != nil {
165 return err
166 }
167
168 var (
169 ctxHandler context.Context
170 cancel context.CancelFunc
171 )
172 ctxHandler, cancel = context.WithCancel(context.Background())
173 defer cancel()
174
175 log.Debugf("call GetApfIdServiceApisServiceApiIdWithResponse before delete apfId %s serviceApiId %s", apfId, serviceApiId)
176 var rsp *publishapi.GetApfIdServiceApisServiceApiIdResponse
177 rsp, err = client.GetApfIdServiceApisServiceApiIdWithResponse(ctxHandler, apfId, serviceApiId)
178
179 if err != nil {
180 msg := err.Error()
181 log.Errorf("error on GetApfIdServiceApisServiceApiIdWithResponse %s", msg)
182 return sendCoreError(ctx, http.StatusInternalServerError, msg)
183 }
184
185 statusCode := rsp.StatusCode()
186 if statusCode != http.StatusOK {
187 log.Debugf("GetApfIdServiceApisServiceApiIdWithResponse status %d", statusCode)
188 return ctx.NoContent(statusCode)
189 }
190
191 rspServiceAPIDescription := *rsp.JSON200
DenisGNoonanfdb05902024-04-19 18:22:31 +0100192
193 statusCode, err = rspServiceAPIDescription.UnregisterKong(ps.KongDomain, ps.KongProtocol, ps.KongControlPlaneIPv4, ps.KongControlPlanePort)
DenisGNoonancbac30a2023-10-24 09:43:52 +0100194 if (err != nil) || (statusCode != http.StatusNoContent) {
195 msg := err.Error()
196 log.Errorf("error on UnregisterKong %s", msg)
197 return sendCoreError(ctx, statusCode, msg)
198 }
199
200 log.Trace("call DeleteApfIdServiceApisServiceApiIdWithResponse")
201 _, err = client.DeleteApfIdServiceApisServiceApiIdWithResponse(ctxHandler, apfId, serviceApiId)
202
203 if err != nil {
204 msg := err.Error()
205 log.Errorf("error on DeleteApfIdServiceApisServiceApiIdWithResponse %s", msg)
206 return sendCoreError(ctx, http.StatusInternalServerError, msg)
207 }
208
209 return ctx.NoContent(http.StatusNoContent)
210}
211
212// Retrieve all published APIs.
213func (ps *PublishService) GetApfIdServiceApis(ctx echo.Context, apfId string) error {
214 log.Tracef("entering GetApfIdServiceApis apfId %s", apfId)
215
216 capifcoreUrl := fmt.Sprintf("%s://%s:%d/published-apis/v1/", ps.CapifProtocol, ps.CapifIPv4, ps.CapifPort)
217 client, err := publishapi.NewClientWithResponses(capifcoreUrl)
218 if err != nil {
219 return err
220 }
221
222 var (
223 ctxHandler context.Context
224 cancel context.CancelFunc
225 )
226 ctxHandler, cancel = context.WithCancel(context.Background())
227 defer cancel()
228
229 var rsp *publishapi.GetApfIdServiceApisResponse
230 rsp, err = client.GetApfIdServiceApisWithResponse(ctxHandler, apfId)
231
232 if err != nil {
233 msg := err.Error()
234 log.Errorf("error on GetApfIdServiceApisWithResponse %s", msg)
235 return sendCoreError(ctx, http.StatusInternalServerError, msg)
236 }
237
238 if rsp.StatusCode() != http.StatusOK {
239 msg := string(rsp.Body)
240 log.Errorf("GetApfIdServiceApisWithResponse status %d", rsp.StatusCode())
241 log.Errorf("GetApfIdServiceApisWithResponse error %s", msg)
242 return sendCoreError(ctx, rsp.StatusCode(), msg)
243 }
244
245 rspServiceAPIDescriptions := *rsp.JSON200
246 err = ctx.JSON(rsp.StatusCode(), rspServiceAPIDescriptions)
247 if err != nil {
248 return err // tell Echo that our handler failed
249 }
250 return nil
251}
252
253// Retrieve a published service API.
254func (ps *PublishService) GetApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error {
255 log.Tracef("entering GetApfIdServiceApisServiceApiId apfId %s", apfId)
256
257 capifcoreUrl := fmt.Sprintf("%s://%s:%d/published-apis/v1/", ps.CapifProtocol, ps.CapifIPv4, ps.CapifPort)
258 client, err := publishapi.NewClientWithResponses(capifcoreUrl)
259 if err != nil {
260 return err
261 }
262
263 var (
264 ctxHandler context.Context
265 cancel context.CancelFunc
266 )
267 ctxHandler, cancel = context.WithCancel(context.Background())
268 defer cancel()
269
270 var rsp *publishapi.GetApfIdServiceApisServiceApiIdResponse
271 rsp, err = client.GetApfIdServiceApisServiceApiIdWithResponse(ctxHandler, apfId, serviceApiId)
272
273 if err != nil {
274 msg := err.Error()
275 log.Errorf("error on GetApfIdServiceApisServiceApiIdWithResponse %s", msg)
276 return sendCoreError(ctx, http.StatusInternalServerError, msg)
277 }
278
279 statusCode := rsp.StatusCode()
280 if statusCode != http.StatusOK {
281 return ctx.NoContent(statusCode)
282 }
283
284 rspServiceAPIDescription := *rsp.JSON200
285
286 err = ctx.JSON(http.StatusOK, rspServiceAPIDescription)
287 if err != nil {
288 return err // tell Echo that our handler failed
289 }
290 return nil
291}
292
293// Modify an existing published service API.
294func (ps *PublishService) ModifyIndAPFPubAPI(ctx echo.Context, apfId string, serviceApiId string) error {
295 return ctx.NoContent(http.StatusNotImplemented)
296}
297
298// Update a published service API.
299func (ps *PublishService) PutApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error {
300 log.Tracef("entering PutApfIdServiceApisServiceApiId apfId %s", apfId)
301
302 capifcoreUrl := fmt.Sprintf("%s://%s:%d/published-apis/v1/", ps.CapifProtocol, ps.CapifIPv4, ps.CapifPort)
303 client, err := publishapi.NewClientWithResponses(capifcoreUrl)
304 if err != nil {
305 return err
306 }
307
308 var (
309 ctxHandler context.Context
310 cancel context.CancelFunc
311 )
312 ctxHandler, cancel = context.WithCancel(context.Background())
313 defer cancel()
314
315 updatedServiceDescription, err := getServiceFromRequest(ctx)
316 if err != nil {
317 return err
318 }
319
320 var rsp *publishapi.PutApfIdServiceApisServiceApiIdResponse
321 bodyServiceAPIDescription := publishapi.PutApfIdServiceApisServiceApiIdJSONRequestBody(updatedServiceDescription)
322
323 rsp, err = client.PutApfIdServiceApisServiceApiIdWithResponse(ctxHandler, apfId, serviceApiId, bodyServiceAPIDescription)
324
325 if err != nil {
326 msg := err.Error()
327 log.Errorf("error on PutApfIdServiceApisServiceApiIdWithResponse %s", msg)
328 return sendCoreError(ctx, http.StatusInternalServerError, msg)
329 }
330
331 if rsp.StatusCode() != http.StatusOK {
332 log.Errorf("PutApfIdServiceApisServiceApiIdWithResponse status code %d", rsp.StatusCode())
333 if rsp.StatusCode() == http.StatusBadRequest {
DenisGNoonanfdb05902024-04-19 18:22:31 +0100334 updatedServiceDescription.UnregisterKong(ps.KongDomain, ps.KongProtocol,ps.KongControlPlaneIPv4, ps.KongControlPlanePort)
DenisGNoonancbac30a2023-10-24 09:43:52 +0100335 }
336 msg := string(rsp.Body)
337 return sendCoreError(ctx, rsp.StatusCode(), msg)
338 }
339
340 rspServiceAPIDescription := *rsp.JSON200
341 apiId := *rspServiceAPIDescription.ApiId
342
343 uri := ctx.Request().Host + ctx.Request().URL.String()
344 ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, apiId))
345
346 err = ctx.JSON(http.StatusOK, rspServiceAPIDescription)
347 if err != nil {
348 return err // Tell Echo that our handler failed
349 }
350 return nil
351}
352
353
354func getServiceFromRequest(ctx echo.Context) (publishapi.ServiceAPIDescription, error) {
355 var updatedServiceDescription publishapi.ServiceAPIDescription
356 err := ctx.Bind(&updatedServiceDescription)
357 if err != nil {
358 return publishapi.ServiceAPIDescription{}, fmt.Errorf("invalid format for service")
359 }
360 return updatedServiceDescription, nil
361}
362
363// This function wraps sending of an error in the Error format, and
364// handling the failure to marshal that.
365func sendCoreError(ctx echo.Context, code int, message string) error {
366 pd := common29122.ProblemDetails{
367 Cause: &message,
368 Status: &code,
369 }
370 err := ctx.JSON(code, pd)
371 return err
lapentafd12bf42c2024-10-01 12:02:38 +0100372}