X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;f=capifcore%2Finternal%2Fpublishservice%2Fpublishservice.go;h=2314039aeacbf5eb42c70471f1daa29ffa3b5238;hb=0b4c4ecb52b1c04037a65644dc8c6c29981d9736;hp=43cdb8d76ef6c4c62832abb89c4e6a4e8dd2c0bb;hpb=eeef4f02de198d1c10c2775010597d783e9cb64a;p=nonrtric%2Fplt%2Fsme.git diff --git a/capifcore/internal/publishservice/publishservice.go b/capifcore/internal/publishservice/publishservice.go index 43cdb8d..2314039 100644 --- a/capifcore/internal/publishservice/publishservice.go +++ b/capifcore/internal/publishservice/publishservice.go @@ -21,15 +21,17 @@ package publishservice import ( + "fmt" "net/http" "path" "strings" "sync" "github.com/labstack/echo/v4" + "k8s.io/utils/strings/slices" "oransc.org/nonrtric/capifcore/internal/common29122" - "oransc.org/nonrtric/capifcore/internal/publishserviceapi" + publishapi "oransc.org/nonrtric/capifcore/internal/publishserviceapi" "oransc.org/nonrtric/capifcore/internal/helmmanagement" "oransc.org/nonrtric/capifcore/internal/providermanagement" @@ -37,131 +39,151 @@ import ( log "github.com/sirupsen/logrus" ) -//go:generate mockery --name APIRegister -type APIRegister interface { - AreAPIsRegistered(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool - GetAPIs() *[]publishserviceapi.ServiceAPIDescription - IsAPIRegistered(aefId, path string) bool +//go:generate mockery --name PublishRegister +type PublishRegister interface { + // Checks if the provided APIs are published. + // Returns true if all provided APIs have been published, false otherwise. + AreAPIsPublished(serviceDescriptions *[]publishapi.ServiceAPIDescription) bool + // Checks if the provided API is published. + // Returns true if the provided API has been published, false otherwise. + IsAPIPublished(aefId, path string) bool + // Gets all published APIs. + // Returns a list of all APIs that has been published. + GetAllPublishedServices() []publishapi.ServiceAPIDescription } type PublishService struct { - publishedServices map[string]publishserviceapi.ServiceAPIDescription + publishedServices map[string][]publishapi.ServiceAPIDescription serviceRegister providermanagement.ServiceRegister helmManager helmmanagement.HelmManager lock sync.Mutex } +// Creates a service that implements both the PublishRegister and the publishserviceapi.ServerInterface interfaces. func NewPublishService(serviceRegister providermanagement.ServiceRegister, hm helmmanagement.HelmManager) *PublishService { return &PublishService{ helmManager: hm, - publishedServices: make(map[string]publishserviceapi.ServiceAPIDescription), + publishedServices: make(map[string][]publishapi.ServiceAPIDescription), serviceRegister: serviceRegister, } } -func (ps *PublishService) AreAPIsRegistered(serviceDescriptions *[]publishserviceapi.ServiceAPIDescription) bool { +func (ps *PublishService) AreAPIsPublished(serviceDescriptions *[]publishapi.ServiceAPIDescription) bool { + + if serviceDescriptions != nil { + registeredApis := ps.getAllAefIds() + return checkNewDescriptions(*serviceDescriptions, registeredApis) + } + return true +} + +func (ps *PublishService) getAllAefIds() []string { ps.lock.Lock() defer ps.lock.Unlock() - allRegistered := true - if serviceDescriptions != nil { - out: - for _, newApi := range *serviceDescriptions { - registeredApi, ok := ps.publishedServices[*newApi.ApiId] - if ok { - if !ps.areProfilesRegistered(newApi.AefProfiles, registeredApi.AefProfiles) { - allRegistered = false - break out - } - } else { - allRegistered = false - break out - } + allIds := []string{} + for _, descriptions := range ps.publishedServices { + for _, description := range descriptions { + allIds = append(allIds, getIdsFromDescription(description)...) } } - return allRegistered + return allIds +} + +func getIdsFromDescription(description publishapi.ServiceAPIDescription) []string { + allIds := []string{} + if description.AefProfiles != nil { + for _, aefProfile := range *description.AefProfiles { + allIds = append(allIds, aefProfile.AefId) + } + } + return allIds } -func (ps *PublishService) areProfilesRegistered(newProfiles *[]publishserviceapi.AefProfile, registeredProfiles *[]publishserviceapi.AefProfile) bool { +func checkNewDescriptions(newDescriptions []publishapi.ServiceAPIDescription, registeredAefIds []string) bool { + registered := true + for _, newApi := range newDescriptions { + if !checkProfiles(newApi.AefProfiles, registeredAefIds) { + registered = false + break + } + } + return registered +} + +func checkProfiles(newProfiles *[]publishapi.AefProfile, registeredAefIds []string) bool { allRegistered := true - if newProfiles != nil && registeredProfiles != nil { - out: - for _, newProfile := range *newProfiles { - for _, registeredProfile := range *registeredProfiles { - if newProfile.AefId == registeredProfile.AefId { - break - } + if newProfiles != nil { + for _, profile := range *newProfiles { + if !slices.Contains(registeredAefIds, profile.AefId) { allRegistered = false - break out + break } } - } else if registeredProfiles == nil { - allRegistered = false } return allRegistered } -func (ps *PublishService) GetAPIs() *[]publishserviceapi.ServiceAPIDescription { - ps.lock.Lock() - defer ps.lock.Unlock() +func (ps *PublishService) IsAPIPublished(aefId, path string) bool { + return slices.Contains(ps.getAllAefIds(), aefId) +} - apis := []publishserviceapi.ServiceAPIDescription{} - for _, service := range ps.publishedServices { - apis = append(apis, service) +func (ps *PublishService) GetAllPublishedServices() []publishapi.ServiceAPIDescription { + publishedDescriptions := []publishapi.ServiceAPIDescription{} + for _, descriptions := range ps.publishedServices { + publishedDescriptions = append(publishedDescriptions, descriptions...) } - return &apis + return publishedDescriptions } -func (ps *PublishService) IsAPIRegistered(aefId, path string) bool { - ps.lock.Lock() - defer ps.lock.Unlock() - - registered := false -out: - for _, service := range ps.publishedServices { - if service.ApiName == path { - for _, profile := range *service.AefProfiles { - if profile.AefId == aefId { - registered = true - break out - } - } +// Retrieve all published APIs. +func (ps *PublishService) GetApfIdServiceApis(ctx echo.Context, apfId string) error { + serviceDescriptions, ok := ps.publishedServices[apfId] + if ok { + err := ctx.JSON(http.StatusOK, serviceDescriptions) + if err != nil { + // Something really bad happened, tell Echo that our handler failed + return err } + } else { + return sendCoreError(ctx, http.StatusNotFound, fmt.Sprintf("Provider %s not registered", apfId)) } - return registered -} -func (ps *PublishService) GetApfIdServiceApis(ctx echo.Context, apfId string) error { - return ctx.NoContent(http.StatusNotImplemented) + return nil } +// Publish a new API. func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) error { - var newServiceAPIDescription publishserviceapi.ServiceAPIDescription + var newServiceAPIDescription publishapi.ServiceAPIDescription err := ctx.Bind(&newServiceAPIDescription) if err != nil { - return sendCoreError(ctx, http.StatusBadRequest, "Invalid format for service") + return sendCoreError(ctx, http.StatusBadRequest, "Invalid format for service "+apfId) } ps.lock.Lock() defer ps.lock.Unlock() + registeredFuncs := ps.serviceRegister.GetAefsForPublisher(apfId) for _, profile := range *newServiceAPIDescription.AefProfiles { - if !ps.serviceRegister.IsFunctionRegistered(profile.AefId) { - return sendCoreError(ctx, http.StatusNotFound, "Function not registered, "+profile.AefId) + if !slices.Contains(registeredFuncs, profile.AefId) { + return sendCoreError(ctx, http.StatusNotFound, fmt.Sprintf("Function %s not registered", profile.AefId)) } } newId := "api_id_" + newServiceAPIDescription.ApiName newServiceAPIDescription.ApiId = &newId - info := strings.Split(*newServiceAPIDescription.Description, ",") - if len(info) == 5 { - err = ps.helmManager.InstallHelmChart(info[1], info[2], info[3], info[4]) - if err != nil { - return sendCoreError(ctx, http.StatusBadRequest, "Unable to install Helm chart due to: "+err.Error()) - } - log.Info("Installed service: ", newId) + + shouldReturn, returnValue := ps.installHelmChart(newServiceAPIDescription, ctx) + if shouldReturn { + return returnValue + } + + _, ok := ps.publishedServices[apfId] + if ok { + ps.publishedServices[apfId] = append(ps.publishedServices[apfId], newServiceAPIDescription) + } else { + ps.publishedServices[apfId] = append([]publishapi.ServiceAPIDescription{}, newServiceAPIDescription) } - ps.publishedServices[*newServiceAPIDescription.ApiId] = newServiceAPIDescription uri := ctx.Request().Host + ctx.Request().URL.String() ctx.Response().Header().Set(echo.HeaderLocation, ctx.Scheme()+`://`+path.Join(uri, *newServiceAPIDescription.ApiId)) @@ -174,24 +196,48 @@ func (ps *PublishService) PostApfIdServiceApis(ctx echo.Context, apfId string) e return nil } +func (ps *PublishService) installHelmChart(newServiceAPIDescription publishapi.ServiceAPIDescription, ctx echo.Context) (bool, error) { + info := strings.Split(*newServiceAPIDescription.Description, ",") + if len(info) == 5 { + err := ps.helmManager.InstallHelmChart(info[1], info[2], info[3], info[4]) + if err != nil { + return true, sendCoreError(ctx, http.StatusBadRequest, fmt.Sprintf("Unable to install Helm chart %s due to: %s", info[3], err.Error())) + } + log.Debug("Installed service: ", newServiceAPIDescription.ApiId) + } + return false, nil +} + +// Unpublish a published service API. func (ps *PublishService) DeleteApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error { - serviceDescription, ok := ps.publishedServices[string(serviceApiId)] + serviceDescriptions, ok := ps.publishedServices[string(apfId)] if ok { - info := strings.Split(*serviceDescription.Description, ",") - if len(info) == 5 { - ps.helmManager.UninstallHelmChart(info[1], info[3]) - log.Info("Deleted service: ", serviceApiId) + pos, description := getServiceDescription(serviceApiId, serviceDescriptions) + if description != nil { + info := strings.Split(*description.Description, ",") + if len(info) == 5 { + ps.helmManager.UninstallHelmChart(info[1], info[3]) + log.Debug("Deleted service: ", serviceApiId) + } + ps.lock.Lock() + defer ps.lock.Unlock() + ps.publishedServices[string(apfId)] = removeServiceDescription(pos, serviceDescriptions) } - ps.lock.Lock() - defer ps.lock.Unlock() - delete(ps.publishedServices, string(serviceApiId)) } return ctx.NoContent(http.StatusNoContent) } +// Retrieve a published service API. func (ps *PublishService) GetApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error { - serviceDescription, ok := ps.publishedServices[string(serviceApiId)] + ps.lock.Lock() + defer ps.lock.Unlock() + + serviceDescriptions, ok := ps.publishedServices[apfId] if ok { + _, serviceDescription := getServiceDescription(serviceApiId, serviceDescriptions) + if serviceDescription == nil { + return ctx.NoContent(http.StatusNotFound) + } err := ctx.JSON(http.StatusOK, serviceDescription) if err != nil { // Something really bad happened, tell Echo that our handler failed @@ -203,10 +249,28 @@ func (ps *PublishService) GetApfIdServiceApisServiceApiId(ctx echo.Context, apfI return ctx.NoContent(http.StatusNotFound) } +func getServiceDescription(serviceApiId string, descriptions []publishapi.ServiceAPIDescription) (int, *publishapi.ServiceAPIDescription) { + for pos, description := range descriptions { + if serviceApiId == *description.ApiId { + return pos, &description + } + } + return -1, nil +} + +func removeServiceDescription(i int, a []publishapi.ServiceAPIDescription) []publishapi.ServiceAPIDescription { + a[i] = a[len(a)-1] // Copy last element to index i. + a[len(a)-1] = publishapi.ServiceAPIDescription{} // Erase last element (write zero value). + a = a[:len(a)-1] // Truncate slice. + return a +} + +// Modify an existing published service API. func (ps *PublishService) ModifyIndAPFPubAPI(ctx echo.Context, apfId string, serviceApiId string) error { return ctx.NoContent(http.StatusNotImplemented) } +// Update a published service API. func (ps *PublishService) PutApfIdServiceApisServiceApiId(ctx echo.Context, apfId string, serviceApiId string) error { return ctx.NoContent(http.StatusNotImplemented) }