diff --git a/nucleus/gnmi_transport_test.go b/nucleus/gnmi_transport_test.go index 9bc9a4de923b8d29fa0ca9ba5481f9b8e3ff51ca..d77cbe0caef4de1daf8f373b0bb821724ba3a0e3 100644 --- a/nucleus/gnmi_transport_test.go +++ b/nucleus/gnmi_transport_test.go @@ -47,6 +47,7 @@ func TestMain(m *testing.M) { testSetupPnd() testSetupStore() testSetupSbi() + testSetupHttp() testSetupIntegration() os.Exit(m.Run()) } diff --git a/nucleus/http.go b/nucleus/http.go index cf808a245815be38935c20f70f51de9212c661b1..12610d9eea4e011285efecc8a5e9563020937e01 100644 --- a/nucleus/http.go +++ b/nucleus/http.go @@ -10,13 +10,11 @@ import ( "net/url" ) -const basePath = "/api" - // deprecated func httpApi() (err error) { - http.HandleFunc(basePath, httpHandler) + http.HandleFunc("/api", httpHandler) http.HandleFunc("/livez", healthCheck) - http.HandleFunc("/readyz", healthCheck) + http.HandleFunc("/readyz", readynessCheck) go func() { err = http.ListenAndServe(":8080", nil) @@ -31,6 +29,10 @@ func healthCheck(writer http.ResponseWriter, request *http.Request) { writer.WriteHeader(http.StatusOK) } +func readynessCheck(writer http.ResponseWriter, request *http.Request) { + writer.WriteHeader(http.StatusOK) +} + // nolint func httpHandler(writer http.ResponseWriter, request *http.Request) { log.WithFields(log.Fields{ @@ -40,6 +42,7 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) { query, err := url.ParseQuery(request.URL.RawQuery) if err != nil { log.Error(err) + writer.WriteHeader(http.StatusBadRequest) return } @@ -64,9 +67,19 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) { pnd, err = c.pndc.get(pid) if err != nil { log.Error(err) + writer.WriteHeader(http.StatusInternalServerError) + return } sbic := pnd.GetSBIs() sbi, err = sbic.(*sbiStore).get(sid) + if err != nil { + log.WithFields(log.Fields{ + "requested uuid": sid, + "available uuids": sbic.(*sbiStore).UUIDs(), + }).Error(err) + writer.WriteHeader(http.StatusInternalServerError) + return + } } switch query.Get("q") { @@ -84,7 +97,7 @@ func httpHandler(writer http.ResponseWriter, request *http.Request) { }) err = pnd.AddDevice(d) if err != nil { - writer.WriteHeader(http.StatusBadRequest) + writer.WriteHeader(http.StatusInternalServerError) log.Error(err) return } diff --git a/nucleus/http_test.go b/nucleus/http_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6f88cf7ab902795557258036cdd8b49dd99b154b --- /dev/null +++ b/nucleus/http_test.go @@ -0,0 +1,180 @@ +package nucleus + +import ( + "code.fbi.h-da.de/cocsn/gosdn/mocks" + "errors" + "github.com/google/uuid" + log "github.com/sirupsen/logrus" + "github.com/stretchr/testify/mock" + "net/http" + "testing" +) + +const apiEndpoint = "http://localhost:8080" + +var sbi SouthboundInterface +var pnd PrincipalNetworkDomain +var args string +var argsNotFound string +var d Device + +func testSetupHttp() { + testSetupPnd() + sbi = &OpenConfig{id: defaultSbiId} + sbi.Schema() + var err error + pnd, err = NewPND("test", "test pnd", defaultPndId, sbi) + if err != nil { + log.Fatal(err) + } + d = mockDevice() + tr := d.Transport.(*mocks.Transport) + mockError := errors.New("mock error") + tr.On("Get", mockContext, "/system/config/hostname").Return(mock.Anything, nil) + tr.On("Get", mockContext, "error").Return(mock.Anything, mockError) + tr.On("Set", mockContext, mock.Anything).Return(mock.Anything, nil) + tr.On("ProcessResponse", mock.Anything, mock.Anything, mock.Anything).Return(nil) + if err := pnd.AddDevice(&d); err != nil { + log.Fatal(err) + } + args = "&uuid=" + mdid.String() + "&pnd=" + defaultPndId.String() + "&sbi=" + defaultSbiId.String() + argsNotFound = "&uuid=" + uuid.New().String() + "&pnd=" + defaultPndId.String() + "&sbi=" + defaultSbiId.String() + + c = &Core{ + pndc: pndStore{store{}}, + sbic: sbiStore{store{}}, + } + if err := c.sbic.add(sbi); err != nil { + log.Fatal(err) + } + if err := c.pndc.add(pnd); err != nil { + log.Fatal(err) + } +} + +func Test_httpApi(t *testing.T) { + tests := []struct { + name string + request string + want *http.Response + wantErr bool + }{ + { + name: "liveliness indicator", + request: apiEndpoint + "/livez", + want: &http.Response{StatusCode: http.StatusOK}, + wantErr: false, + }, + { + name: "readyness indicator", + request: apiEndpoint + "/readyz", + want: &http.Response{StatusCode: http.StatusOK}, + wantErr: false, + }, + { + name: "init", + request: apiEndpoint + "/api?q=init", + want: &http.Response{StatusCode: http.StatusOK}, + wantErr: false, + }, + { + name: "get-ids", + request: apiEndpoint + "/api?q=getIDs", + want: &http.Response{StatusCode: http.StatusOK}, + wantErr: false, + }, + { + name: "add-device", + request: apiEndpoint + "/api?q=addDevice" + args, + want: &http.Response{StatusCode: http.StatusCreated}, + wantErr: false, + }, + { + name: "request", + request: apiEndpoint + "/api?q=request" + args + "&path=/system/config/hostname", + want: &http.Response{StatusCode: http.StatusOK}, + wantErr: false, + }, + { + name: "request not found", + request: apiEndpoint + "/api?q=request" + argsNotFound + "&path=/system/config/hostname", + want: &http.Response{StatusCode: http.StatusNotFound}, + wantErr: false, + }, + { + name: "request internal server error", + request: apiEndpoint + "/api?q=request" + args + "&path=error", + want: &http.Response{StatusCode: http.StatusInternalServerError}, + wantErr: false, + }, + { + name: "request-all", + request: apiEndpoint + "/api?q=requestAll" + args + "&path=/system/config/hostname", + want: &http.Response{StatusCode: http.StatusOK}, + wantErr: false, + }, + { + name: "request-all internal server error", + request: apiEndpoint + "/api?q=requestAll" + args + "&path=error", + want: &http.Response{StatusCode: http.StatusInternalServerError}, + wantErr: false, + }, + + { + name: "get-device", + request: apiEndpoint + "/api?q=getDevice" + args, + want: &http.Response{StatusCode: http.StatusOK}, + wantErr: false, + }, + { + name: "get-device not found", + request: apiEndpoint + "/api?q=getDevice" + argsNotFound, + want: &http.Response{StatusCode: http.StatusNotFound}, + wantErr: false, + }, + { + name: "set", + request: apiEndpoint + "/api?q=set" + args + "&path=/system/config/hostname&value=ceos3000", + want: &http.Response{StatusCode: http.StatusOK}, + wantErr: false, + }, + { + name: "internal server errror: wrong pnd", + request: apiEndpoint + "/api?pnd=" + uuid.New().String(), + want: &http.Response{StatusCode: http.StatusInternalServerError}, + wantErr: false, + }, + { + name: "internal server errror: wrong sbi", + request: apiEndpoint + "/api?sbi=" + uuid.New().String(), + want: &http.Response{StatusCode: http.StatusInternalServerError}, + wantErr: false, + }, + } + if err := httpApi(); err != nil { + t.Errorf("httpApi() error = %v", err) + return + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := http.Get(tt.request) + if (err != nil) != tt.wantErr { + t.Errorf("httpApi() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got.StatusCode != tt.want.StatusCode { + t.Errorf("httpApi() got: %v, want %v", got.StatusCode, tt.want.StatusCode) + } + if tt.name == "add-device" { + for k := range pnd.(*pndImplementation).devices.store{ + if k != mdid { + if err := pnd.RemoveDevice(k); err != nil { + t.Error(err) + return + } + } + } + } + }) + } +}