diff --git a/client/client_test.go b/client/client_test.go index 7ade16eb4..f15692cc6 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -1,6 +1,7 @@ package client import ( + "bytes" "context" "fmt" "io" @@ -19,6 +20,7 @@ const ( testLibraryVersion = "v3" testAbsolutePath = "api/nutanix/" + testLibraryVersion testUserAgent = "nutanix/" + testLibraryVersion + fileName = "v3/v3.go" ) func setup() (*http.ServeMux, *Client, *httptest.Server) { @@ -38,7 +40,7 @@ func TestNewClient(t *testing.T) { t.Errorf("Unexpected Error: %v", err) } - expectedURL := fmt.Sprintf(defaultBaseURL, "https", "foo.com") + expectedURL := fmt.Sprintf(defaultBaseURL, httpsPrefix, "foo.com") if c.BaseURL == nil || c.BaseURL.String() != expectedURL { t.Errorf("NewClient BaseURL = %v, expected %v", c.BaseURL, expectedURL) @@ -49,6 +51,23 @@ func TestNewClient(t *testing.T) { } } +func TestNewBaseClient(t *testing.T) { + c, err := NewBaseClient(&Credentials{"foo.com", "username", "password", "", "", true, false, "", "", "", nil}, testAbsolutePath, true) + if err != nil { + t.Errorf("Unexpected Error: %v", err) + } + + expectedURL := fmt.Sprintf(defaultBaseURL, httpPrefix, "foo.com") + + if c.BaseURL == nil || c.BaseURL.String() != expectedURL { + t.Errorf("NewBaseClient BaseURL = %v, expected %v", c.BaseURL, expectedURL) + } + + if c.AbsolutePath != testAbsolutePath { + t.Errorf("NewBaseClient UserAgent = %v, expected %v", c.AbsolutePath, testAbsolutePath) + } +} + func TestNewRequest(t *testing.T) { c, err := NewClient(&Credentials{"foo.com", "username", "password", "", "", true, false, "", "", "", nil}, testUserAgent, testAbsolutePath, false) @@ -73,6 +92,179 @@ func TestNewRequest(t *testing.T) { } } +func TestNewUploadRequest(t *testing.T) { + c, err := NewClient(&Credentials{"foo.com", "username", "password", "", "", true, false, "", "", "", nil}, testUserAgent, testAbsolutePath, true) + + if err != nil { + t.Errorf("Unexpected Error: %v", err) + } + + inURL, outURL := "/foo", fmt.Sprintf(defaultBaseURL+testAbsolutePath+"/foo", httpPrefix, "foo.com") + inBody, _ := os.Open(fileName) + if err != nil { + t.Fatalf("Error opening file %v, error : %v", fileName, err) + } + + // expected body + out, _ := os.Open(fileName) + outBody, _ := ioutil.ReadAll(out) + + req, err := c.NewUploadRequest(context.TODO(), http.MethodPost, inURL, inBody) + if err != nil { + t.Fatalf("NewUploadRequest() errored out with error : %v", err.Error()) + } + // test relative URL was expanded + if req.URL.String() != outURL { + t.Errorf("NewUploadRequest(%v) URL = %v, expected %v", inURL, req.URL, outURL) + } + + //test body contents + got, _ := ioutil.ReadAll(req.Body) + if !bytes.Equal(got, outBody) { + t.Errorf("NewUploadRequest(%v) Body = %v, expected %v", inBody, string(got), string(outBody)) + } + + // test headers. + inHeaders := map[string]string{ + "Content-Type": octetStreamType, + "Accept": mediaType, + } + for k, v := range inHeaders { + if v != req.Header[k][0] { + t.Errorf("NewUploadRequest() Header value for %v = %v, expected %v", k, req.Header[k][0], v) + } + } +} + +func TestNewUnAuthRequest(t *testing.T) { + c, err := NewClient(&Credentials{"foo.com", "username", "password", "", "", true, false, "", "", "", nil}, testUserAgent, testAbsolutePath, true) + + if err != nil { + t.Errorf("Unexpected Error: %v", err) + } + + inURL, outURL := "/foo", fmt.Sprintf(defaultBaseURL+testAbsolutePath+"/foo", httpPrefix, "foo.com") + inBody, outBody := map[string]interface{}{"name": "bar"}, `{"name":"bar"}`+"\n" + + req, _ := c.NewUnAuthRequest(context.TODO(), http.MethodPost, inURL, inBody) + + // test relative URL was expanded + if req.URL.String() != outURL { + t.Errorf("NewUnAuthRequest(%v) URL = %v, expected %v", inURL, req.URL, outURL) + } + + // test body was JSON encoded + body, _ := ioutil.ReadAll(req.Body) + if string(body) != outBody { + t.Errorf("NewUnAuthRequest(%v) Body = %v, expected %v", inBody, string(body), outBody) + } + + // test headers. Authorization header shouldn't exist + if _, ok := req.Header["Authorization"]; ok { + t.Errorf("Unexpected Authorization header obtained in request from NewUnAuthRequest()") + } + inHeaders := map[string]string{ + "Content-Type": mediaType, + "Accept": mediaType, + "User-Agent": testUserAgent, + } + for k, v := range req.Header { + if v[0] != inHeaders[k] { + t.Errorf("NewUnAuthRequest() Header value for %v = %v, expected %v", k, v[0], inHeaders[k]) + } + } +} + +func TestNewUnAuthFormEncodedRequest(t *testing.T) { + c, err := NewClient(&Credentials{"foo.com", "username", "password", "", "", true, false, "", "", "", nil}, testUserAgent, testAbsolutePath, true) + + if err != nil { + t.Errorf("Unexpected Error: %v", err) + } + + inURL, outURL := "/foo", fmt.Sprintf(defaultBaseURL+testAbsolutePath+"/foo", httpPrefix, "foo.com") + inBody := map[string]string{"name": "bar", "fullname": "foobar"} + outBody := map[string][]string{"name": {"bar"}, "fullname": {"foobar"}} + + req, _ := c.NewUnAuthFormEncodedRequest(context.TODO(), http.MethodPost, inURL, inBody) + + // test relative URL was expanded + if req.URL.String() != outURL { + t.Errorf("NewUnAuthFormEncodedRequest(%v) URL = %v, expected %v", inURL, req.URL, outURL) + } + + // test body + // Parse the body form data to a map structure which can be accessed by req.PostForm + req.ParseForm() + + // check form encoded key-values as compared to input values + if !reflect.DeepEqual(outBody, (map[string][]string)(req.PostForm)) { + t.Errorf("NewUnAuthFormEncodedRequest(%v) Form encoded k-v, got = %v, expected %v", inBody, req.PostForm, outBody) + } + + // test headers. Authorization header shouldn't exist + if _, ok := req.Header["Authorization"]; ok { + t.Errorf("Unexpected Authorization header obtained in request from NewUnAuthFormEncodedRequest()") + } + inHeaders := map[string]string{ + "Content-Type": formEncodedType, + "Accept": mediaType, + "User-Agent": testUserAgent, + } + for k, v := range req.Header { + if v[0] != inHeaders[k] { + t.Errorf("NewUnAuthFormEncodedRequest() Header value for %v = %v, expected %v", k, v[0], inHeaders[k]) + } + } +} + +func TestNewUnAuthUploadRequest(t *testing.T) { + c, err := NewClient(&Credentials{"foo.com", "username", "password", "", "", true, false, "", "", "", nil}, testUserAgent, testAbsolutePath, true) + + if err != nil { + t.Errorf("Unexpected Error: %v", err) + } + + inURL, outURL := "/foo", fmt.Sprintf(defaultBaseURL+testAbsolutePath+"/foo", httpPrefix, "foo.com") + inBody, _ := os.Open(fileName) + if err != nil { + t.Fatalf("Error opening fiele %v, error : %v", fileName, err) + } + + // expected body + out, _ := os.Open(fileName) + outBody, _ := ioutil.ReadAll(out) + + req, err := c.NewUnAuthUploadRequest(context.TODO(), http.MethodPost, inURL, inBody) + if err != nil { + t.Fatalf("NewUnAuthUploadRequest() errored out with error : %v", err.Error()) + } + // test relative URL was expanded + if req.URL.String() != outURL { + t.Errorf("NewUnAuthUploadRequest(%v) URL = %v, expected %v", inURL, req.URL, outURL) + } + + //test body contents + got, _ := ioutil.ReadAll(req.Body) + if !bytes.Equal(got, outBody) { + t.Errorf("NewUnAuthUploadRequest(%v) Body = %v, expected %v", inBody, string(got), string(outBody)) + } + + // test headers. Authorization header shouldn't exist + if _, ok := req.Header["Authorization"]; ok { + t.Errorf("Unexpected Authorization header obtained in request from NewUnAuthUploadRequest()") + } + inHeaders := map[string]string{ + "Content-Type": octetStreamType, + "Accept": mediaType, + } + for k, v := range inHeaders { + if v != req.Header[k][0] { + t.Errorf("NewUploadRequest() Header value for %v = %v, expected %v", k, req.Header[k][0], v) + } + } +} + func TestErrorResponse_Error(t *testing.T) { messageResource := MessageResource{Message: "This field may not be blank."} messageList := make([]MessageResource, 1) @@ -351,54 +543,6 @@ func TestClient_NewRequest(t *testing.T) { } } -func TestClient_NewUploadRequest(t *testing.T) { - type fields struct { - Credentials *Credentials - client *http.Client - BaseURL *url.URL - UserAgent string - onRequestCompleted RequestCompletionCallback - } - type args struct { - ctx context.Context - method string - urlStr string - file *os.File - } - - tests := []struct { - name string - fields fields - args args - want *http.Request - wantErr bool - }{ - // TODO: Add test cases. - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - c := &Client{ - Credentials: tt.fields.Credentials, - client: tt.fields.client, - BaseURL: tt.fields.BaseURL, - UserAgent: tt.fields.UserAgent, - onRequestCompleted: tt.fields.onRequestCompleted, - } - got, err := c.NewUploadRequest(tt.args.ctx, tt.args.method, tt.args.urlStr, tt.args.file) - if (err != nil) != tt.wantErr { - t.Errorf("Client.NewUploadRequest() error = %v, wantErr %v", err, tt.wantErr) - - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("Client.NewUploadRequest() = %v, want %v", got, tt.want) - } - }) - } -} - func TestClient_OnRequestCompleted(t *testing.T) { type fields struct { Credentials *Credentials diff --git a/client/foundation/foundation_api_test.go b/client/foundation/foundation_api_test.go new file mode 100644 index 000000000..dd4c073f9 --- /dev/null +++ b/client/foundation/foundation_api_test.go @@ -0,0 +1,52 @@ +package foundation + +import ( + "fmt" + "testing" + + "github.com/terraform-providers/terraform-provider-nutanix/client" +) + +func TestNewFoundationAPIClient(t *testing.T) { + // verifies positive client creation + cred := client.Credentials{ + URL: "foo.com", + Username: "username", + Password: "password", + Port: "", + Endpoint: "0.0.0.0", + Insecure: true, + FoundationEndpoint: "10.0.0.0", + FoundationPort: "8000", + RequiredFields: nil, + } + foundationClient, err := NewFoundationAPIClient(cred) + if err != nil { + t.Errorf(err.Error()) + } + outURL := fmt.Sprintf("http://%s:%s/", cred.FoundationEndpoint, cred.FoundationPort) + if foundationClient.client.BaseURL.String() != outURL { + t.Errorf("NewFoundationAPIClient(%v) BaseUrl in base client of foundation client = %v, expected %v", cred, foundationClient.client.BaseURL.String(), outURL) + } + + // verify missing client scenario + cred2 := client.Credentials{ + URL: "foo.com", + Username: "username", + Password: "password", + Port: "", + Endpoint: "0.0.0.0", + Insecure: true, + RequiredFields: map[string][]string{ + "foundation": {"foundation_endpoint"}, + }, + } + foundationClient2, err2 := NewFoundationAPIClient(cred2) + if err2 != nil { + t.Errorf(err2.Error()) + } + + if foundationClient2.client.ErrorMsg == "" { + t.Errorf("NewFoundationAPIClient(%v) expected the base client in foundation client to have some error message", cred2) + } +} diff --git a/client/foundation/foundation_file_management_service_test.go b/client/foundation/foundation_file_management_service_test.go new file mode 100644 index 000000000..f601ae35d --- /dev/null +++ b/client/foundation/foundation_file_management_service_test.go @@ -0,0 +1,194 @@ +package foundation + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "reflect" + "testing" + + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func TestFMOperations_ListNOSPackages(t *testing.T) { + mux, c, server := setup() + defer server.Close() + mux.HandleFunc("/foundation/enumerate_nos_packages", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodGet) + + // mock response + fmt.Fprintf(w, `[ + "package1", + "package2" + ]`) + }) + ctx := context.TODO() + + out := &ListNOSPackagesResponse{ + "package1", + "package2", + } + + op := FileManagementOperations{ + client: c, + } + + // checks + got, err := op.ListNOSPackages(ctx) + if err != nil { + t.Fatalf("FileManagementOperations.ListNOSPackages() error = %v", err) + } + if !reflect.DeepEqual(got, out) { + t.Errorf("FileManagementOperations.ListNOSPackages() got = %#v, want = %#v", got, out) + } +} + +func TestFMOperations_ListHypervisorISOs(t *testing.T) { + mux, c, server := setup() + defer server.Close() + mux.HandleFunc("/foundation/enumerate_hypervisor_isos", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodGet) + + // mock response + fmt.Fprintf(w, `{ + "hyperv": [{ + "filename": "hyperv1.iso", + "supported": true + }, + { + "filename": "hyperv2.iso", + "supported": false + } + ], + "kvm": [{ + "filename": "kvm1.iso", + "supported": true + }, + { + "filename": "kvm2.iso", + "supported": false + } + ] + }`) + }) + ctx := context.TODO() + + out := &ListHypervisorISOsResponse{ + Hyperv: []*HypervisorISOReference{ + { + Supported: utils.BoolPtr(true), + Filename: "hyperv1.iso", + }, + { + Supported: utils.BoolPtr(false), + Filename: "hyperv2.iso", + }, + }, + Kvm: []*HypervisorISOReference{ + { + Supported: utils.BoolPtr(true), + Filename: "kvm1.iso", + }, + { + Supported: utils.BoolPtr(false), + Filename: "kvm2.iso", + }, + }, + } + + op := FileManagementOperations{ + client: c, + } + + // checks + got, err := op.ListHypervisorISOs(ctx) + if err != nil { + t.Fatalf("FileManagementOperations.ListHypervisorISOs() error = %v", err) + } + if !reflect.DeepEqual(got, out) { + t.Errorf("FileManagementOperations.ListHypervisorISOs() got = %#v, want = %#v", got, out) + } +} + +func TestFMOperations_UploadImage(t *testing.T) { + mux, c, server := setup() + defer server.Close() + installerType := "kvm" + filename := "test_ahv.iso" + source := "foundation_api.go" + mux.HandleFunc("/foundation/upload", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + + expectedURL := fmt.Sprintf("/foundation/upload?installer_type=%v&filename=%v", installerType, filename) + if expectedURL != r.URL.String() { + t.Errorf("FileManagementOperations.UploadImage() expected URL %v, got %v", expectedURL, r.URL.String()) + } + + body, _ := ioutil.ReadAll(r.Body) + file, _ := ioutil.ReadFile(source) + + if !reflect.DeepEqual(body, file) { + t.Errorf("FileManagementOperations.UploadImage() error: different uploaded files") + } + + // mock response + fmt.Fprintf(w, `{ + "md5sum": "1234QAA", + "name": "/home/foundation/kvm/%v", + "in_whitelist": false + }`, filename) + }) + ctx := context.TODO() + + out := &UploadImageResponse{ + Md5Sum: "1234QAA", + Name: "/home/foundation/kvm/" + filename, + InWhitelist: false, + } + + op := FileManagementOperations{ + client: c, + } + + // checks + got, err := op.UploadImage(ctx, installerType, filename, source) + if err != nil { + t.Fatalf("FileManagementOperations.UploadImage() error = %v", err) + } + if !reflect.DeepEqual(got, out) { + t.Errorf("FileManagementOperations.UploadImage() got = %#v, want = %#v", got, out) + } +} + +func TestFMOperations_DeleteImage(t *testing.T) { + mux, c, server := setup() + defer server.Close() + installerType := "kvm" + filename := "test_ahv.iso" + mux.HandleFunc("/foundation/delete/", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + t.Fatalf("FileManagementOperations.DeleteImage() error reading request body = %v", err) + } + + // check form encoded body + expected := fmt.Sprintf("filename=%v&installer_type=%v", filename, installerType) + if string(body) != expected { + t.Errorf("FileManagementOperations.DeleteImage() request body expected = %v, got = %v", expected, string(body)) + } + }) + ctx := context.TODO() + + op := FileManagementOperations{ + client: c, + } + + // checks + err := op.DeleteImage(ctx, installerType, filename) + if err != nil { + t.Fatalf("FileManagementOperations.DeleteImage() error = %v", err) + } +} diff --git a/client/foundation/foundation_networking_service_test.go b/client/foundation/foundation_networking_service_test.go new file mode 100644 index 000000000..c4ccee992 --- /dev/null +++ b/client/foundation/foundation_networking_service_test.go @@ -0,0 +1,281 @@ +package foundation + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "reflect" + "testing" + + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func TestNtwOperations_DiscoverNodes(t *testing.T) { + mux, c, server := setup() + defer server.Close() + mux.HandleFunc("/foundation/discover_nodes", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodGet) + + // mock response + fmt.Fprintf(w, `[{ + "model": "XCV10", + "nodes": [{ + "node_position": "A", + "hypervisor": "kvm", + "svm_ip": "0.0.0.0", + "configured": true + }], + "block_id": "GMD1" + }, { + "model": "XCV10", + "nodes": [{ + "node_position": "A", + "hypervisor": "kvm", + "svm_ip": "0.0.0.0", + "configured": false + } + ], + "block_id": "GMD2" + }]`) + }) + ctx := context.TODO() + + out := &DiscoverNodesAPIResponse{ + { + Model: "XCV10", + Nodes: []DiscoveredNode{ + { + NodePosition: "A", + Hypervisor: "kvm", + SvmIP: "0.0.0.0", + Configured: utils.BoolPtr(true), + }, + }, + BlockID: "GMD1", + }, + { + Model: "XCV10", + Nodes: []DiscoveredNode{ + { + NodePosition: "A", + Hypervisor: "kvm", + SvmIP: "0.0.0.0", + Configured: utils.BoolPtr(false), + }, + }, + BlockID: "GMD2", + }, + } + + op := NetworkingOperations{ + client: c, + } + + // checks + got, err := op.DiscoverNodes(ctx) + if err != nil { + t.Fatalf("NetworkingOperations.DiscoverNodes() error = %v", err) + } + if !reflect.DeepEqual(got, out) { + t.Errorf("NetworkingOperations.DiscoverNodes() got = %#v, want = %#v", got, out) + } +} + +func TestNtwOperations_NodeNetworkDetails(t *testing.T) { + mux, c, server := setup() + defer server.Close() + mux.HandleFunc("/foundation/node_network_details", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + + expected := map[string]interface{}{ + "nodes": []interface{}{ + map[string]interface{}{ + "ipv6_address": "ffff::ffff:fffff:ffff", + }, + map[string]interface{}{ + "ipv6_address": "ec12::ec12:ec12:ec12", + }, + }, + "timeout": "30", + } + + // checks + var v map[string]interface{} + err := json.NewDecoder(r.Body).Decode(&v) + if err != nil { + t.Fatalf("decode json: %v", err) + } + + if !reflect.DeepEqual(v, expected) { + t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected) + } + + // mock response + fmt.Fprintf(w, `{ + "nodes" : [ + { + "cvm_ip" : "0.0.0.0", + "node_serial" : "NX1234", + "ipmi_ip" : "0.0.0.0" + }, + { + "cvm_ip" : "0.0.0.0", + "node_serial" : "NX1235", + "ipmi_ip" : "0.0.0.0" + } + ] + }`) + }) + ctx := context.TODO() + inp := &NodeNetworkDetailsInput{ + Nodes: []NodeIpv6Input{ + { + Ipv6Address: "ffff::ffff:fffff:ffff", + }, + { + Ipv6Address: "ec12::ec12:ec12:ec12", + }, + }, + Timeout: "30", + } + out := &NodeNetworkDetailsResponse{ + Nodes: []NodeNetworkDetail{ + { + CvmIP: "0.0.0.0", + NodeSerial: "NX1234", + IpmiIP: "0.0.0.0", + }, + { + CvmIP: "0.0.0.0", + NodeSerial: "NX1235", + IpmiIP: "0.0.0.0", + }, + }, + } + + op := NetworkingOperations{ + client: c, + } + + // checks + got, err := op.NodeNetworkDetails(ctx, inp) + if err != nil { + t.Fatalf("NetworkingOperations.NodeNetworkDetails() error = %v", err) + } + if !reflect.DeepEqual(got, out) { + t.Errorf("NetworkingOperations.NodeNetworkDetails() got = %#v, want = %#v", got, out) + } +} + +func TestNtwOperations_ConfigureIPMI(t *testing.T) { + mux, c, server := setup() + defer server.Close() + mux.HandleFunc("/foundation/ipmi_config", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + + expected := map[string]interface{}{ + "blocks": []interface{}{ + map[string]interface{}{ + "nodes": []interface{}{ + map[string]interface{}{ + "ipmi_ip": "0.0.0.0", + "ipmi_mac": "ac:da:af:fa:af:fa", + "ipmi_configure_now": true, + }, + }, + "block_id": "GMD10", + }, + }, + "ipmi_netmask": "255.255.255.0", + "ipmi_gateway": "0.0.0.0", + "ipmi_user": "username", + "ipmi_password": "password", + } + + // checks + var v map[string]interface{} + err := json.NewDecoder(r.Body).Decode(&v) + if err != nil { + t.Fatalf("decode json: %v", err) + } + + if !reflect.DeepEqual(v, expected) { + t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected) + } + + // mock response + fmt.Fprintf(w, `{ + "blocks": [ + { + "nodes":[ + { + "ipmi_ip": "0.0.0.0", + "ipmi_mac": "ac:da:af:fa:af:fa", + "ipmi_configure_now": true, + "ipmi_configure_successful": true, + "ipmi_message" : "success" + } + ], + "block_id": "GMD10" + } + ], + "ipmi_netmask": "255.255.255.0", + "ipmi_gateway": "0.0.0.0", + "ipmi_user": "username", + "ipmi_password": "password" + }`) + }) + ctx := context.TODO() + inp := &IPMIConfigAPIInput{ + IpmiUser: "username", + IpmiPassword: "password", + IpmiNetmask: "255.255.255.0", + IpmiGateway: "0.0.0.0", + Blocks: []IPMIConfigBlockInput{ + { + Nodes: []IPMIConfigNodeInput{ + { + IpmiIP: "0.0.0.0", + IpmiMac: "ac:da:af:fa:af:fa", + IpmiConfigureNow: true, + }, + }, + BlockID: "GMD10", + }, + }, + } + out := &IPMIConfigAPIResponse{ + IpmiUser: "username", + IpmiPassword: "password", + IpmiNetmask: "255.255.255.0", + IpmiGateway: "0.0.0.0", + Blocks: []IPMIConfigBlockResponse{ + { + Nodes: []IPMIConfigNodeResponse{ + { + IpmiIP: "0.0.0.0", + IpmiMac: "ac:da:af:fa:af:fa", + IpmiConfigureNow: true, + IpmiConfigureSuccessful: true, + IpmiMessage: "success", + }, + }, + BlockID: "GMD10", + }, + }, + } + + op := NetworkingOperations{ + client: c, + } + + // checks + got, err := op.ConfigureIPMI(ctx, inp) + if err != nil { + t.Fatalf("NetworkingOperations.ConfigureIPMI() error = %v", err) + } + if !reflect.DeepEqual(got, out) { + t.Errorf("NetworkingOperations.ConfigureIPMI() got = %#v, want = %#v", got, out) + } +} diff --git a/client/foundation/foundation_node_imaging_service_test.go b/client/foundation/foundation_node_imaging_service_test.go new file mode 100644 index 000000000..e2b9296b5 --- /dev/null +++ b/client/foundation/foundation_node_imaging_service_test.go @@ -0,0 +1,222 @@ +package foundation + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "reflect" + "testing" + + "github.com/terraform-providers/terraform-provider-nutanix/client" + "github.com/terraform-providers/terraform-provider-nutanix/utils" +) + +func setup() (*http.ServeMux, *client.Client, *httptest.Server) { + mux := http.NewServeMux() + server := httptest.NewServer(mux) + c, _ := client.NewBaseClient(&client.Credentials{ + URL: "", + Username: "username", + Password: "password", + Port: "", + Endpoint: "0.0.0.0", + Insecure: true}, + absolutePath, + true) + c.UserAgent = userAgent + c.BaseURL, _ = url.Parse(server.URL) + + return mux, c, server +} + +func testHTTPMethod(t *testing.T, r *http.Request, expected string) { + if expected != r.Method { + t.Errorf("Request method = %v, expected %v", r.Method, expected) + } +} + +func TestNodeImagingOperations_ImageNodes(t *testing.T) { + mux, c, server := setup() + defer server.Close() + + mux.HandleFunc("/foundation/image_nodes", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodPost) + + expected := map[string]interface{}{ + "ipmi_password": "test_password", + "ipmi_user": "test_user", + "cvm_gateway": "0.0.0.0", + "cvm_netmask": "255.255.255.0", + "hypervisor_gateway": "0.0.0.0", + "hypervisor_netmask": "255.255.255.0", + "nos_package": "test_nos.tar.gz", + "hypervisor_iso": map[string]interface{}{}, + "blocks": []interface{}{ + map[string]interface{}{ + "block_id": "N123", + "nodes": []interface{}{ + map[string]interface{}{ + "ipmi_configure_now": true, + "ipmi_ip": "0.0.0.0", + "cvm_ip": "0.0.0.0", + "hypervisor_ip": "0.0.0.0", + "image_now": true, + "ipmi_password": "test_password", + "ipmi_user": "test_user", + "hypervisor_hostname": "test_hostname", + "hypervisor": "kvm", + "node_position": "A", + }, + }, + }, + }, + "clusters": []interface{}{ + map[string]interface{}{ + "redundancy_factor": float64(1), + "cluster_init_now": true, + "cluster_external_ip": nil, + "cluster_name": "test_cluster", + "cluster_members": []interface{}{"0.0.0.0"}, + }, + }, + } + + // checks + var v map[string]interface{} + err := json.NewDecoder(r.Body).Decode(&v) + if err != nil { + t.Fatalf("decode json: %v", err) + } + if !reflect.DeepEqual(v, expected) { + t.Errorf("Request body\n got=%#v\nwant=%#v", v, expected) + } + + // mock response + fmt.Fprintf(w, `{ + "session_id" : "123456-1234-123456" + }`) + }) + ctx := context.TODO() + inp := &ImageNodesInput{ + IpmiPassword: "test_password", + IpmiUser: "test_user", + CvmGateway: "0.0.0.0", + CvmNetmask: "255.255.255.0", + HypervisorGateway: "0.0.0.0", + HypervisorNetmask: "255.255.255.0", + NosPackage: "test_nos.tar.gz", + Blocks: []*Block{ + { + BlockID: "N123", + Nodes: []*Node{ + { + IpmiConfigureNow: utils.BoolPtr(true), + IpmiIP: "0.0.0.0", + IpmiUser: "test_user", + IpmiPassword: "test_password", + CvmIP: "0.0.0.0", + ImageNow: utils.BoolPtr(true), + HypervisorIP: "0.0.0.0", + HypervisorHostname: "test_hostname", + Hypervisor: "kvm", + NodePosition: "A", + }, + }, + }, + }, + Clusters: []*Clusters{ + { + RedundancyFactor: utils.Int64Ptr(1), + ClusterInitNow: utils.BoolPtr(true), + ClusterName: "test_cluster", + ClusterMembers: []string{"0.0.0.0"}, + }, + }, + } + + out := &ImageNodesAPIResponse{ + SessionID: "123456-1234-123456", + } + + op := NodeImagingOperations{ + client: c, + } + + // checks + got, err := op.ImageNodes(ctx, inp) + if err != nil { + t.Fatalf("NodeImagingOperations.ImageNodes() error = %v", err) + } + if !reflect.DeepEqual(got, out) { + t.Errorf("NodeImagingOperations.ImageNodes() got = %#v, want = %#v", got, out) + } +} + +func TestNodeImagingOperations_ImageNodesProgress(t *testing.T) { + mux, c, server := setup() + defer server.Close() + sessionID := "123456-1234-123456" + mux.HandleFunc("/foundation/progress", func(w http.ResponseWriter, r *http.Request) { + testHTTPMethod(t, r, http.MethodGet) + + // mock response + fmt.Fprintf(w, `{ + "session_id": "%v", + "imaging_stopped": true, + "aggregate_percent_complete": 100.00, + "clusters": [{ + "cluster_name": "test_cluster", + "time_elapsed": 102.33, + "cluster_members": [ + "0.0.0.0" + ], + "percent_complete": 100.00 + }], + "nodes": [{ + "cvm_ip": "0.0.0.0", + "hypervisor_ip": "0.0.0.0", + "time_elapsed": 102.33, + "percent_complete": 100.00 + }] + }`, sessionID) + }) + ctx := context.TODO() + + out := &ImageNodesProgressResponse{ + SessionID: "123456-1234-123456", + ImagingStopped: utils.BoolPtr(true), + AggregatePercentComplete: utils.Float64Ptr(100.00), + Clusters: []*ClusterProgress{ + { + ClusterName: "test_cluster", + TimeElapsed: utils.Float64Ptr(102.33), + ClusterMembers: []string{"0.0.0.0"}, + PercentComplete: utils.Float64Ptr(100.00), + }, + }, + Nodes: []*NodeProgress{ + { + CvmIP: "0.0.0.0", + HypervisorIP: "0.0.0.0", + TimeElapsed: utils.Float64Ptr(102.33), + PercentComplete: utils.Float64Ptr(100.00), + }, + }, + } + + op := NodeImagingOperations{ + client: c, + } + + // checks + got, err := op.ImageNodesProgress(ctx, sessionID) + if err != nil { + t.Fatalf("NodeImagingOperations.ImageNodesProgress() error = %v", err) + } + if !reflect.DeepEqual(got, out) { + t.Errorf("NodeImagingOperations.ImageNodesProgress() got = %#v, want = %#v", got, out) + } +} diff --git a/client/foundation/foundation_structs.go b/client/foundation/foundation_structs.go index 6b863024f..a51b2e77e 100644 --- a/client/foundation/foundation_structs.go +++ b/client/foundation/foundation_structs.go @@ -70,7 +70,7 @@ type FcSettings struct { type Clusters struct { EnableNs *bool `json:"enable_ns,omitempty"` BackplaneSubnet string `json:"backplane_subnet,omitempty"` - ClusterInitSuccessful *bool `json:"cluster_init_successful"` + ClusterInitSuccessful *bool `json:"cluster_init_successful,omitempty"` BackplaneNetmask string `json:"backplane_netmask,omitempty"` RedundancyFactor *int64 `json:"redundancy_factor"` BackplaneVlan string `json:"backplane_vlan,omitempty"` diff --git a/client/karbon/karbon_api_test.go b/client/karbon/karbon_api_test.go new file mode 100644 index 000000000..7d299b140 --- /dev/null +++ b/client/karbon/karbon_api_test.go @@ -0,0 +1,43 @@ +package karbon + +import ( + "testing" + + "github.com/terraform-providers/terraform-provider-nutanix/client" +) + +func TestNewKarbonAPIClient(t *testing.T) { + // verifies positive client creation + cred := client.Credentials{ + URL: "foo.com", + Username: "username", + Password: "password", + Port: "", + Endpoint: "0.0.0.0", + Insecure: true, + FoundationEndpoint: "10.0.0.0", + FoundationPort: "8000", + RequiredFields: nil, + } + _, err := NewKarbonAPIClient(cred) + if err != nil { + t.Errorf(err.Error()) + } + + // verify missing client scenario + cred2 := client.Credentials{ + URL: "foo.com", + Insecure: true, + RequiredFields: map[string][]string{ + "karbon": {"username", "password", "endpoint"}, + }, + } + v3Client2, err2 := NewKarbonAPIClient(cred2) + if err2 != nil { + t.Errorf(err2.Error()) + } + + if v3Client2.client.ErrorMsg == "" { + t.Errorf("NewKarbonAPIClient(%v) expected the base client in karbon client to have some error message", cred2) + } +} diff --git a/client/v3/v3_service_test.go b/client/v3/v3_service_test.go index b8c915e34..d7350f64a 100644 --- a/client/v3/v3_service_test.go +++ b/client/v3/v3_service_test.go @@ -1003,9 +1003,9 @@ func TestOperations_UploadImage(t *testing.T) { testHTTPMethod(t, r, http.MethodPut) bodyBytes, _ := ioutil.ReadAll(r.Body) - file, _ := ioutil.ReadFile("/v3.go") + file, _ := ioutil.ReadFile("v3.go") - if reflect.DeepEqual(bodyBytes, file) { + if !reflect.DeepEqual(bodyBytes, file) { t.Errorf("Operations.UploadImage() error: different uploaded files") } }) @@ -1027,7 +1027,7 @@ func TestOperations_UploadImage(t *testing.T) { { "TestOperations_UploadImage Upload Image", fields{c}, - args{"cfde831a-4e87-4a75-960f-89b0148aa2cc", "./v3.go"}, + args{"cfde831a-4e87-4a75-960f-89b0148aa2cc", "v3.go"}, }, } diff --git a/client/v3/v3_test.go b/client/v3/v3_test.go index dca654a9a..fd2b58862 100644 --- a/client/v3/v3_test.go +++ b/client/v3/v3_test.go @@ -1,54 +1,43 @@ package v3 import ( - "reflect" "testing" "github.com/terraform-providers/terraform-provider-nutanix/client" ) func TestNewV3Client(t *testing.T) { - cred := client.Credentials{URL: "foo.com", Username: "username", Password: "password", Port: "", Endpoint: "0.0.0.0", Insecure: true} - c, _ := NewV3Client(cred) - - cred2 := client.Credentials{URL: "^^^", Username: "username", Password: "password", Port: "", Endpoint: "0.0.0.0", Insecure: true} - c2, _ := NewV3Client(cred2) - - type args struct { - credentials client.Credentials + // verifies positive client creation + cred := client.Credentials{ + URL: "foo.com", + Username: "username", + Password: "password", + Port: "", + Endpoint: "0.0.0.0", + Insecure: true, + FoundationEndpoint: "10.0.0.0", + FoundationPort: "8000", + RequiredFields: nil, + } + _, err := NewV3Client(cred) + if err != nil { + t.Errorf(err.Error()) } - tests := []struct { - name string - args args - want *Client - wantErr bool - }{ - { - "test one", - args{cred}, - c, - false, - }, - { - "test one", - args{cred2}, - c2, - true, + // verify missing client scenario + cred2 := client.Credentials{ + URL: "foo.com", + Insecure: true, + RequiredFields: map[string][]string{ + "prism_central": {"username", "password", "endpoint"}, }, } + v3Client2, err2 := NewV3Client(cred2) + if err2 != nil { + t.Errorf(err2.Error()) + } - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - got, err := NewV3Client(tt.args.credentials) - if (err != nil) != tt.wantErr { - t.Errorf("NewV3Client() error = %v, wantErr %v", err, tt.wantErr) - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("NewV3Client() = %v, want %v", got, tt.want) - } - }) + if v3Client2.client.ErrorMsg == "" { + t.Errorf("NewV3Client(%v) expected the base client in v3 client to have some error message", cred2) } }