Skip to content

Commit

Permalink
Merge pull request #24 from yccodr/refactor/server
Browse files Browse the repository at this point in the history
refactor: NSSF app, sbi, processor, consumer
  • Loading branch information
ianchen0119 authored Jun 27, 2024
2 parents 574d044 + 201cb73 commit a73f10f
Show file tree
Hide file tree
Showing 33 changed files with 1,371 additions and 1,234 deletions.
16 changes: 14 additions & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package main

import (
"context"
"os"
"os/signal"
"path/filepath"
"runtime/debug"
"syscall"

"github.com/urfave/cli"

Expand Down Expand Up @@ -56,13 +59,22 @@ func action(cliCtx *cli.Context) error {
}
factory.NssfConfig = cfg

nssf, err := service.NewApp(cfg)
ctx, cancel := context.WithCancel(context.Background())
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM)

go func() {
<-sigCh // Wait for interrupt signal to gracefully shutdown
cancel() // Notify each goroutine and wait them stopped
}()

nssf, err := service.NewApp(ctx, cfg, tlsKeyLogPath)
if err != nil {
return err
}
NSSF = nssf

nssf.Start(tlsKeyLogPath)
nssf.Start()

return nil
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.8.1
github.com/urfave/cli v1.22.5
go.uber.org/mock v0.4.0
gopkg.in/yaml.v2 v2.4.0
)

Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
Expand Down
8 changes: 6 additions & 2 deletions internal/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"github.com/free5gc/openapi/oauth"
)

const NRF_PORT = 29510

var nssfContext = NSSFContext{}

// Initialize NSSF context with default value
Expand All @@ -40,7 +42,7 @@ func Init() {
}
nssfContext.NfService = initNfService(serviceName, "1.0.0")

nssfContext.NrfUri = fmt.Sprintf("%s://%s:%d", models.UriScheme_HTTPS, nssfContext.RegisterIPv4, 29510)
nssfContext.NrfUri = fmt.Sprintf("%s://%s:%d", models.UriScheme_HTTPS, nssfContext.RegisterIPv4, NRF_PORT)
}

type NFContext interface {
Expand Down Expand Up @@ -71,6 +73,8 @@ func InitNssfContext() {
nssfContext.Name = nssfConfig.Configuration.NssfName
}

nssfContext.NfId = uuid.New().String()
nssfContext.Name = "NSSF"
nssfContext.UriScheme = nssfConfig.Configuration.Sbi.Scheme
nssfContext.RegisterIPv4 = nssfConfig.Configuration.Sbi.RegisterIPv4
nssfContext.SBIPort = nssfConfig.Configuration.Sbi.Port
Expand All @@ -91,7 +95,7 @@ func InitNssfContext() {
nssfContext.NrfUri = nssfConfig.Configuration.NrfUri
} else {
logger.InitLog.Warn("NRF Uri is empty! Using localhost as NRF IPv4 address.")
nssfContext.NrfUri = fmt.Sprintf("%s://%s:%d", nssfContext.UriScheme, "127.0.0.1", 29510)
nssfContext.NrfUri = fmt.Sprintf("%s://%s:%d", nssfContext.UriScheme, "127.0.0.1", NRF_PORT)
}
nssfContext.NrfCertPem = nssfConfig.Configuration.NrfCertPem
nssfContext.SupportedPlmnList = nssfConfig.Configuration.SupportedPlmnList
Expand Down
235 changes: 235 additions & 0 deletions internal/sbi/api_nssaiavailability.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
package sbi

import (
"net/http"
"strings"

"github.com/gin-gonic/gin"

"github.com/free5gc/nssf/internal/logger"
"github.com/free5gc/nssf/internal/plugin"
"github.com/free5gc/nssf/internal/util"
"github.com/free5gc/openapi"
"github.com/free5gc/openapi/models"
)

func (s *Server) getNssaiAvailabilityRoutes() []Route {
return []Route{
{
"Health Check",
strings.ToUpper("Get"),
"/",
func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{"status": "Service Available"})
},
},

{
"NSSAIAvailabilityDelete",
strings.ToUpper("Delete"),
"/nssai-availability/:nfId",
s.NSSAIAvailabilityDelete,
},

{
"NSSAIAvailabilityPatch",
strings.ToUpper("Patch"),
"/nssai-availability/:nfId",
s.NSSAIAvailabilityPatch,
},

{
"NSSAIAvailabilityPut",
strings.ToUpper("Put"),
"/nssai-availability/:nfId",
s.NSSAIAvailabilityPut,
},

// Regular expressions for route matching should be unique in Gin package
// 'subscriptions' would conflict with existing wildcard ':nfId'
// Simply replace 'subscriptions' with ':nfId' and check if ':nfId' is 'subscriptions' in handler function
{
"NSSAIAvailabilityUnsubscribe",
strings.ToUpper("Delete"),
// "/nssai-availability/subscriptions/:subscriptionId",
"/nssai-availability/:nfId/:subscriptionId",
s.NSSAIAvailabilityUnsubscribeDelete,
},

{
"NSSAIAvailabilityPost",
strings.ToUpper("Post"),
"/nssai-availability/subscriptions",
s.NSSAIAvailabilityPost,
},
}
}

