diff --git a/go.mod b/go.mod index dc0d7e2..8e08394 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,14 @@ go 1.21.1 require ( github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.6.8 // indirect - golang.org/x/net v0.17.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 github.com/golang/mock v1.6.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/stretchr/testify v1.8.4 // indirect golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.18.0 // indirect golang.org/x/sys v0.14.0 // indirect golang.org/x/tools v0.15.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect diff --git a/go.sum b/go.sum index 6b4a8fa..0be2f8a 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,11 @@ github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.6.8 h1:rwRvTaHutTdU/cacsKeY6E6yWwnSXCzRbPZaCOSfCks= github.com/NaverCloudPlatform/ncloud-sdk-go-v2 v1.6.8/go.mod h1:jRp8KZ64MUevBWNqehghhG2oF5/JU3Dmt/Cu7dp1mQE= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU= +github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -17,29 +18,42 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -53,10 +67,13 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/mocks/mock_NetworkInterface.go b/mocks/mock_NetworkInterface.go new file mode 100644 index 0000000..e6040d7 --- /dev/null +++ b/mocks/mock_NetworkInterface.go @@ -0,0 +1,94 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + + types "github.com/cloud-club/Aviator-service/types/server" +) + +// MockNetworkInterface is an autogenerated mock type for the NetworkInterface type +type MockNetworkInterface struct { + mock.Mock +} + +type MockNetworkInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *MockNetworkInterface) EXPECT() *MockNetworkInterface_Expecter { + return &MockNetworkInterface_Expecter{mock: &_m.Mock} +} + +// Get provides a mock function with given fields: url +func (_m *MockNetworkInterface) Get(url string) (*types.NetworkInterfaceList, error) { + ret := _m.Called(url) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 *types.NetworkInterfaceList + var r1 error + if rf, ok := ret.Get(0).(func(string) (*types.NetworkInterfaceList, error)); ok { + return rf(url) + } + if rf, ok := ret.Get(0).(func(string) *types.NetworkInterfaceList); ok { + r0 = rf(url) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.NetworkInterfaceList) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(url) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockNetworkInterface_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type MockNetworkInterface_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - url string +func (_e *MockNetworkInterface_Expecter) Get(url interface{}) *MockNetworkInterface_Get_Call { + return &MockNetworkInterface_Get_Call{Call: _e.mock.On("Get", url)} +} + +func (_c *MockNetworkInterface_Get_Call) Run(run func(url string)) *MockNetworkInterface_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockNetworkInterface_Get_Call) Return(_a0 *types.NetworkInterfaceList, _a1 error) *MockNetworkInterface_Get_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockNetworkInterface_Get_Call) RunAndReturn(run func(string) (*types.NetworkInterfaceList, error)) *MockNetworkInterface_Get_Call { + _c.Call.Return(run) + return _c +} + +// NewMockNetworkInterface creates a new instance of MockNetworkInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockNetworkInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *MockNetworkInterface { + mock := &MockNetworkInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/mocks/mock_ServerInterface.go b/mocks/mock_ServerInterface.go index 2d8d5ce..bc2b3fc 100644 --- a/mocks/mock_ServerInterface.go +++ b/mocks/mock_ServerInterface.go @@ -2,7 +2,11 @@ package mocks -import mock "github.com/stretchr/testify/mock" +import ( + mock "github.com/stretchr/testify/mock" + + types "github.com/cloud-club/Aviator-service/types/server" +) // MockServerInterface is an autogenerated mock type for the ServerInterface type type MockServerInterface struct { @@ -17,22 +21,34 @@ func (_m *MockServerInterface) EXPECT() *MockServerInterface_Expecter { return &MockServerInterface_Expecter{mock: &_m.Mock} } -// Create provides a mock function with given fields: url, payload -func (_m *MockServerInterface) Create(url string, payload interface{}) error { - ret := _m.Called(url, payload) +// Create provides a mock function with given fields: url, request, networkInterfaceIndex +func (_m *MockServerInterface) Create(url string, request *types.CreateServerRequest, networkInterfaceIndex int) (*types.CreateServerResponse, error) { + ret := _m.Called(url, request, networkInterfaceIndex) if len(ret) == 0 { panic("no return value specified for Create") } - var r0 error - if rf, ok := ret.Get(0).(func(string, interface{}) error); ok { - r0 = rf(url, payload) + var r0 *types.CreateServerResponse + var r1 error + if rf, ok := ret.Get(0).(func(string, *types.CreateServerRequest, int) (*types.CreateServerResponse, error)); ok { + return rf(url, request, networkInterfaceIndex) + } + if rf, ok := ret.Get(0).(func(string, *types.CreateServerRequest, int) *types.CreateServerResponse); ok { + r0 = rf(url, request, networkInterfaceIndex) } else { - r0 = ret.Error(0) + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.CreateServerResponse) + } } - return r0 + if rf, ok := ret.Get(1).(func(string, *types.CreateServerRequest, int) error); ok { + r1 = rf(url, request, networkInterfaceIndex) + } else { + r1 = ret.Error(1) + } + + return r0, r1 } // MockServerInterface_Create_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Create' @@ -42,24 +58,25 @@ type MockServerInterface_Create_Call struct { // Create is a helper method to define mock.On call // - url string -// - payload interface{} -func (_e *MockServerInterface_Expecter) Create(url interface{}, payload interface{}) *MockServerInterface_Create_Call { - return &MockServerInterface_Create_Call{Call: _e.mock.On("Create", url, payload)} +// - request *types.CreateServerRequest +// - networkInterfaceIndex int +func (_e *MockServerInterface_Expecter) Create(url interface{}, request interface{}, networkInterfaceIndex interface{}) *MockServerInterface_Create_Call { + return &MockServerInterface_Create_Call{Call: _e.mock.On("Create", url, request, networkInterfaceIndex)} } -func (_c *MockServerInterface_Create_Call) Run(run func(url string, payload interface{})) *MockServerInterface_Create_Call { +func (_c *MockServerInterface_Create_Call) Run(run func(url string, request *types.CreateServerRequest, networkInterfaceIndex int)) *MockServerInterface_Create_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(interface{})) + run(args[0].(string), args[1].(*types.CreateServerRequest), args[2].(int)) }) return _c } -func (_c *MockServerInterface_Create_Call) Return(_a0 error) *MockServerInterface_Create_Call { - _c.Call.Return(_a0) +func (_c *MockServerInterface_Create_Call) Return(_a0 *types.CreateServerResponse, _a1 error) *MockServerInterface_Create_Call { + _c.Call.Return(_a0, _a1) return _c } -func (_c *MockServerInterface_Create_Call) RunAndReturn(run func(string, interface{}) error) *MockServerInterface_Create_Call { +func (_c *MockServerInterface_Create_Call) RunAndReturn(run func(string, *types.CreateServerRequest, int) (*types.CreateServerResponse, error)) *MockServerInterface_Create_Call { _c.Call.Return(run) return _c } diff --git a/mocks/mock_SubnetInterface.go b/mocks/mock_SubnetInterface.go new file mode 100644 index 0000000..eb44bfd --- /dev/null +++ b/mocks/mock_SubnetInterface.go @@ -0,0 +1,94 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + + types "github.com/cloud-club/Aviator-service/types/server" +) + +// MockSubnetInterface is an autogenerated mock type for the SubnetInterface type +type MockSubnetInterface struct { + mock.Mock +} + +type MockSubnetInterface_Expecter struct { + mock *mock.Mock +} + +func (_m *MockSubnetInterface) EXPECT() *MockSubnetInterface_Expecter { + return &MockSubnetInterface_Expecter{mock: &_m.Mock} +} + +// Get provides a mock function with given fields: url +func (_m *MockSubnetInterface) Get(url string) (*types.SubnetList, error) { + ret := _m.Called(url) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 *types.SubnetList + var r1 error + if rf, ok := ret.Get(0).(func(string) (*types.SubnetList, error)); ok { + return rf(url) + } + if rf, ok := ret.Get(0).(func(string) *types.SubnetList); ok { + r0 = rf(url) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*types.SubnetList) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(url) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockSubnetInterface_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type MockSubnetInterface_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - url string +func (_e *MockSubnetInterface_Expecter) Get(url interface{}) *MockSubnetInterface_Get_Call { + return &MockSubnetInterface_Get_Call{Call: _e.mock.On("Get", url)} +} + +func (_c *MockSubnetInterface_Get_Call) Run(run func(url string)) *MockSubnetInterface_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string)) + }) + return _c +} + +func (_c *MockSubnetInterface_Get_Call) Return(_a0 *types.SubnetList, _a1 error) *MockSubnetInterface_Get_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockSubnetInterface_Get_Call) RunAndReturn(run func(string) (*types.SubnetList, error)) *MockSubnetInterface_Get_Call { + _c.Call.Return(run) + return _c +} + +// NewMockSubnetInterface creates a new instance of MockSubnetInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockSubnetInterface(t interface { + mock.TestingT + Cleanup(func()) +}) *MockSubnetInterface { + mock := &MockSubnetInterface{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/accesscontrolgroup.go b/pkg/accesscontrolgroup.go new file mode 100644 index 0000000..a14a401 --- /dev/null +++ b/pkg/accesscontrolgroup.go @@ -0,0 +1,65 @@ +package pkg + +import ( + "fmt" + "io" + "log" + "net/http" + + serverType "github.com/cloud-club/Aviator-service/types/server" +) + +type AccessControlGroupService struct{} + +func NewAccessControlGroupService() AccessControlGroupInterface { + return &AccessControlGroupService{} +} + +type AccessControlGroupInterface interface { + Get(url string) (*serverType.AccessControlGroupList, error) +} + +func (acg *AccessControlGroupService) Get(url string) (*serverType.AccessControlGroupList, error) { + // Set url with query parameters + // However, there is no must required query parameters for this API, so we will comment this line right now. + //requestParams := serverType.CreateRequestString(request) + + // Create an HTTP request + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return nil, err + } + + // Set HTTP header for NCP authorization + SetNCPHeader(req, "YnCbljlTZfqRkFOXighj", "bDoQVXDGLJ9BhzpOYxnjSyxNB97dohAUPLeiQC0D") + + // Make the HTTP request + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + // Check the response status + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected response status: %s", resp.Status) + } + + // Read the response body + responseByteData, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + return nil, err + } + + // Convert the response body to the struct + var acgl *serverType.AccessControlGroupList + responseInterface, err := serverType.MapResponse(responseByteData, &acgl) + if err != nil { + return nil, err + } + + // interface{} to *serverType.AccessControlGroupList + responseStruct := responseInterface.(**serverType.AccessControlGroupList) + + return *responseStruct, nil +} diff --git a/pkg/constant.go b/pkg/constant.go index c1caffe..72e6a24 100644 --- a/pkg/constant.go +++ b/pkg/constant.go @@ -1 +1,7 @@ package pkg + +var API_URL = "https://ncloud.apigw.ntruss.com/vserver/v2/" +var CREATE_SERVER_INSTANCE_PATH = "createServerInstances" +var GET_NETWORKINTERFACE_LIST_PATH = "getNetworkInterfaceList" +var GET_SUBNET_LIST_PATH = "getSubnetList" +var GET_ACG_LIST_PATH = "getAccessControlGroupList" diff --git a/pkg/ncp.go b/pkg/ncp.go index fae63c6..b153a40 100644 --- a/pkg/ncp.go +++ b/pkg/ncp.go @@ -2,9 +2,10 @@ package pkg import ( "errors" + "time" + "github.com/cloud-club/Aviator-service/types/auth" "github.com/dgrijalva/jwt-go/v4" - "time" ) const ( @@ -13,8 +14,11 @@ const ( ) type NcpService struct { - token string - Server ServerInterface + token string + Server ServerInterface + Network NetworkInterface + Subnet SubnetInterface + AccessControlGroup AccessControlGroupInterface } func NewNcpService(token string) *NcpService { diff --git a/pkg/networkInterface.go b/pkg/networkInterface.go new file mode 100644 index 0000000..8572fc8 --- /dev/null +++ b/pkg/networkInterface.go @@ -0,0 +1,65 @@ +package pkg + +import ( + "fmt" + "io" + "log" + "net/http" + + serverType "github.com/cloud-club/Aviator-service/types/server" +) + +type NetworkInterfaceService struct{} + +func NewNetworkInterfaceService() NetworkInterface { + return &NetworkInterfaceService{} +} + +type NetworkInterface interface { + Get(url string) (*serverType.NetworkInterfaceList, error) +} + +func (networkInterface *NetworkInterfaceService) Get(url string) (*serverType.NetworkInterfaceList, error) { + // Set url with query parameters + // However, there is no must required query parameters for this API, so we will comment this line right now. + //requestParams := serverType.CreateRequestString(request) + + // Create an HTTP request + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return nil, err + } + + // Set HTTP header for NCP authorization + SetNCPHeader(req, "YnCbljlTZfqRkFOXighj", "bDoQVXDGLJ9BhzpOYxnjSyxNB97dohAUPLeiQC0D") + + // Make the HTTP request + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + // Check the response status + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected response status: %s", resp.Status) + } + + // Read the response body + responseByteData, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + return nil, err + } + + // Convert the response body to the struct + var nifl *serverType.NetworkInterfaceList + responseInterface, err := serverType.MapResponse(responseByteData, &nifl) + if err != nil { + return nil, err + } + + // interface{} to *serverType.NetworkInterfaceList + responseStruct := responseInterface.(**serverType.NetworkInterfaceList) + + return *responseStruct, nil +} diff --git a/pkg/networkInterface_test.go b/pkg/networkInterface_test.go new file mode 100644 index 0000000..7263ce9 --- /dev/null +++ b/pkg/networkInterface_test.go @@ -0,0 +1,64 @@ +package pkg_test + +import ( + "testing" + + "github.com/cloud-club/Aviator-service/mocks" + "github.com/cloud-club/Aviator-service/pkg" + server "github.com/cloud-club/Aviator-service/types/server" + "github.com/stretchr/testify/assert" +) + +func TestGetNetworkInterfaceList(t *testing.T) { + testCases := []struct { + testName string + url string + response *server.NetworkInterfaceList + err error + }{ + { + testName: "Success - 성공", + url: pkg.API_URL + pkg.GET_NETWORKINTERFACE_LIST_PATH, + response: &server.NetworkInterfaceList{ + TotalRows: 2, + NetworkInterfaceList: []server.NetworkInterface{ + { + NetworkInterfaceNo: "1", + NetworkInterfaceName: "test-01", + SubnetNo: "1", + DeleteOnTermination: true, + IsDefault: true, + NetworkInterfaceStatus: server.CommonCode{Code: "code", CodeName: "codeName"}, + IP: "10.0.0.0", + MacAddress: "00:00:00:00:00:00", + }, + { + NetworkInterfaceNo: "2", + NetworkInterfaceName: "test-02", + SubnetNo: "2", + DeleteOnTermination: true, + IsDefault: true, + NetworkInterfaceStatus: server.CommonCode{Code: "code", CodeName: "codeName"}, + IP: "10.0.0.1", + MacAddress: "00:00:00:00:00:00", + }, + }, + }, + err: nil, + }, + } + + for _, tc := range testCases { + t.Helper() + t.Run(tc.testName, func(t *testing.T) { + mockServer := &mocks.MockNetworkInterface{} + mockServer.On("Get", tc.url).Return(tc.response, tc.err).Once() + + response, err := mockServer.Get(tc.url) + + assert.Equal(t, tc.response, response) + assert.Nil(t, err) + mockServer.AssertExpectations(t) + }) + } +} diff --git a/pkg/server.go b/pkg/server.go index bf43cd8..4af72c6 100644 --- a/pkg/server.go +++ b/pkg/server.go @@ -3,7 +3,11 @@ package pkg import ( "errors" "fmt" + "io" + "log" "net/http" + + serverType "github.com/cloud-club/Aviator-service/types/server" ) type ServerService struct { @@ -14,7 +18,7 @@ type ServerService struct { type ServerInterface interface { Get(url string) error List(url string) error - Create(url string, payload interface{}) error + Create(url string, request *serverType.CreateServerRequest) (*serverType.CreateServerResponse, error) Update(url string) error Delete(url string) error } @@ -27,8 +31,45 @@ func (server *ServerService) GetToken() string { return server.token } -func (server *ServerService) Create(url string, payload interface{}) error { - return nil +func (server *ServerService) Create(url string, request *serverType.CreateServerRequest) (*serverType.CreateServerResponse, error) { + // Set url with query parameters + requestParams := serverType.CreateRequestString(request) + + // Create an HTTP request + req, err := http.NewRequest(http.MethodGet, url+requestParams, nil) + if err != nil { + return nil, err + } + // Set HTTP header for NCP authorization + SetNCPHeader(req, "45x3qDmooHFxwJywHbbK", "xUFTKEw2POsYl5AgBSxf4K2ZJm1JHJ51KHN5BDK8") + + // Make the HTTP request + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + // Check the response status + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected response status: %s", resp.Status) + } + + responseByteData, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + return nil, err + } + + var csr *serverType.CreateServerResponse + responseInterface, err := serverType.MapResponse(responseByteData, &csr) + if err != nil { + log.Fatal(err) + return nil, err + } + + // interface{} 타입으로 변환된 responseInterface를 다시 CreateServerResponse 타입으로 변환 + responseStruct := responseInterface.(*serverType.CreateServerResponse) + + return responseStruct, err } func (server *ServerService) Get(url string) error { diff --git a/pkg/server_test.go b/pkg/server_test.go index adb682d..428b1eb 100644 --- a/pkg/server_test.go +++ b/pkg/server_test.go @@ -1,8 +1,13 @@ package pkg_test import ( - "github.com/cloud-club/Aviator-service/pkg" "testing" + "time" + + "github.com/cloud-club/Aviator-service/mocks" + "github.com/cloud-club/Aviator-service/pkg" + server "github.com/cloud-club/Aviator-service/types/server" + "github.com/stretchr/testify/assert" ) func TestList(t *testing.T) { @@ -50,3 +55,90 @@ func TestList(t *testing.T) { } } + +func TestCreate(t *testing.T) { + testCases := []struct { + testName string + url string + request *server.CreateServerRequest + networkInterfaceIndex int + response *server.CreateServerResponse + err error + }{ + { + testName: "Success - 성공", + url: pkg.API_URL + pkg.CREATE_SERVER_INSTANCE_PATH, + request: &server.CreateServerRequest{ + ServerImageProductCode: "SW.VSVR.OS.LNX64.CNTOS.0703.B050", + VpcNo: "***04", + SubnetNo: "***43", + NetworkInterfaceOrder: 1, + }, + networkInterfaceIndex: 0, + response: &server.CreateServerResponse{ + RequestId: "e7e7e7e7-7e7e-7e7e-7e7e-7e7e7e7e7e7e", + ReturnCode: 0, + ReturnMessage: "success", + TotalRows: 1, + ServerInstanceList: []server.ServerInstance{ + { + ServerInstanceNo: "***4299", + ServerName: "test-***", + CpuCount: 2, + MemorySize: 4294967296, + PlatformType: server.CommonCode{Code: "LNX64", CodeName: "Linux 64 Bit"}, + LoginKeyName: "test-***", + ServerInstanceStatus: server.CommonCode{Code: "INIT", CodeName: "Server initializing"}, + ServerInstanceOperation: server.CommonCode{Code: "NULL", CodeName: "Server operation null"}, + ServerInstanceStatusName: "init", + CreateDate: time.Time{}, + Uptime: time.Time{}, + ServerImageProductCode: "SW.VSVR.OS.LNX64.CNTOS.0703.B050", + ServerProductCode: "SVR.VSVR.STAND.C002.M004.NET.SSD.B050.G001", + IsProtectServerTermination: false, + ZoneCode: "KR-1", + RegionCode: "KR", + VpcNo: "***04", + SubnetNo: "***43", + NetworkInterfaceNoList: server.NetworkInterfaceNoList{}, + ServerInstanceType: server.CommonCode{Code: "SVRSTAND", CodeName: "Server Standard"}, + BaseBlockStorageDiskType: server.CommonCode{Code: "NET", CodeName: "Network Storage"}, + BaseBlockStorageDiskDetailType: server.CommonCode{Code: "SSD", CodeName: "SSD"}, + }, + }, + }, + err: nil, + }, + { + testName: "Failed - 필수 파라미터 누락", + url: pkg.API_URL + pkg.CREATE_SERVER_INSTANCE_PATH, + request: &server.CreateServerRequest{ + VpcNo: "***04", + SubnetNo: "***43", + }, + networkInterfaceIndex: 0, + response: &server.CreateServerResponse{ + RequestId: "e7e7e7e7-7e7e-7e7e-7e7e-7e7e7e7e7e7e", + ReturnCode: 0, + ReturnMessage: "Failed", + }, + err: nil, + }, + } + + for _, tc := range testCases { + t.Helper() + t.Run(tc.testName, func(t *testing.T) { + mockServer := &mocks.MockServerInterface{} + mockServer.On("Create", tc.url, tc.request, tc.networkInterfaceIndex). + Return(tc.response, tc.err). + Once() + + response, err := mockServer.Create(pkg.API_URL+pkg.CREATE_SERVER_INSTANCE_PATH, tc.request, tc.networkInterfaceIndex) + + assert.Nil(t, err, "The error should be nil") + assert.Equal(t, tc.response, response, "The responses should be equal") + mockServer.AssertExpectations(t) + }) + } +} diff --git a/pkg/subnet.go b/pkg/subnet.go new file mode 100644 index 0000000..eaf09bc --- /dev/null +++ b/pkg/subnet.go @@ -0,0 +1,65 @@ +package pkg + +import ( + "fmt" + "io" + "log" + "net/http" + + serverType "github.com/cloud-club/Aviator-service/types/server" +) + +type SubnetService struct{} + +func NewSubnetService() SubnetInterface { + return &SubnetService{} +} + +type SubnetInterface interface { + Get(url string) (*serverType.SubnetList, error) +} + +func (subnet *SubnetService) Get(url string) (*serverType.SubnetList, error) { + // Set url with query parameters + // However, there is no must required query parameters for this API, so we will comment this line right now. + //requestParams := serverType.CreateRequestString(request) + + // Create an HTTP request + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return nil, err + } + + // Set HTTP header for NCP authorization + SetNCPHeader(req, "YnCbljlTZfqRkFOXighj", "bDoQVXDGLJ9BhzpOYxnjSyxNB97dohAUPLeiQC0D") + + // Make the HTTP request + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + // Check the response status + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected response status: %s", resp.Status) + } + + // Read the response body + responseByteData, err := io.ReadAll(resp.Body) + if err != nil { + log.Fatal(err) + return nil, err + } + + // Convert the response body to the struct + var sl *serverType.SubnetList + responseInterface, err := serverType.MapResponse(responseByteData, &sl) + if err != nil { + return nil, err + } + + // interface{} to *serverType.SubnetList + responseStruct := responseInterface.(**serverType.SubnetList) + + return *responseStruct, nil +} diff --git a/types/server/accesscontrolgroup.go b/types/server/accesscontrolgroup.go new file mode 100644 index 0000000..1493f0a --- /dev/null +++ b/types/server/accesscontrolgroup.go @@ -0,0 +1,15 @@ +package types + +type AccessControlGroup struct { + AccessControlGroupNo string `xml:"accessControlGroupNo"` + AccessControlGroupName string `xml:"accessControlGroupName"` + IsDefault bool `xml:"isDefault"` + VpcNo string `xml:"vpcNo"` + AccessControlGroupStatus CommonCode `xml:"accessControlGroupStatus"` + AccessControlGroupDescription string `xml:"accessControlGroupDescription"` +} + +type AccessControlGroupList struct { + TotalRows int `xml:"totalRows"` + AccessControlGroupSet []AccessControlGroup `xml:"accessControlGroupList>accessControlGroup"` +} diff --git a/types/server/common.go b/types/server/common.go index 8faa348..3a581a0 100644 --- a/types/server/common.go +++ b/types/server/common.go @@ -1,6 +1,6 @@ package types type CommonCode struct { - Code string // server status : int | creat | run | nstop - CodeName string + Code string `xml:"code"` // server status : int | creat | run | nstop + CodeName string `xml:"codeName"` } diff --git a/types/server/network.go b/types/server/network.go index 243b1a1..a7d3a11 100644 --- a/types/server/network.go +++ b/types/server/network.go @@ -1,33 +1,30 @@ package types type NetworkInterface struct { - NetworkInterfaceNo string - NetworkInterfaceName string - SubnetNo string - DeleteOnTermination bool - IsDefault bool - DeviceName string - NetworkInterfaceStatus CommonCode - InstanceType CommonCode - InstanceNo string - IP string - MacAddress string - AccessControlGroupNoList []AccessControlGroup - NetworkInterfaceDescription string - SecondaryIPList []SecondaryIP + NetworkInterfaceNo string `xml:"networkInterfaceNo"` + NetworkInterfaceName string `xml:"networkInterfaceName"` + SubnetNo string `xml:"subnetNo"` + DeleteOnTermination bool `xml:"deleteOnTermination"` + IsDefault bool `xml:"isDefault"` + //DeviceName string `xml:"deviceName"` // Conditional + NetworkInterfaceStatus CommonCode `xml:"networkInterfaceStatus"` + //InstanceType CommonCode `xml:"instanceType"` // Conditional + //InstanceNo string `xml:"instanceNo"` // Conditional + IP string `xml:"ip"` + MacAddress string `xml:"macAddress"` + //AccessControlGroupNoList []AccessControlGroup `xml:"accessControlGroupNoList"` // Conditional + //NetworkInterfaceDescription string `xml:"networkInterfaceDescription"` // Conditional + //SecondaryIPList []SecondaryIP `xml:"secondaryIPList"` // Conditional } type NetworkInterfaceList struct { - TotalRows int - NetworkInterfaceList []NetworkInterface + TotalRows int `xml:"totalRows"` + NetworkInterfaceList []NetworkInterface `xml:"networkInterfaceList>networkInterface"` } type NetworkInterfaceNoList struct { NetworkInterfaceNoList []string } -type AccessControlGroup struct { -} - type SecondaryIP struct { } diff --git a/types/server/request.go b/types/server/request.go index e4d142f..f74d96c 100644 --- a/types/server/request.go +++ b/types/server/request.go @@ -1,16 +1,12 @@ package types -import ( - "fmt" - "reflect" -) - // 필수가 아닌 필드(필수 여부: No)는 주석 처리 해두었음. // 필요할 때 주석 해제 // 필수가 아닌 필드 중 (필수 여부: Conditional)는 주석 처리 안 했음. // 필요할 때 주석 처리 type CreateServerRequest struct { //RegionCode string `json:"regionCode"` + // ServerImageProductCode 와 MemberServerImageInstanceNo 둘 중 하나는 무조건 필수 기재 MemberServerImageInstanceNo string `json:"memberServerImageInstanceNo"` // Conditional ServerImageProductCode string `json:"serverImageProductCode"` // Conditional ServerImageNo string `json:"serverImageNo"` // Conditional @@ -27,11 +23,24 @@ type CreateServerRequest struct { // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // NetworkInterfaceList.N의 값들 // NetworkInterfaceList를 먼저 호출한 뒤, N 번째 NetworkInterface 정보에서 필요한 부분들 추출해서 사용 - NetworkInterfaceOrder int `json:"networkInterfaceList"` - //NetworkInterfaceNo string `json:"networkInterfaceNo"` - //NetworkInterfaceSubnetNo string `json:"networkInterfaceSubnetNo"` + + //기본 네트워크 인터페이스로 설정하려면 0 입력 + // min:0, max:2 + NetworkInterfaceOrder int `json:"networkInterfaceList.N.networkInterfaceOrder"` + + // 사용자가 직접 생성한 네트워크 인터페이스를 추가하려고 하는 경우 해당 네트워크 인터페이스 번호 입력 + NetworkInterfaceNo string `json:"networkInterfaceNo"` + + // 새로 생성할 네트워크 인터페이스의 서브넷 또는 추가하려고 하는 기존 네트워크 인터페이스의 서브넷 결정 + // 기본 네트워크 인터페이스인 경우에는 자동으로 할당 + NetworkInterfaceSubnetNo string `json:"networkInterfaceSubnetNo"` + //NetworkInterfaceIp string `json:"networkInterfaceIp"` - //accessControlGroupNoListN []string `json:"accessControlGroupNoList"` + + // 네트워크 인터페이스를 새로 생성하는 경우 반드시 적용할 ACG 결정 + // 최대 3개의 ACG 적용 가능 + // accessControlGroupNo는 getAccessControlGroupList 액션을 통해 획득 가능 + AccessControlGroupNoListN []string `json:"accessControlGroupNoList"` //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< // PlacementGroupNo string `json:"placementGroupNo"` @@ -68,21 +77,3 @@ type GetServerRequest struct{} type ListServerRequest struct{} type UpdateServerRequest struct{} - -// Request 구조체를 모두 하나의 String으로 변환해주는 함수 -// NetworkInterfaceList.N, BlockDevicePartitionList.N, BlockStorageMappingList.N의 값들에 대한 수정 필요 -func GenerateRequestString(request interface{}) string { - var requestString string - requestType := reflect.TypeOf(request) - requestValue := reflect.ValueOf(request) - - for i := 0; i < requestType.NumField(); i++ { - fieldValue := requestValue.Field(i) - if fieldValue.IsZero() { - continue - } - requestString += fmt.Sprintf("%s=%v&", requestType.Field(i).Name, fmt.Sprintf("%v", fieldValue)) - } - - return "?" + requestString[:len(requestString)-1] -} diff --git a/types/server/request_response_func.go b/types/server/request_response_func.go new file mode 100644 index 0000000..4698d26 --- /dev/null +++ b/types/server/request_response_func.go @@ -0,0 +1,54 @@ +package types + +import ( + "encoding/xml" + "fmt" + "net/url" + "reflect" + "strings" +) + +// Request 구조체를 모두 하나의 String으로 변환해주는 함수 +func CreateRequestString(csr *CreateServerRequest, networkInterfaceIndex int) string { + v := url.Values{} + s := reflect.ValueOf(csr).Elem() + + for i := 0; i < s.NumField(); i++ { + field := s.Field(i) + fieldName := s.Type().Field(i).Name // Get the field name + jsonTag := strings.Split(s.Type().Field(i).Tag.Get("json"), ",")[0] + + // If the field is NetworkInterfaceOrder, change jsonTag's 'N' value to networkInterfaceIndex + // Before: networkInterfaceList.N.networkInterfaceOrder + // After: networkInterfaceList.0.networkInterfaceOrder + if fieldName == "NetworkInterfaceOrder" { + jsonTag = strings.Replace(jsonTag, "N", fmt.Sprint(networkInterfaceIndex), 1) + } + + if fieldName == "AccessControlGroupNoListN" { + + } + + v.Add(jsonTag, fmt.Sprint(field.Interface())) + } + + return "?" + v.Encode() +} + +// Response XML을 struct로 변환하는 함수 +// responseBody: API 서버로부터 받은 XML response body +// v: interface{} 타입의 struct 포인터 +// 예시: mapResponse(responseBody, &CreateServerResponse{}) +func MapResponse(responseBody []byte, v interface{}) (interface{}, error) { + rv := reflect.ValueOf(v) // reflect를 이용해 v가 어떤 구조체 타입인지 알아냄 + if rv.Kind() != reflect.Ptr || rv.IsNil() { // 만약 v가 포인터가 아니거나 nil이면 에러 반환 + return nil, fmt.Errorf("non-nil pointer expected") + } + + err := xml.Unmarshal(responseBody, v) // responseBody를 v로 매핑. 만약 CreateServerResponse 타입이면 CreateServerResponse로 매핑 + if err != nil { + return nil, fmt.Errorf("error unmarshalling response: %v", err) + } + + return v, nil +} diff --git a/types/server/response.go b/types/server/response.go index f31ff97..10f7b68 100644 --- a/types/server/response.go +++ b/types/server/response.go @@ -1,9 +1,6 @@ package types import ( - "encoding/xml" - "fmt" - "reflect" "time" ) @@ -61,21 +58,3 @@ type ListServerResponse struct{} type UpdateServerResponse struct{} type DeleteServerResponse struct{} - -// Response XML을 Go struct로 변환하는 함수 -// responseBody: API 서버로부터 받은 XML response body -// v: interface{} 타입의 struct 포인터 -// 예시: mapResponse(responseBody, &CreateServerResponse{}) -func MapResponse(responseBody []byte, v interface{}) (interface{}, error) { - rv := reflect.ValueOf(v) // reflect를 이용해 v가 어떤 구조체 타입인지 알아냄 - if rv.Kind() != reflect.Ptr || rv.IsNil() { // 만약 v가 포인터가 아니거나 nil이면 에러 반환 - return nil, fmt.Errorf("non-nil pointer expected") - } - - err := xml.Unmarshal(responseBody, v) // responseBody를 v로 매핑. 만약 CreateServerResponse 타입이면 CreateServerResponse로 매핑 - if err != nil { - return nil, fmt.Errorf("error unmarshalling response: %v", err) - } - - return v, nil -} diff --git a/types/server/subnet.go b/types/server/subnet.go new file mode 100644 index 0000000..6ba96d1 --- /dev/null +++ b/types/server/subnet.go @@ -0,0 +1,21 @@ +package types + +import "time" + +type Subnet struct { + SubnetNo string `xml:"subnetNo"` + VpcNo string `xml:"vpcNo"` + ZoneCode string `xml:"zoneCode"` + SubnetName string `xml:"subnetName"` + Subnet string `xml:"subnet"` + SubnetStatus CommonCode `xml:"subnetStatus"` + CreateDate time.Time `xml:"createDate"` + SubnetType CommonCode `xml:"subnetType"` + UsageType CommonCode `xml:"usageType"` + NetworkAclNo string `xml:"networkAclNo"` +} + +type SubnetList struct { + TotalRows int `xml:"totalRows"` + Subnet []Subnet `xml:"subnetList>subnet"` +}