blob: 40318278e3fd9ac2f5109a3232d5c3537f9aa26c [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)
DenisGNoonancbac30a2023-10-24 09:43:52 +0100106 if (err != nil) || (statusCode != http.StatusCreated) {
107 // We can return with http.StatusForbidden if there is a http.StatusConflict detected by Kong
108 msg := err.Error()
109 log.Errorf("error on RegisterKong %s", msg)
110 return sendCoreError(ctx, statusCode, msg)
111 }
112
113 bodyServiceAPIDescription := publishapi.PostApfIdServiceApisJSONRequestBody(newServiceAPIDescription)
114 var rsp *publishapi.PostApfIdServiceApisResponse
115
116 log.Trace("calling PostApfIdServiceApisWithResponse")
117 rsp, err = client.PostApfIdServiceApisWithResponse(ctxHandler, apfId, bodyServiceAPIDescription)
118
119 if err != nil {
120 msg := err.Error()
121 log.Errorf("error on PostApfIdServiceApisWithResponse %s", msg)
122 return sendCoreError(ctx, http.StatusInternalServerError, msg)
123 }
124
125 if rsp.StatusCode() != http.StatusCreated {
126 msg := string(rsp.Body)
127 log.Debugf("PostApfIdServiceApisWithResponse status code %d", rsp.StatusCode())
128 log.Debugf("PostApfIdServiceApisWithResponse error %s", msg)
129 if rsp.StatusCode() == http.StatusForbidden || rsp.StatusCode() == http.StatusBadRequest {
DenisGNoonanfdb05902024-04-19 18:22:31 +0100130 newServiceAPIDescription.UnregisterKong(ps.KongDomain, ps.KongProtocol, ps.KongControlPlaneIPv4, ps.KongControlPlanePort)
DenisGNoonancbac30a2023-10-24 09:43:52 +0100131 }
132 return sendCoreError(ctx, rsp.StatusCode(), msg)
133 }
134
135 rspServiceAPIDescription := *rsp.JSON201
136 apiId := *rspServiceAPIDescription.ApiId
137
138 uri := ctx.Request().Host + ctx.Request().URL.String()
139 ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, apiId))
140
141 err = ctx.JSON(http.StatusCreated, rspServiceAPIDescription)
142 if err != nil {
143 return err // Tell Echo that our handler failed
144 }
145
146 return nil
147}
148
149
150// Unpublish a published service API.
151func (ps *PublishService) DeleteApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error {
152 log.Tracef("entering DeleteApfIdServiceApisServiceApiId apfId %s serviceApiId %s", apfId, serviceApiId)
153
154 capifcoreUrl := fmt.Sprintf("%s://%s:%d/published-apis/v1/", ps.CapifProtocol, ps.CapifIPv4, ps.CapifPort)
155 client, err := publishapi.NewClientWithResponses(capifcoreUrl)
156 if err != nil {
157 return err
158 }
159
160 var (
161 ctxHandler context.Context
162 cancel context.CancelFunc
163 )
164 ctxHandler, cancel = context.WithCancel(context.Background())
165 defer cancel()
166
167 log.Debugf("call GetApfIdServiceApisServiceApiIdWithResponse before delete apfId %s serviceApiId %s", apfId, serviceApiId)
168 var rsp *publishapi.GetApfIdServiceApisServiceApiIdResponse
169 rsp, err = client.GetApfIdServiceApisServiceApiIdWithResponse(ctxHandler, apfId, serviceApiId)
170
171 if err != nil {
172 msg := err.Error()
173 log.Errorf("error on GetApfIdServiceApisServiceApiIdWithResponse %s", msg)
174 return sendCoreError(ctx, http.StatusInternalServerError, msg)
175 }
176
177 statusCode := rsp.StatusCode()
178 if statusCode != http.StatusOK {
179 log.Debugf("GetApfIdServiceApisServiceApiIdWithResponse status %d", statusCode)
180 return ctx.NoContent(statusCode)
181 }
182
183 rspServiceAPIDescription := *rsp.JSON200
DenisGNoonanfdb05902024-04-19 18:22:31 +0100184
185 statusCode, err = rspServiceAPIDescription.UnregisterKong(ps.KongDomain, ps.KongProtocol, ps.KongControlPlaneIPv4, ps.KongControlPlanePort)
DenisGNoonancbac30a2023-10-24 09:43:52 +0100186 if (err != nil) || (statusCode != http.StatusNoContent) {
187 msg := err.Error()
188 log.Errorf("error on UnregisterKong %s", msg)
189 return sendCoreError(ctx, statusCode, msg)
190 }
191
192 log.Trace("call DeleteApfIdServiceApisServiceApiIdWithResponse")
193 _, err = client.DeleteApfIdServiceApisServiceApiIdWithResponse(ctxHandler, apfId, serviceApiId)
194
195 if err != nil {
196 msg := err.Error()
197 log.Errorf("error on DeleteApfIdServiceApisServiceApiIdWithResponse %s", msg)
198 return sendCoreError(ctx, http.StatusInternalServerError, msg)
199 }
200
201 return ctx.NoContent(http.StatusNoContent)
202}
203
204// Retrieve all published APIs.
205func (ps *PublishService) GetApfIdServiceApis(ctx echo.Context, apfId string) error {
206 log.Tracef("entering GetApfIdServiceApis apfId %s", apfId)
207
208 capifcoreUrl := fmt.Sprintf("%s://%s:%d/published-apis/v1/", ps.CapifProtocol, ps.CapifIPv4, ps.CapifPort)
209 client, err := publishapi.NewClientWithResponses(capifcoreUrl)
210 if err != nil {
211 return err
212 }
213
214 var (
215 ctxHandler context.Context
216 cancel context.CancelFunc
217 )
218 ctxHandler, cancel = context.WithCancel(context.Background())
219 defer cancel()
220
221 var rsp *publishapi.GetApfIdServiceApisResponse
222 rsp, err = client.GetApfIdServiceApisWithResponse(ctxHandler, apfId)
223
224 if err != nil {
225 msg := err.Error()
226 log.Errorf("error on GetApfIdServiceApisWithResponse %s", msg)
227 return sendCoreError(ctx, http.StatusInternalServerError, msg)
228 }
229
230 if rsp.StatusCode() != http.StatusOK {
231 msg := string(rsp.Body)
232 log.Errorf("GetApfIdServiceApisWithResponse status %d", rsp.StatusCode())
233 log.Errorf("GetApfIdServiceApisWithResponse error %s", msg)
234 return sendCoreError(ctx, rsp.StatusCode(), msg)
235 }
236
237 rspServiceAPIDescriptions := *rsp.JSON200
238 err = ctx.JSON(rsp.StatusCode(), rspServiceAPIDescriptions)
239 if err != nil {
240 return err // tell Echo that our handler failed
241 }
242 return nil
243}
244
245// Retrieve a published service API.
246func (ps *PublishService) GetApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error {
247 log.Tracef("entering GetApfIdServiceApisServiceApiId apfId %s", apfId)
248
249 capifcoreUrl := fmt.Sprintf("%s://%s:%d/published-apis/v1/", ps.CapifProtocol, ps.CapifIPv4, ps.CapifPort)
250 client, err := publishapi.NewClientWithResponses(capifcoreUrl)
251 if err != nil {
252 return err
253 }
254
255 var (
256 ctxHandler context.Context
257 cancel context.CancelFunc
258 )
259 ctxHandler, cancel = context.WithCancel(context.Background())
260 defer cancel()
261
262 var rsp *publishapi.GetApfIdServiceApisServiceApiIdResponse
263 rsp, err = client.GetApfIdServiceApisServiceApiIdWithResponse(ctxHandler, apfId, serviceApiId)
264
265 if err != nil {
266 msg := err.Error()
267 log.Errorf("error on GetApfIdServiceApisServiceApiIdWithResponse %s", msg)
268 return sendCoreError(ctx, http.StatusInternalServerError, msg)
269 }
270
271 statusCode := rsp.StatusCode()
272 if statusCode != http.StatusOK {
273 return ctx.NoContent(statusCode)
274 }
275
276 rspServiceAPIDescription := *rsp.JSON200
277
278 err = ctx.JSON(http.StatusOK, rspServiceAPIDescription)
279 if err != nil {
280 return err // tell Echo that our handler failed
281 }
282 return nil
283}
284
285// Modify an existing published service API.
286func (ps *PublishService) ModifyIndAPFPubAPI(ctx echo.Context, apfId string, serviceApiId string) error {
287 return ctx.NoContent(http.StatusNotImplemented)
288}
289
290// Update a published service API.
291func (ps *PublishService) PutApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error {
292 log.Tracef("entering PutApfIdServiceApisServiceApiId apfId %s", apfId)
293
294 capifcoreUrl := fmt.Sprintf("%s://%s:%d/published-apis/v1/", ps.CapifProtocol, ps.CapifIPv4, ps.CapifPort)
295 client, err := publishapi.NewClientWithResponses(capifcoreUrl)
296 if err != nil {
297 return err
298 }
299
300 var (
301 ctxHandler context.Context
302 cancel context.CancelFunc
303 )
304 ctxHandler, cancel = context.WithCancel(context.Background())
305 defer cancel()
306
307 updatedServiceDescription, err := getServiceFromRequest(ctx)
308 if err != nil {
309 return err
310 }
311
312 var rsp *publishapi.PutApfIdServiceApisServiceApiIdResponse
313 bodyServiceAPIDescription := publishapi.PutApfIdServiceApisServiceApiIdJSONRequestBody(updatedServiceDescription)
314
315 rsp, err = client.PutApfIdServiceApisServiceApiIdWithResponse(ctxHandler, apfId, serviceApiId, bodyServiceAPIDescription)
316
317 if err != nil {
318 msg := err.Error()
319 log.Errorf("error on PutApfIdServiceApisServiceApiIdWithResponse %s", msg)
320 return sendCoreError(ctx, http.StatusInternalServerError, msg)
321 }
322
323 if rsp.StatusCode() != http.StatusOK {
324 log.Errorf("PutApfIdServiceApisServiceApiIdWithResponse status code %d", rsp.StatusCode())
325 if rsp.StatusCode() == http.StatusBadRequest {
DenisGNoonanfdb05902024-04-19 18:22:31 +0100326 updatedServiceDescription.UnregisterKong(ps.KongDomain, ps.KongProtocol,ps.KongControlPlaneIPv4, ps.KongControlPlanePort)
DenisGNoonancbac30a2023-10-24 09:43:52 +0100327 }
328 msg := string(rsp.Body)
329 return sendCoreError(ctx, rsp.StatusCode(), msg)
330 }
331
332 rspServiceAPIDescription := *rsp.JSON200
333 apiId := *rspServiceAPIDescription.ApiId
334
335 uri := ctx.Request().Host + ctx.Request().URL.String()
336 ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, apiId))
337
338 err = ctx.JSON(http.StatusOK, rspServiceAPIDescription)
339 if err != nil {
340 return err // Tell Echo that our handler failed
341 }
342 return nil
343}
344
345
346func getServiceFromRequest(ctx echo.Context) (publishapi.ServiceAPIDescription, error) {
347 var updatedServiceDescription publishapi.ServiceAPIDescription
348 err := ctx.Bind(&updatedServiceDescription)
349 if err != nil {
350 return publishapi.ServiceAPIDescription{}, fmt.Errorf("invalid format for service")
351 }
352 return updatedServiceDescription, nil
353}
354
355// This function wraps sending of an error in the Error format, and
356// handling the failure to marshal that.
357func sendCoreError(ctx echo.Context, code int, message string) error {
358 pd := common29122.ProblemDetails{
359 Cause: &message,
360 Status: &code,
361 }
362 err := ctx.JSON(code, pd)
363 return err
364}