// NSSAIAvailabilityDelete - Deletes an already existing S-NSSAIs per TA
// provided by the NF service consumer (e.g AMF)
func (s *Server) NSSAIAvailabilityDelete(c *gin.Context) {
logger.NssaiavailLog.Infof("Handle NSSAIAvailabilityDelete")

nfId := c.Params.ByName("nfId")

if nfId == "" {
problemDetails := &models.ProblemDetails{
Status: http.StatusBadRequest,
Cause: "UNSPECIFIED", // TODO: Check if this is the correct cause
}

util.GinProblemJson(c, problemDetails)
return
}

s.Processor().NssaiAvailabilityNfInstanceDelete(c, nfId)
}

// NSSAIAvailabilityPatch - Updates an already existing S-NSSAIs per TA
// provided by the NF service consumer (e.g AMF)
func (s *Server) NSSAIAvailabilityPatch(c *gin.Context) {
logger.NssaiavailLog.Infof("Handle NSSAIAvailabilityPatch")

nfId := c.Params.ByName("nfId")

if nfId == "" {
problemDetails := &models.ProblemDetails{
Status: http.StatusBadRequest,
Cause: "UNSPECIFIED", // TODO: Check if this is the correct cause
}

util.GinProblemJson(c, problemDetails)
return
}

var patchDocument plugin.PatchDocument

requestBody, err := c.GetRawData()
if err != nil {
problemDetails := &models.ProblemDetails{
Status: http.StatusInternalServerError,
Cause: "SYSTEM_FAILURE",
}

util.GinProblemJson(c, problemDetails)
return
}

if err = openapi.Deserialize(&patchDocument, requestBody, "application/json"); err != nil {
problemDetails := &models.ProblemDetails{
Status: http.StatusBadRequest,
Cause: "UNSPECIFIED", // TODO: Check if this is the correct cause
}

logger.SBILog.Errorf("Error deserializing patch document: %+v", err)
util.GinProblemJson(c, problemDetails)
return
}

// TODO: Request NfProfile of NfId from NRF
// Check if NfId is valid AMF and obtain AMF Set ID
// If NfId is invalid, return ProblemDetails with code 404 Not Found
// If NF consumer is not authorized to update NSSAI availability, return ProblemDetails with code 403 Forbidden

s.Processor().NssaiAvailabilityNfInstancePatch(c, patchDocument, nfId)
}

