From: elinuxhenrik Date: Thu, 18 Nov 2021 06:52:38 +0000 (+0100) Subject: Introduce choice between secure and non secure com X-Git-Tag: 1.2.0~41 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=commitdiff_plain;h=200787a2e87c95b43f36c538e5d4ed5f5ca62d53;p=nonrtric.git Introduce choice between secure and non secure com The consumer now uses secure communication towards any address configured to use the https scheme. If its own callback is configured to the https scheme, it will only listen to an https port, otherwise it will only use an http port. Issue-ID: NONRTRIC-638 Signed-off-by: elinuxhenrik Change-Id: I54dd3e9e4652998337c55b2ab30e5daa4d96de4b --- diff --git a/test/usecases/oruclosedlooprecovery/goversion/.gitignore b/test/usecases/oruclosedlooprecovery/goversion/.gitignore index f564804c..381bc416 100644 --- a/test/usecases/oruclosedlooprecovery/goversion/.gitignore +++ b/test/usecases/oruclosedlooprecovery/goversion/.gitignore @@ -2,4 +2,5 @@ .history oruclosedloop -simulator +producer +sdnr diff --git a/test/usecases/oruclosedlooprecovery/goversion/README.md b/test/usecases/oruclosedlooprecovery/goversion/README.md index 1ca37b39..ec542c12 100644 --- a/test/usecases/oruclosedlooprecovery/goversion/README.md +++ b/test/usecases/oruclosedlooprecovery/goversion/README.md @@ -2,25 +2,52 @@ This consumer creates a job of type `STD_Fault_Messages` in the Information Coordinator Service (ICS). When it recieves messages, it checks if they are link failure messages. If they are, it checks if the event severity is other than normal. If so, it looks up the O-DU ID mapped to the O-RU the message originates from and sends a configuration message to the O-DU through SDNC. If the event severity is normal, then it logs, on `Debug` level, that the link failure has been cleared. -The producer takes a number of environment variables, described below, as configuration. +## Configuration + +The consumer takes a number of environment variables, described below, as configuration. >- CONSUMER_HOST **Required**. The host for the consumer. Example: `http://mrproducer` ->- CONSUMER_HOST **Required**. The port for the consumer. Example: `8095` ->- LOG_LEVEL Optional. The log level, which can be `Error`, `Warn`, `Info` or `Debug`. Defaults to `Info`. +>- CONSUMER_PORT **Required**. The port for the consumer. Example: `8095` +>- CONSUMER_CERT_PATH **Required**. The path to the certificate to use for https. Defaults to `security/producer.crt` +>- CONSUMER_KEY_PATH **Required**. The path to the key to the certificate to use for https. Defaults to `security/producer.key` >- INFO_COORD_ADDR Optional. The address of the Information Coordinator. Defaults to `http://enrichmentservice:8083`. ->- SDNR_HOST Optional. The host for SDNR. Defaults to `http://localhost`. ->- SDNR_PORT Optional. The port for SDNR. Defaults to `3904`. +>- SDNR_ADDRESS Optional. The address for SDNR. Defaults to `http://localhost:3904`. >- SDNR_USER Optional. The user for the SDNR. Defaults to `admin`. >- SDNR_PASSWORD Optional. The password for the SDNR user. Defaults to `Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U`. >- ORU_TO_ODU_MAP_FILE Optional. The file containing the mapping from O-RU ID to O-DU ID. Defaults to `o-ru-to-o-du-map.csv`. +>- LOG_LEVEL Optional. The log level, which can be `Error`, `Warn`, `Info` or `Debug`. Defaults to `Info`. + +Any of the addresses used by this product can be configured to use https, by specifying it as the scheme of the address URI. The client will not use server certificate verification. The consumer's own callback will only listen to the scheme configured in the scheme of the consumer host address. + +The configured public key and cerificate shall be PEM-encoded. A self signed certificate and key are provided in the `security` folder of the project. These files should be replaced for production. To generate a self signed key and certificate, use the example code below: + + openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650 -The creation of the job is not done when the consumer is started. Instead the consumer provides a REST API where it can be started and stopped, described below. +T## Functionality + +he creation of the job is not done when the consumer is started. Instead the consumer provides a REST API where it can be started and stopped, described below. >- /start Creates the job in ICS. >- /stop Deletes the job in ICS. If the consumer is shut down with a SIGTERM, it will also delete the job before exiting. +## Development + +To make it easy to test during development of the consumer, two stubs are provided in the `stub` folder. + +One, under the `producer` folder, called `producer` that stubs the producer and pushes an array with one message with `eventSeverity` alternating between `NORMAL` and `CRITICAL`. To build and start the stub, do the following: +>1. cd stub/producer +>2. go build +>3. ./producer + +One, under the `sdnr` folder, called `sdnr` that at startup will listen for REST calls and print the body of them. By default, it listens to the port `3904`, but his can be overridden by passing a `-port [PORT]` flag when starting the stub. To build and start the stub, do the following: +>1. cd stub/sdnr +>2. go build +>3. ./sdnr + +Mocks needed for unit tests have been generated using `github.com/stretchr/testify/mock` and are checked in under the `mocks` folder. **Note!** Keep in mind that if any of the mocked interfaces change, a new mock for that interface must be generated and checked in. + ## License Copyright (C) 2021 Nordix Foundation. diff --git a/test/usecases/oruclosedlooprecovery/goversion/go.mod b/test/usecases/oruclosedlooprecovery/goversion/go.mod index 754bba10..2eaa371c 100644 --- a/test/usecases/oruclosedlooprecovery/goversion/go.mod +++ b/test/usecases/oruclosedlooprecovery/goversion/go.mod @@ -9,10 +9,14 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/mux v1.8.0 github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.1.1 // indirect golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) + +require ( + github.com/hashicorp/go-cleanhttp v0.5.1 // indirect + github.com/hashicorp/go-retryablehttp v0.7.0 // indirect +) diff --git a/test/usecases/oruclosedlooprecovery/goversion/go.sum b/test/usecases/oruclosedlooprecovery/goversion/go.sum index 6ce76047..970999b8 100644 --- a/test/usecases/oruclosedlooprecovery/goversion/go.sum +++ b/test/usecases/oruclosedlooprecovery/goversion/go.sum @@ -5,6 +5,11 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-retryablehttp v0.7.0 h1:eu1EI/mbirUgP5C8hVsTNaGZreBDlYiwC1FZWkvQPQ4= +github.com/hashicorp/go-retryablehttp v0.7.0/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= diff --git a/test/usecases/oruclosedlooprecovery/goversion/internal/config/config.go b/test/usecases/oruclosedlooprecovery/goversion/internal/config/config.go index 43656b77..718f435f 100644 --- a/test/usecases/oruclosedlooprecovery/goversion/internal/config/config.go +++ b/test/usecases/oruclosedlooprecovery/goversion/internal/config/config.go @@ -21,6 +21,7 @@ package config import ( + "fmt" "os" "strconv" @@ -28,31 +29,37 @@ import ( ) type Config struct { - LogLevel log.Level ConsumerHost string ConsumerPort int InfoCoordinatorAddress string - SDNRHost string - SDNRPort int + SDNRAddress string SDNRUser string SDNPassword string ORUToODUMapFile string + ConsumerCertPath string + ConsumerKeyPath string + LogLevel log.Level } func New() *Config { return &Config{ - LogLevel: getLogLevel(), ConsumerHost: getEnv("CONSUMER_HOST", ""), ConsumerPort: getEnvAsInt("CONSUMER_PORT", 0), InfoCoordinatorAddress: getEnv("INFO_COORD_ADDR", "http://enrichmentservice:8083"), - SDNRHost: getEnv("SDNR_HOST", "http://localhost"), - SDNRPort: getEnvAsInt("SDNR_PORT", 3904), + SDNRAddress: getEnv("SDNR_ADDR", "http://localhost:3904"), SDNRUser: getEnv("SDNR_USER", "admin"), SDNPassword: getEnv("SDNR_PASSWORD", "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U"), ORUToODUMapFile: getEnv("ORU_TO_ODU_MAP_FILE", "o-ru-to-o-du-map.csv"), + ConsumerCertPath: getEnv("CONSUMER_CERT_PATH", "security/consumer.crt"), + ConsumerKeyPath: getEnv("CONSUMER_KEY_PATH", "security/consumer.key"), + LogLevel: getLogLevel(), } } +func (c Config) String() string { + return fmt.Sprintf("ConsumerHost: %v, ConsumerPort: %v, InfoCoordinatorAddress: %v, SDNRAddress: %v, SDNRUser: %v, SDNRPassword: %v, ORUToODUMapFile: %v, ConsumerCertPath: %v, ConsumerKeyPath: %v, LogLevel: %v", c.ConsumerHost, c.ConsumerPort, c.InfoCoordinatorAddress, c.SDNRAddress, c.SDNRUser, c.SDNPassword, c.ORUToODUMapFile, c.ConsumerCertPath, c.ConsumerKeyPath, c.LogLevel) +} + func getEnv(key string, defaultVal string) string { if value, exists := os.LookupEnv(key); exists { return value diff --git a/test/usecases/oruclosedlooprecovery/goversion/internal/config/config_test.go b/test/usecases/oruclosedlooprecovery/goversion/internal/config/config_test.go index a5b16242..3d9983a6 100644 --- a/test/usecases/oruclosedlooprecovery/goversion/internal/config/config_test.go +++ b/test/usecases/oruclosedlooprecovery/goversion/internal/config/config_test.go @@ -31,28 +31,30 @@ import ( func TestNew_envVarsSetConfigContainSetValues(t *testing.T) { assertions := require.New(t) - os.Setenv("LOG_LEVEL", "Debug") os.Setenv("CONSUMER_HOST", "consumerHost") os.Setenv("CONSUMER_PORT", "8095") os.Setenv("INFO_COORD_ADDR", "infoCoordAddr") - os.Setenv("SDNR_HOST", "sdnrHost") - os.Setenv("SDNR_PORT", "3908") + os.Setenv("SDNR_ADDR", "sdnrHost:3908") os.Setenv("SDNR_USER", "admin") os.Setenv("SDNR_PASSWORD", "pwd") os.Setenv("ORU_TO_ODU_MAP_FILE", "file") + os.Setenv("CONSUMER_CERT_PATH", "cert") + os.Setenv("CONSUMER_KEY_PATH", "key") + os.Setenv("LOG_LEVEL", "Debug") t.Cleanup(func() { os.Clearenv() }) wantConfig := Config{ - LogLevel: log.DebugLevel, ConsumerHost: "consumerHost", ConsumerPort: 8095, InfoCoordinatorAddress: "infoCoordAddr", - SDNRHost: "sdnrHost", - SDNRPort: 3908, + SDNRAddress: "sdnrHost:3908", SDNRUser: "admin", SDNPassword: "pwd", ORUToODUMapFile: "file", + ConsumerCertPath: "cert", + ConsumerKeyPath: "key", + LogLevel: log.DebugLevel, } got := New() @@ -70,15 +72,16 @@ func TestNew_faultyIntValueSetConfigContainDefaultValueAndWarnInLog(t *testing.T os.Clearenv() }) wantConfig := Config{ - LogLevel: log.InfoLevel, ConsumerHost: "", ConsumerPort: 0, InfoCoordinatorAddress: "http://enrichmentservice:8083", - SDNRHost: "http://localhost", - SDNRPort: 3904, + SDNRAddress: "http://localhost:3904", SDNRUser: "admin", SDNPassword: "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U", ORUToODUMapFile: "o-ru-to-o-du-map.csv", + ConsumerCertPath: "security/consumer.crt", + ConsumerKeyPath: "security/consumer.key", + LogLevel: log.InfoLevel, } got := New() @@ -99,15 +102,16 @@ func TestNew_envFaultyLogLevelConfigContainDefaultValues(t *testing.T) { os.Clearenv() }) wantConfig := Config{ - LogLevel: log.InfoLevel, ConsumerHost: "", ConsumerPort: 0, InfoCoordinatorAddress: "http://enrichmentservice:8083", - SDNRHost: "http://localhost", - SDNRPort: 3904, + SDNRAddress: "http://localhost:3904", SDNRUser: "admin", SDNPassword: "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U", ORUToODUMapFile: "o-ru-to-o-du-map.csv", + ConsumerCertPath: "security/consumer.crt", + ConsumerKeyPath: "security/consumer.key", + LogLevel: log.InfoLevel, } got := New() assertions.Equal(&wantConfig, got) diff --git a/test/usecases/oruclosedlooprecovery/goversion/internal/linkfailure/linkfailurehandler.go b/test/usecases/oruclosedlooprecovery/goversion/internal/linkfailure/linkfailurehandler.go index 01f121a9..3aecf45c 100644 --- a/test/usecases/oruclosedlooprecovery/goversion/internal/linkfailure/linkfailurehandler.go +++ b/test/usecases/oruclosedlooprecovery/goversion/internal/linkfailure/linkfailurehandler.go @@ -76,10 +76,10 @@ func (lfh LinkFailureHandler) sendUnlockMessage(oRuId string) { if error := restclient.Put(lfh.config.SDNRAddress+sdnrPath, unlockMessage, lfh.client, lfh.config.SDNRUser, lfh.config.SDNRPassword); error == nil { log.Debugf("Sent unlock message for O-RU: %v to O-DU: %v.", oRuId, oDuId) } else { - log.Warn(error) + log.Warn("Send of unlock message failed due to ", error) } } else { - log.Warn(err) + log.Warn("Send of unlock message failed due to ", err) } } diff --git a/test/usecases/oruclosedlooprecovery/goversion/internal/restclient/client.go b/test/usecases/oruclosedlooprecovery/goversion/internal/restclient/client.go index 036819a0..fdd0549a 100644 --- a/test/usecases/oruclosedlooprecovery/goversion/internal/restclient/client.go +++ b/test/usecases/oruclosedlooprecovery/goversion/internal/restclient/client.go @@ -22,9 +22,15 @@ package restclient import ( "bytes" + "crypto/tls" "fmt" "io" + "math" "net/http" + "net/url" + "time" + + "github.com/hashicorp/go-retryablehttp" ) type RequestError struct { @@ -33,7 +39,7 @@ type RequestError struct { } func (e RequestError) Error() string { - return fmt.Sprintf("Request failed due to error response with status: %v and body: %v", e.StatusCode, string(e.Body)) + return fmt.Sprintf("error response with status: %v and body: %v", e.StatusCode, string(e.Body)) } // HTTPClient interface @@ -55,6 +61,40 @@ func Delete(url string, client HTTPClient) error { return do(http.MethodDelete, url, nil, client) } +func CreateClientCertificate(certPath string, keyPath string) (tls.Certificate, error) { + if cert, err := tls.LoadX509KeyPair(certPath, keyPath); err == nil { + return cert, nil + } else { + return tls.Certificate{}, fmt.Errorf("cannot create x509 keypair from cert file %s and key file %s due to: %v", certPath, keyPath, err) + } +} + +func CreateRetryClient(cert tls.Certificate) *http.Client { + rawRetryClient := retryablehttp.NewClient() + rawRetryClient.RetryWaitMax = time.Minute + rawRetryClient.RetryMax = math.MaxInt + rawRetryClient.HTTPClient.Transport = getSecureTransportWithoutVerify(cert) + + client := rawRetryClient.StandardClient() + return client +} + +func IsUrlSecure(configUrl string) bool { + u, _ := url.Parse(configUrl) + return u.Scheme == "https" +} + +func getSecureTransportWithoutVerify(cert tls.Certificate) *http.Transport { + return &http.Transport{ + TLSClientConfig: &tls.Config{ + Certificates: []tls.Certificate{ + cert, + }, + InsecureSkipVerify: true, + }, + } +} + func do(method string, url string, body []byte, client HTTPClient, userInfo ...string) error { if req, reqErr := http.NewRequest(method, url, bytes.NewBuffer(body)); reqErr == nil { if body != nil { diff --git a/test/usecases/oruclosedlooprecovery/goversion/internal/restclient/client_test.go b/test/usecases/oruclosedlooprecovery/goversion/internal/restclient/client_test.go index 8271fd07..2c915fda 100644 --- a/test/usecases/oruclosedlooprecovery/goversion/internal/restclient/client_test.go +++ b/test/usecases/oruclosedlooprecovery/goversion/internal/restclient/client_test.go @@ -21,11 +21,16 @@ package restclient import ( "bytes" + "crypto/tls" "fmt" "io/ioutil" + "math" "net/http" + "reflect" "testing" + "time" + "github.com/hashicorp/go-retryablehttp" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "oransc.org/usecase/oruclosedloop/mocks" @@ -38,7 +43,7 @@ func TestRequestError_Error(t *testing.T) { StatusCode: http.StatusBadRequest, Body: []byte("error"), } - assertions.Equal("Request failed due to error response with status: 400 and body: error", actualError.Error()) + assertions.Equal("error response with status: 400 and body: error", actualError.Error()) } func TestPutWithoutAuth(t *testing.T) { @@ -167,3 +172,63 @@ func Test_doErrorCases(t *testing.T) { }) } } + +func Test_createClientCertificate(t *testing.T) { + assertions := require.New(t) + wantedCert, _ := tls.LoadX509KeyPair("../../security/consumer.crt", "../../security/consumer.key") + type args struct { + certPath string + keyPath string + } + tests := []struct { + name string + args args + wantCert tls.Certificate + wantErr error + }{ + { + name: "Paths to cert info ok should return cerftificate", + args: args{ + certPath: "../../security/consumer.crt", + keyPath: "../../security/consumer.key", + }, + wantCert: wantedCert, + }, + { + name: "Paths to cert info not ok should return error with info about error", + args: args{ + certPath: "wrong_cert", + keyPath: "wrong_key", + }, + wantErr: fmt.Errorf("cannot create x509 keypair from cert file wrong_cert and key file wrong_key due to: open wrong_cert: no such file or directory"), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cert, err := CreateClientCertificate(tt.args.certPath, tt.args.keyPath) + assertions.Equal(tt.wantCert, cert, tt.name) + assertions.Equal(tt.wantErr, err, tt.name) + }) + } +} + +func Test_CreateRetryClient(t *testing.T) { + assertions := require.New(t) + + client := CreateRetryClient(tls.Certificate{}) + + transport := client.Transport + assertions.Equal("*retryablehttp.RoundTripper", reflect.TypeOf(transport).String()) + retryableTransport := transport.(*retryablehttp.RoundTripper) + retryableClient := retryableTransport.Client + assertions.Equal(time.Minute, retryableClient.RetryWaitMax) + assertions.Equal(math.MaxInt, retryableClient.RetryMax) +} + +func TestIsUrlSecured(t *testing.T) { + assertions := require.New(t) + + assertions.True(IsUrlSecure("https://url")) + + assertions.False(IsUrlSecure("http://url")) +} diff --git a/test/usecases/oruclosedlooprecovery/goversion/main.go b/test/usecases/oruclosedlooprecovery/goversion/main.go index bef9e24c..b7d68954 100644 --- a/test/usecases/oruclosedlooprecovery/goversion/main.go +++ b/test/usecases/oruclosedlooprecovery/goversion/main.go @@ -21,13 +21,13 @@ package main import ( + "crypto/tls" "encoding/json" "fmt" "net/http" "os" "os/signal" "syscall" - "time" "github.com/gorilla/mux" log "github.com/sirupsen/logrus" @@ -41,7 +41,6 @@ type Server interface { ListenAndServe() error } -const timeoutHTTPClient = time.Second * 5 const jobId = "14e7bb84-a44d-44c1-90b7-6995a92ad43c" var jobRegistrationInfo = struct { @@ -70,16 +69,13 @@ func doInit() { configuration = config.New() log.SetLevel(configuration.LogLevel) - - client = &http.Client{ - Timeout: timeoutHTTPClient, - } + log.Debug("Using configuration: ", configuration) consumerPort = fmt.Sprint(configuration.ConsumerPort) jobRegistrationInfo.JobResultUri = configuration.ConsumerHost + ":" + consumerPort linkfailureConfig = linkfailure.Configuration{ - SDNRAddress: configuration.SDNRHost + ":" + fmt.Sprint(configuration.SDNRPort), + SDNRAddress: configuration.SDNRAddress, SDNRUser: configuration.SDNRUser, SDNRPassword: configuration.SDNPassword, } @@ -95,12 +91,16 @@ func main() { log.Fatalf("Unable to create LookupService due to inability to get O-RU-ID to O-DU-ID map. Cause: %v", initErr) } + var cert tls.Certificate + if c, err := restclient.CreateClientCertificate(configuration.ConsumerCertPath, configuration.ConsumerKeyPath); err == nil { + cert = c + } else { + log.Fatalf("Stopping producer due to error: %v", err) + } + client = restclient.CreateRetryClient(cert) + go func() { - startServer(&http.Server{ - Addr: ":" + consumerPort, - Handler: getRouter(), - }) - deleteJob() + startServer() os.Exit(1) // If the startServer function exits, it is because there has been a failure in the server, so we exit. }() @@ -116,6 +116,11 @@ func validateConfiguration(configuration *config.Config) error { if configuration.ConsumerHost == "" || configuration.ConsumerPort == 0 { return fmt.Errorf("consumer host and port must be provided") } + + if configuration.ConsumerCertPath == "" || configuration.ConsumerKeyPath == "" { + return fmt.Errorf("missing CONSUMER_CERT and/or CONSUMER_KEY") + } + return nil } @@ -135,8 +140,14 @@ func getRouter() *mux.Router { return r } -func startServer(server Server) { - if err := server.ListenAndServe(); err != nil { +func startServer() { + var err error + if restclient.IsUrlSecure(configuration.ConsumerHost) { + err = http.ListenAndServeTLS(fmt.Sprintf(":%v", configuration.ConsumerPort), configuration.ConsumerCertPath, configuration.ConsumerKeyPath, getRouter()) + } else { + err = http.ListenAndServe(fmt.Sprintf(":%v", configuration.ConsumerPort), getRouter()) + } + if err != nil { log.Errorf("Server stopped unintentionally due to: %v. Deleteing job.", err) if deleteErr := deleteJob(); deleteErr != nil { log.Error(fmt.Sprintf("Unable to delete consumer job due to: %v. Please remove job %v manually.", deleteErr, jobId)) diff --git a/test/usecases/oruclosedlooprecovery/goversion/main_test.go b/test/usecases/oruclosedlooprecovery/goversion/main_test.go index 99419bf7..3fcb23ea 100644 --- a/test/usecases/oruclosedlooprecovery/goversion/main_test.go +++ b/test/usecases/oruclosedlooprecovery/goversion/main_test.go @@ -54,15 +54,16 @@ func Test_init(t *testing.T) { doInit() wantedConfiguration := &config.Config{ - LogLevel: log.InfoLevel, ConsumerHost: "consumerHost", ConsumerPort: 8095, InfoCoordinatorAddress: "http://enrichmentservice:8083", - SDNRHost: "http://localhost", - SDNRPort: 3904, + SDNRAddress: "http://localhost:3904", SDNRUser: "admin", SDNPassword: "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U", ORUToODUMapFile: "o-ru-to-o-du-map.csv", + ConsumerCertPath: "security/consumer.crt", + ConsumerKeyPath: "security/consumer.key", + LogLevel: log.InfoLevel, } assertions.Equal(wantedConfiguration, configuration) @@ -70,7 +71,7 @@ func Test_init(t *testing.T) { assertions.Equal(wantedConfiguration.ConsumerHost+":"+fmt.Sprint(wantedConfiguration.ConsumerPort), jobRegistrationInfo.JobResultUri) wantedLinkFailureConfig := linkfailure.Configuration{ - SDNRAddress: wantedConfiguration.SDNRHost + ":" + fmt.Sprint(wantedConfiguration.SDNRPort), + SDNRAddress: wantedConfiguration.SDNRAddress, SDNRUser: wantedConfiguration.SDNRUser, SDNRPassword: wantedConfiguration.SDNPassword, } @@ -92,8 +93,10 @@ func Test_validateConfiguration(t *testing.T) { name: "Valid config, should return nil", args: args{ configuration: &config.Config{ - ConsumerHost: "host", - ConsumerPort: 80, + ConsumerHost: "host", + ConsumerPort: 80, + ConsumerCertPath: "security/consumer.crt", + ConsumerKeyPath: "security/consumer.key", }, }, }, @@ -188,28 +191,6 @@ func Test_getRouter_shouldContainAllPathsWithHandlers(t *testing.T) { assertions.Equal("/admin/stop", path) } -func Test_startServer_shouldDeleteJobWhenServerStopsWithErrorAndLog(t *testing.T) { - assertions := require.New(t) - - var buf bytes.Buffer - log.SetOutput(&buf) - - os.Setenv("CONSUMER_PORT", "wrong") - t.Cleanup(func() { - log.SetOutput(os.Stderr) - }) - - mockServer := &mocks.Server{} - mockServer.On("ListenAndServe").Return(errors.New("Server failure")) - - startServer(mockServer) - - log := buf.String() - assertions.Contains(log, "level=error") - assertions.Contains(log, "Server stopped unintentionally due to: Server failure. Deleteing job.") - assertions.Contains(log, "Please remove job 14e7bb84-a44d-44c1-90b7-6995a92ad43c manually") -} - func Test_startHandler(t *testing.T) { assertions := require.New(t) diff --git a/test/usecases/oruclosedlooprecovery/goversion/mocks/Server.go b/test/usecases/oruclosedlooprecovery/goversion/mocks/Server.go deleted file mode 100644 index ad16503f..00000000 --- a/test/usecases/oruclosedlooprecovery/goversion/mocks/Server.go +++ /dev/null @@ -1,24 +0,0 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" - -// Server is an autogenerated mock type for the Server type -type Server struct { - mock.Mock -} - -// ListenAndServe provides a mock function with given fields: -func (_m *Server) ListenAndServe() error { - ret := _m.Called() - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/test/usecases/oruclosedlooprecovery/goversion/security/consumer.crt b/test/usecases/oruclosedlooprecovery/goversion/security/consumer.crt new file mode 100644 index 00000000..0f6d8a35 --- /dev/null +++ b/test/usecases/oruclosedlooprecovery/goversion/security/consumer.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDXzCCAkegAwIBAgIUEbuDTP0ixwxCxCQ9tR5DijGCbtkwDQYJKoZIhvcNAQEL +BQAwPzELMAkGA1UEBhMCc2UxDDAKBgNVBAoMA0VTVDERMA8GA1UECwwIRXJpY3Nz +b24xDzANBgNVBAMMBnNlcnZlcjAeFw0yMTEwMTkxNDA1MzVaFw0zMTEwMTcxNDA1 +MzVaMD8xCzAJBgNVBAYTAnNlMQwwCgYDVQQKDANFU1QxETAPBgNVBAsMCEVyaWNz +c29uMQ8wDQYDVQQDDAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDnH4imV8kx/mXz6BDbq8e4oZGqGgv7V837iNspj/zIZXhEMP9311fdsZEE +Y6VWU47bSYRn2xJOP+wmfKewbw0OcEWu/RkdvO7Y0VIVrlbEJYu88ZjK14dMUpfe +72iMbTc5q2uYi0ImB5/m3jyMSXgso6NDWuvXrp2VSWjb1tG++des9rhvyrZyNrua +I4iOnMvvuc71gvHol7appRu3+LRTQFYsAizdfHEQ9k949MZH4fiIu5NmCT/wNJVo +uUZYYJseFhOlIANaXn6qmz7kKVYfxfV+Z5EccaRixaClCFwyRdmjgLyyeuI4/QPD +x9PjmGmf6eOEC2ZHBi4OHwjIzmLnAgMBAAGjUzBRMB0GA1UdDgQWBBRjeDLPpLm2 +W623wna7xBCbHxtxVjAfBgNVHSMEGDAWgBRjeDLPpLm2W623wna7xBCbHxtxVjAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAbFUAWFZaIMXmd5qv/ +xJYr1oPJpsmbgWGRWZWDZqbUabvWObyXlDJWIau60BerfcC5TmyElBjTyONSGwCT +tq+SVB0PXpgqa8ZQ25Ytn2AMDFWhrGbOefCXs6te3HGq6BNubTWrOVIvJypCwC95 ++iXVuDd4eg+n2fWv7h7fZRZHum/zLoRxB2lKoMMbc/BQX9hbtP6xyvIVvaYdhcJw +VzJJGIDqpMiMH6IBaOFSmgfOyGblGKAicj3E3kpGBfadLx3R+9V6aG7zyBnVbr2w +YJbV2Ay4PrF+PTpCMB/mNwC5RBTYHpSNdrCMSyq3I+QPVJq8dPJr7fd1Uwl3WHqX +FV0h +-----END CERTIFICATE----- diff --git a/test/usecases/oruclosedlooprecovery/goversion/security/consumer.key b/test/usecases/oruclosedlooprecovery/goversion/security/consumer.key new file mode 100644 index 00000000..5346bb7f --- /dev/null +++ b/test/usecases/oruclosedlooprecovery/goversion/security/consumer.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDnH4imV8kx/mXz +6BDbq8e4oZGqGgv7V837iNspj/zIZXhEMP9311fdsZEEY6VWU47bSYRn2xJOP+wm +fKewbw0OcEWu/RkdvO7Y0VIVrlbEJYu88ZjK14dMUpfe72iMbTc5q2uYi0ImB5/m +3jyMSXgso6NDWuvXrp2VSWjb1tG++des9rhvyrZyNruaI4iOnMvvuc71gvHol7ap +pRu3+LRTQFYsAizdfHEQ9k949MZH4fiIu5NmCT/wNJVouUZYYJseFhOlIANaXn6q +mz7kKVYfxfV+Z5EccaRixaClCFwyRdmjgLyyeuI4/QPDx9PjmGmf6eOEC2ZHBi4O +HwjIzmLnAgMBAAECggEBAMq1lZyPkh8PCUyLVX3VhC4jRybyAWBI+piKx+4EI6l/ +laP5dZcegCoo+w/mdbTpRHqAWGjec4e9+Nkoq8rLG6B2SCfaRJUYiEQSEvSBHAid +BZqKK4B82GXQavNU91Vy1OT3vD7mpPXF6jEK6gAA0C4Wt7Lzo7ZfqEavRBDMsNnV +jOxLwWJCFSKhfeA6grJCnagmEDKSxxazlNBgCahjPf/+IRJZ7Vk4Zjq+I/5nWKf8 +lYaQExKDIANuM/jMRnYVq5k4g2MKHUADWGTSvG1DMJiMHzdxb2miZovpIkEE86bC +wKBuele9IR6Rb/wygYj7WdaWysZ081OT7mNyju08e4ECgYEA8+q7vv4Nlz8bAcmY +Ip5517s15M5D9iLsE2Q5m9Zs99rUyQv0E8ekpChhtTSdvj+eNl39O4hji46Gyceu +MYPfNL7+YWaFDxuyaXEe/OFuKbFqgE1p08HXFcQJCvgqD1MWO5b9BRDc0qpNFIA8 +eN9xFBMQ2UFaALBMAup7Ef85q4kCgYEA8pKOAIsgmlnO8P9cPzkMC1oozslraAti +1JnOJjwPLoHFubtH2u7WoIkSvNfeNwfrsVXwAP0m7C8p7qhYppS+0XGjKpYNSezK +1GCqCVv8R1m+AsSseSUUaQCmEydd+gQbBq3r4u3wU3ylrgAoR3m+7SVyhvD+vbwI +7+zfj+O3zu8CgYEAqaAsQH5c5Tm1hmCztB+RjD1dFWl8ScevdSzWA1HzJcrA/6+Y +ZckI7kBG8sVMjemgFR735FbNI1hS1DBRK44Rw5SvQv0Qu5j/UeShMCt1ePkwn1k2 +p1S+Rxy1TTOXzGBzra0q+ELpzncwc3lalJSPBu7bYLrZ5HC167E1NSbQ7EECgYBo +e/IIj+TyNz7pFcVhQixK84HiWGYYQddHJhzi4TnU2XcWonG3/uqZ6ZEVoJIJ+DJw +h0jC1EggscwJDaBp2GY9Bwq2PD3rGsDfK+fx8ho/jYtH2/lCkVMyS2I9m9Zh68TM +YrvZWo4LGASxZ0XyS6GOunOTZlkD1uuulMRTUU4KJwKBgQCwyjs0/ElVFvO0lPIC +JJ//B5rqI7hNMJuTBvr4yiqVZdbgFukaU7FBVyNYDMpZi/nRbpglm+psFcwXtL8n +bHOIGLkh8vB7OuETRYhXs567lPYtO4BmHZlXW70Sq/0xqi/Mmz1RuEg4SQ1Ug5oy +wG6IV5EWSQAhsGirdybQ+bY7Kw== +-----END PRIVATE KEY----- diff --git a/test/usecases/oruclosedlooprecovery/goversion/simulator/producer.go b/test/usecases/oruclosedlooprecovery/goversion/stub/producer/producerstub.go similarity index 100% rename from test/usecases/oruclosedlooprecovery/goversion/simulator/producer.go rename to test/usecases/oruclosedlooprecovery/goversion/stub/producer/producerstub.go diff --git a/test/usecases/oruclosedlooprecovery/goversion/stub/sdnr/sdnrstub.go b/test/usecases/oruclosedlooprecovery/goversion/stub/sdnr/sdnrstub.go new file mode 100644 index 00000000..b59dbd96 --- /dev/null +++ b/test/usecases/oruclosedlooprecovery/goversion/stub/sdnr/sdnrstub.go @@ -0,0 +1,49 @@ +// - +// ========================LICENSE_START================================= +// O-RAN-SC +// %% +// Copyright (C) 2021: Nordix Foundation +// %% +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ========================LICENSE_END=================================== +// + +package main + +import ( + "flag" + "fmt" + "io" + "net/http" + + "github.com/gorilla/mux" +) + +func main() { + port := flag.Int("port", 3904, "The port this SDNR stub will listen on") + flag.Parse() + + r := mux.NewRouter() + r.HandleFunc("/rests/data/network-topology:network-topology/topology=topology-netconf/node={O-DU-ID}/yang-ext:mount/o-ran-sc-du-hello-world:network-function/du-to-ru-connection={O-RU-ID}", handleData) + + fmt.Println("Starting SDNR on port: ", *port) + http.ListenAndServe(fmt.Sprintf(":%v", *port), r) + +} + +func handleData(w http.ResponseWriter, req *http.Request) { + defer req.Body.Close() + if reqData, err := io.ReadAll(req.Body); err == nil { + fmt.Println("SDNR received body: ", string(reqData)) + } +}