// NSSAIAvailabilityPut - Updates/replaces the NSSF
// with the S-NSSAIs the NF service consumer (e.g AMF) supports per TA
func (s *Server) NSSAIAvailabilityPut(c *gin.Context) {
logger.NssaiavailLog.Infof("Handle NSSAIAvailabilityPut")

nfId := c.Params.ByName("nfId")

if nfId == "" {
problemDetails := &models.ProblemDetails{
Status: http.StatusBadRequest,
Cause: "UNSPECIFIED", // TODO: Check if this is the correct cause
}

util.GinProblemJson(c, problemDetails)
return
}

var nssaiAvailabilityInfo models.NssaiAvailabilityInfo
data, err := c.GetRawData()
if err != nil {
problemDetails := &models.ProblemDetails{
Status: http.StatusInternalServerError,
Cause: "SYSTEM_FAILURE",
}

util.GinProblemJson(c, problemDetails)
return
}

if err = openapi.Deserialize(&nssaiAvailabilityInfo, data, "application/json"); err != nil {
problemDetails := &models.ProblemDetails{
Status: http.StatusBadRequest,
Cause: "UNSPECIFIED", // TODO: Check if this is the correct cause
}

logger.SBILog.Errorf("Error deserializing NSSAI availability info: %+v", err)
util.GinProblemJson(c, problemDetails)
return
}

s.Processor().NssaiAvailabilityNfInstanceUpdate(c, nssaiAvailabilityInfo, nfId)
}

func (s *Server) NSSAIAvailabilityPost(c *gin.Context) {
var createData models.NssfEventSubscriptionCreateData

requestBody, err := c.GetRawData()
if err != nil {
problemDetail := &models.ProblemDetails{
Title: "System failure",
Status: http.StatusInternalServerError,
Detail: err.Error(),
Cause: "SYSTEM_FAILURE",
}
logger.NssaiavailLog.Errorf("Get Request Body error: %+v", err)

util.GinProblemJson(c, problemDetail)
return
}

err = openapi.Deserialize(&createData, requestBody, "application/json")
if err != nil {
problemDetail := "[Request Body] " + err.Error()
rsp := &models.ProblemDetails{
Title: "Malformed request syntax",
Status: http.StatusBadRequest,
Detail: problemDetail,
}
logger.NssaiavailLog.Errorln(problemDetail)

util.GinProblemJson(c, rsp)
return
}

s.Processor().NssaiAvailabilitySubscriptionCreate(c, createData)
}

func (s *Server) NSSAIAvailabilityUnsubscribeDelete(c *gin.Context) {
// Due to conflict of route matching, 'subscriptions' in the route is replaced with the existing wildcard ':nfId'
nfID := c.Param("nfId")
if nfID != "subscriptions" {
c.JSON(http.StatusNotFound, gin.H{})
logger.NssaiavailLog.Infof("404 Not Found")
return
}

subscriptionId := c.Params.ByName("subscriptionId")
if subscriptionId == "" {
problemDetails := &models.ProblemDetails{
Status: http.StatusBadRequest,
Cause: "UNSPECIFIED", // TODO: Check if this is the correct cause
}

util.GinProblemJson(c, problemDetails)
return
}

s.Processor().NssaiAvailabilitySubscriptionUnsubscribe(c, subscriptionId)
}
37 changes: 37 additions & 0 deletions internal/sbi/api_nsselection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package sbi

import (
"net/http"
"strings"

"github.com/gin-gonic/gin"

"github.com/free5gc/nssf/internal/logger"
)

func (s *Server) getNsSelectionRoutes() []Route {
return []Route{
{
"Health Check",
strings.ToUpper("Get"),
"/",
func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{"status": "Service Available"})
},
},

{
"NSSelectionGet",
strings.ToUpper("Get"),
"/network-slice-information",
s.NetworkSliceInformationGet,
},
}
}

func (s *Server) NetworkSliceInformationGet(c *gin.Context) {
logger.NsselLog.Infof("Handle NSSelectionGet")

query := c.Request.URL.Query()
s.Processor().NSSelectionSliceInformationGet(c, query)
}
25 changes: 25 additions & 0 deletions internal/sbi/consumer/consumer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package consumer

import (
"github.com/free5gc/nssf/pkg/app"
"github.com/free5gc/openapi/Nnrf_NFManagement"
)

type Consumer struct {
app.NssfApp

*NrfService
}

func NewConsumer(nssf app.NssfApp) *Consumer {
configuration := Nnrf_NFManagement.NewConfiguration()
configuration.SetBasePath(nssf.Context().NrfUri)
nrfService := &NrfService{
nrfNfMgmtClient: Nnrf_NFManagement.NewAPIClient(configuration),
}

return &Consumer{
NssfApp: nssf,
NrfService: nrfService,
}
}
Loading

0 comments on commit a73f10f

Please sign in to comment.