diff --git a/CHANGELOG.md b/CHANGELOG.md index 280bee1..4322b19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ FEATURES: * Adds support for new split monitoring permissions create_jobs, update_jobs and delete_jobs +* Remove DDI (DHCP & IPAM) code ## 2.11.0 (May 23rd, 2024) diff --git a/rest/_examples/ddi_apikey.go b/rest/_examples/ddi_apikey.go deleted file mode 100644 index 4f0f548..0000000 --- a/rest/_examples/ddi_apikey.go +++ /dev/null @@ -1,84 +0,0 @@ -package main - -import ( - "crypto/tls" - "fmt" - "log" - "net/http" - "os" - "time" - - api "gopkg.in/ns1/ns1-go.v2/rest" - "gopkg.in/ns1/ns1-go.v2/rest/model/account" -) - -var ddiClient *api.Client - -// Helper that initializes rest api client from environment variable. -func init() { - k := os.Getenv("NS1_APIKEY") - if k == "" { - log.Fatal("NS1_APIKEY environment variable is not set, giving up") - } - tr := &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - } - httpClient := &http.Client{Timeout: time.Second * 10} - httpClient.Transport = tr - // Adds logging to each http request. - doer := api.Decorate(httpClient, api.Logging(log.New(os.Stdout, "", log.LstdFlags))) - // In this example DDI is running on localhost - ddiClient = api.NewClient(doer, api.SetAPIKey(k), api.SetDDIAPI(), api.SetEndpoint("https://127.0.0.1/v1/")) -} - -func main() { - key := account.APIKey{ - Name: "foobar", - TeamIDs: []string{}, - IPWhitelist: []string{"1.1.1.1", "2.2.2.2"}, - IPWhitelistStrict: true, - Permissions: account.PermissionsMap{ - DNS: account.PermissionsDNS{ - ZonesAllow: []string{}, - ZonesDeny: []string{}, - }, - DHCP: &account.PermissionsDHCP{ - TagsAllow: &[]account.AuthTag{}, - TagsDeny: &[]account.AuthTag{}, - }, - IPAM: &account.PermissionsIPAM{ - TagsAllow: &[]account.AuthTag{}, - TagsDeny: &[]account.AuthTag{}, - }, - }, - } - - _, err := ddiClient.APIKeys.Create(&key) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("New API key: %v", key) - - updatedIPAMPermissions := &account.PermissionsIPAM{ - TagsAllow: &[]account.AuthTag{ - { - Name: "auth:foo", - Value: "", - }, - { - Name: "auth:bar", - Value: "baz", - }, - }, - TagsDeny: &[]account.AuthTag{}, - } - key.Permissions.IPAM = updatedIPAMPermissions - - _, err = ddiClient.APIKeys.Update(&key) - if err != nil { - log.Fatal(err) - } - - fmt.Printf("Updated API key: %v", key) -} diff --git a/rest/account_apikey.go b/rest/account_apikey.go index dbfd339..b9e5f6b 100644 --- a/rest/account_apikey.go +++ b/rest/account_apikey.go @@ -66,18 +66,9 @@ func (s *APIKeysService) Create(a *account.APIKey) (*http.Response, error) { err error ) - // If this is DDI then the permissions need to be transformed to DDI-compatible permissions. - if s.client.DDI && a != nil { - ddiAPIKey := apiKeyToDDIAPIKey(a) - req, err = s.client.NewRequest("PUT", "account/apikeys", ddiAPIKey) - if err != nil { - return nil, err - } - } else { - req, err = s.client.NewRequest("PUT", "account/apikeys", a) - if err != nil { - return nil, err - } + req, err = s.client.NewRequest("PUT", "account/apikeys", a) + if err != nil { + return nil, err } // Update account fields with data from api(ensure consistent) @@ -106,18 +97,9 @@ func (s *APIKeysService) Update(a *account.APIKey) (*http.Response, error) { err error ) - // If this is DDI then the permissions need to be transformed to DDI-compatible permissions. - if s.client.DDI && a != nil { - ddiAPIKey := apiKeyToDDIAPIKey(a) - req, err = s.client.NewRequest("POST", path, ddiAPIKey) - if err != nil { - return nil, err - } - } else { - req, err = s.client.NewRequest("POST", path, a) - if err != nil { - return nil, err - } + req, err = s.client.NewRequest("POST", path, a) + if err != nil { + return nil, err } // Update apikey fields with data from api(ensure consistent) @@ -166,40 +148,3 @@ var ( // ErrKeyMissing bundles GET/POST/DELETE error. ErrKeyMissing = errors.New("key does not exist") ) - -func apiKeyToDDIAPIKey(k *account.APIKey) *ddiAPIKey { - ddiAPIKey := &ddiAPIKey{ - ID: k.ID, - Key: k.Key, - LastAccess: k.LastAccess, - Name: k.Name, - TeamIDs: k.TeamIDs, - IPWhitelist: k.IPWhitelist, - IPWhitelistStrict: k.IPWhitelistStrict, - Permissions: ddiPermissionsMap{ - DNS: k.Permissions.DNS, - Data: k.Permissions.Data, - Account: permissionsDDIAccount{ - ManageUsers: k.Permissions.Account.ManageUsers, - ManageTeams: k.Permissions.Account.ManageTeams, - ManageApikeys: k.Permissions.Account.ManageApikeys, - ManageAccountSettings: k.Permissions.Account.ManageAccountSettings, - ViewActivityLog: k.Permissions.Account.ViewActivityLog, - }, - }, - } - - if k.Permissions.Security != nil { - ddiAPIKey.Permissions.Security = permissionsDDISecurity(*k.Permissions.Security) - } - - if k.Permissions.DHCP != nil { - ddiAPIKey.Permissions.DHCP = *k.Permissions.DHCP - } - - if k.Permissions.IPAM != nil { - ddiAPIKey.Permissions.IPAM = *k.Permissions.IPAM - } - - return ddiAPIKey -} diff --git a/rest/account_apikey_test.go b/rest/account_apikey_test.go index a636f8e..66d22e7 100644 --- a/rest/account_apikey_test.go +++ b/rest/account_apikey_test.go @@ -21,8 +21,6 @@ func TestCreateAPIKey(t *testing.T) { var k account.APIKey require.NoError(t, json.Unmarshal(b, &k)) assert.Nil(t, k.Permissions.Security) - assert.Nil(t, k.Permissions.DHCP) - assert.Nil(t, k.Permissions.IPAM) assert.False(t, k.Permissions.Monitoring.ManageJobs) assert.False(t, k.Permissions.Monitoring.CreateJobs) assert.False(t, k.Permissions.Monitoring.UpdateJobs) @@ -44,117 +42,3 @@ func TestCreateAPIKey(t *testing.T) { _, err := c.APIKeys.Create(k) require.NoError(t, err) } - -func TestCreateDDIAPIKey(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - b, err := ioutil.ReadAll(r.Body) - require.NoError(t, err) - - var k account.APIKey - require.NoError(t, json.Unmarshal(b, &k)) - switch k.ID { - case "ddi-no-authtags": - assert.NotNil(t, k.Permissions.Security) - assert.NotNil(t, k.Permissions.DHCP) - assert.NotNil(t, k.Permissions.IPAM) - assert.NotNil(t, k.IPWhitelist) - assert.True(t, k.IPWhitelistStrict) - // ensure auth tag permissions are not included by default to maintain backwards compatibility - assert.Nil(t, k.Permissions.DHCP.TagsAllow) - assert.Nil(t, k.Permissions.DHCP.TagsDeny) - assert.Nil(t, k.Permissions.IPAM.TagsAllow) - assert.Nil(t, k.Permissions.IPAM.TagsDeny) - case "ddi-authtags": - assert.NotNil(t, k.Permissions.DHCP) - assert.NotNil(t, k.Permissions.IPAM) - assert.Equal(t, "auth:dhcpallow", (*k.Permissions.DHCP.TagsAllow)[0].Name) - assert.Equal(t, "", (*k.Permissions.DHCP.TagsAllow)[0].Value) - assert.Equal(t, "auth:dhcpdeny", (*k.Permissions.DHCP.TagsDeny)[0].Name) - assert.Equal(t, "denyme", (*k.Permissions.DHCP.TagsDeny)[0].Value) - assert.Equal(t, "auth:ipamallow", (*k.Permissions.IPAM.TagsAllow)[0].Name) - assert.Equal(t, "", (*k.Permissions.IPAM.TagsAllow)[0].Value) - assert.Equal(t, "auth:ipamdeny", (*k.Permissions.IPAM.TagsDeny)[0].Name) - assert.Equal(t, "denyme", (*k.Permissions.IPAM.TagsDeny)[0].Value) - case "ddi-empty-authtags": - assert.NotNil(t, k.Permissions.DHCP) - assert.NotNil(t, k.Permissions.IPAM) - assert.Equal(t, []account.AuthTag{}, *k.Permissions.DHCP.TagsAllow) - assert.Equal(t, []account.AuthTag{}, *k.Permissions.DHCP.TagsDeny) - assert.Equal(t, []account.AuthTag{}, *k.Permissions.IPAM.TagsAllow) - assert.Equal(t, []account.AuthTag{}, *k.Permissions.IPAM.TagsDeny) - } - - _, err = w.Write(b) - require.NoError(t, err) - })) - defer ts.Close() - c := NewClient(nil, SetEndpoint(ts.URL), SetDDIAPI()) - - // Create a key without auth tags - k := &account.APIKey{ - ID: "ddi-no-authtags", - Key: "key-1", - Name: "name-1", - IPWhitelist: []string{"1.1.1.1"}, - IPWhitelistStrict: true, - Permissions: account.PermissionsMap{}, - } - - _, err := c.APIKeys.Create(k) - require.NoError(t, err) - // Create a key with auth tags - k = &account.APIKey{ - ID: "ddi-authtags", - Key: "key-2", - Name: "name-2", - Permissions: account.PermissionsMap{ - DHCP: &account.PermissionsDHCP{ - TagsAllow: &[]account.AuthTag{ - { - Name: "auth:dhcpallow", - Value: "", - }, - }, - TagsDeny: &[]account.AuthTag{ - { - Name: "auth:dhcpdeny", - Value: "denyme", - }, - }, - }, - IPAM: &account.PermissionsIPAM{ - TagsAllow: &[]account.AuthTag{ - { - Name: "auth:ipamallow", - Value: "", - }, - }, - TagsDeny: &[]account.AuthTag{ - { - Name: "auth:ipamdeny", - Value: "denyme", - }, - }, - }, - }, - } - // Create a key with empty auth tags - k = &account.APIKey{ - ID: "ddi-empty-authtags", - Key: "key-3", - Name: "name-3", - Permissions: account.PermissionsMap{ - DHCP: &account.PermissionsDHCP{ - TagsAllow: &[]account.AuthTag{}, - TagsDeny: &[]account.AuthTag{}, - }, - IPAM: &account.PermissionsIPAM{ - TagsAllow: &[]account.AuthTag{}, - TagsDeny: &[]account.AuthTag{}, - }, - }, - } - - _, err = c.APIKeys.Create(k) - require.NoError(t, err) -} diff --git a/rest/account_team.go b/rest/account_team.go index 1ff8dd9..21ce6d1 100644 --- a/rest/account_team.go +++ b/rest/account_team.go @@ -64,18 +64,9 @@ func (s *TeamsService) Create(t *account.Team) (*http.Response, error) { err error ) - // If this is DDI then the permissions need to be transformed to DDI-compatible permissions. - if s.client.DDI && t != nil { - ddiTeam := teamToDDITeam(t) - req, err = s.client.NewRequest("PUT", "account/teams", ddiTeam) - if err != nil { - return nil, err - } - } else { - req, err = s.client.NewRequest("PUT", "account/teams", t) - if err != nil { - return nil, err - } + req, err = s.client.NewRequest("PUT", "account/teams", t) + if err != nil { + return nil, err } // Update team fields with data from api(ensure consistent) @@ -104,18 +95,9 @@ func (s *TeamsService) Update(t *account.Team) (*http.Response, error) { err error ) - // If this is DDI then the permissions need to be transformed to DDI-compatible permissions. - if s.client.DDI && t != nil { - ddiTeam := teamToDDITeam(t) - req, err = s.client.NewRequest("POST", path, ddiTeam) - if err != nil { - return nil, err - } - } else { - req, err = s.client.NewRequest("POST", path, t) - if err != nil { - return nil, err - } + req, err = s.client.NewRequest("POST", path, t) + if err != nil { + return nil, err } // Update team fields with data from api(ensure consistent) @@ -164,36 +146,3 @@ var ( // ErrTeamMissing bundles GET/POST/DELETE error. ErrTeamMissing = errors.New("team does not exist") ) - -func teamToDDITeam(t *account.Team) *ddiTeam { - ddiTeam := &ddiTeam{ - ID: t.ID, - Name: t.Name, - IPWhitelist: t.IPWhitelist, - Permissions: ddiPermissionsMap{ - DNS: t.Permissions.DNS, - Data: t.Permissions.Data, - Account: permissionsDDIAccount{ - ManageUsers: t.Permissions.Account.ManageUsers, - ManageTeams: t.Permissions.Account.ManageTeams, - ManageApikeys: t.Permissions.Account.ManageApikeys, - ManageAccountSettings: t.Permissions.Account.ManageAccountSettings, - ViewActivityLog: t.Permissions.Account.ViewActivityLog, - }, - }, - } - - if t.Permissions.Security != nil { - ddiTeam.Permissions.Security = permissionsDDISecurity(*t.Permissions.Security) - } - - if t.Permissions.DHCP != nil { - ddiTeam.Permissions.DHCP = *t.Permissions.DHCP - } - - if t.Permissions.IPAM != nil { - ddiTeam.Permissions.IPAM = *t.Permissions.IPAM - } - - return ddiTeam -} diff --git a/rest/account_team_test.go b/rest/account_team_test.go index 1dfad94..64d189a 100644 --- a/rest/account_team_test.go +++ b/rest/account_team_test.go @@ -21,8 +21,6 @@ func TestCreateTeam(t *testing.T) { var tm account.Team require.NoError(t, json.Unmarshal(b, &tm)) assert.Nil(t, tm.Permissions.Security) - assert.Nil(t, tm.Permissions.DHCP) - assert.Nil(t, tm.Permissions.IPAM) assert.False(t, tm.Permissions.Monitoring.ManageJobs) assert.False(t, tm.Permissions.Monitoring.CreateJobs) assert.False(t, tm.Permissions.Monitoring.UpdateJobs) @@ -42,33 +40,3 @@ func TestCreateTeam(t *testing.T) { _, err := c.Teams.Create(tm) require.NoError(t, err) } - -func TestCreateDDITeam(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - b, err := ioutil.ReadAll(r.Body) - require.NoError(t, err) - - var tm account.Team - require.NoError(t, json.Unmarshal(b, &tm)) - assert.NotNil(t, tm.Permissions.Security) - assert.NotNil(t, tm.Permissions.DHCP) - assert.NotNil(t, tm.Permissions.IPAM) - assert.NotNil(t, tm.IPWhitelist) - - w.Write(b) - })) - defer ts.Close() - c := NewClient(nil, SetEndpoint(ts.URL), SetDDIAPI()) - - tm := &account.Team{ - ID: "id-1", - Name: "team-1", - IPWhitelist: []account.IPWhitelist{ - {Name: "whitelist", Values: []string{"1.1.1.1"}}, - }, - Permissions: account.PermissionsMap{}, - } - - _, err := c.Teams.Create(tm) - require.NoError(t, err) -} diff --git a/rest/account_user.go b/rest/account_user.go index 82bc7cf..6d43ead 100644 --- a/rest/account_user.go +++ b/rest/account_user.go @@ -64,18 +64,9 @@ func (s *UsersService) Create(u *account.User) (*http.Response, error) { err error ) - // If this is DDI then the permissions need to be transformed to DDI-compatible permissions. - if s.client.DDI && u != nil { - ddiUser := userToDDIUser(u) - req, err = s.client.NewRequest("PUT", "account/users", ddiUser) - if err != nil { - return nil, err - } - } else { - req, err = s.client.NewRequest("PUT", "account/users", u) - if err != nil { - return nil, err - } + req, err = s.client.NewRequest("PUT", "account/users", u) + if err != nil { + return nil, err } // Update user fields with data from api(ensure consistent) @@ -104,18 +95,9 @@ func (s *UsersService) Update(u *account.User) (*http.Response, error) { err error ) - // If this is DDI then the permissions need to be transformed to DDI-compatible permissions. - if s.client.DDI && u != nil { - ddiUser := userToDDIUser(u) - req, err = s.client.NewRequest("POST", path, ddiUser) - if err != nil { - return nil, err - } - } else { - req, err = s.client.NewRequest("POST", path, u) - if err != nil { - return nil, err - } + req, err = s.client.NewRequest("POST", path, u) + if err != nil { + return nil, err } // Update user fields with data from api(ensure consistent) @@ -164,41 +146,3 @@ var ( // ErrUserMissing bundles GET/POST/DELETE error. ErrUserMissing = errors.New("user does not exist") ) - -func userToDDIUser(u *account.User) *ddiUser { - ddiUser := &ddiUser{ - LastAccess: u.LastAccess, - Name: u.Name, - Username: u.Username, - Email: u.Email, - TeamIDs: u.TeamIDs, - Notify: u.Notify, - IPWhitelist: u.IPWhitelist, - IPWhitelistStrict: u.IPWhitelistStrict, - Permissions: ddiPermissionsMap{ - DNS: u.Permissions.DNS, - Data: u.Permissions.Data, - Account: permissionsDDIAccount{ - ManageUsers: u.Permissions.Account.ManageUsers, - ManageTeams: u.Permissions.Account.ManageTeams, - ManageApikeys: u.Permissions.Account.ManageApikeys, - ManageAccountSettings: u.Permissions.Account.ManageAccountSettings, - ViewActivityLog: u.Permissions.Account.ViewActivityLog, - }, - }, - } - - if u.Permissions.Security != nil { - ddiUser.Permissions.Security = permissionsDDISecurity(*u.Permissions.Security) - } - - if u.Permissions.DHCP != nil { - ddiUser.Permissions.DHCP = *u.Permissions.DHCP - } - - if u.Permissions.IPAM != nil { - ddiUser.Permissions.IPAM = *u.Permissions.IPAM - } - - return ddiUser -} diff --git a/rest/account_user_test.go b/rest/account_user_test.go index 7299122..ced25a4 100644 --- a/rest/account_user_test.go +++ b/rest/account_user_test.go @@ -21,8 +21,6 @@ func TestCreateUser(t *testing.T) { var u account.User require.NoError(t, json.Unmarshal(b, &u)) assert.Nil(t, u.Permissions.Security) - assert.Nil(t, u.Permissions.DHCP) - assert.Nil(t, u.Permissions.IPAM) assert.False(t, u.Permissions.Monitoring.ManageJobs) assert.False(t, u.Permissions.Monitoring.CreateJobs) assert.False(t, u.Permissions.Monitoring.UpdateJobs) @@ -43,34 +41,3 @@ func TestCreateUser(t *testing.T) { _, err := c.Users.Create(u) require.NoError(t, err) } - -func TestCreateDDIUser(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - b, err := ioutil.ReadAll(r.Body) - require.NoError(t, err) - - var u account.User - require.NoError(t, json.Unmarshal(b, &u)) - assert.NotNil(t, u.Permissions.Security) - assert.NotNil(t, u.Permissions.DHCP) - assert.NotNil(t, u.Permissions.IPAM) - assert.NotNil(t, u.IPWhitelist) - assert.True(t, u.IPWhitelistStrict) - - w.Write(b) - })) - defer ts.Close() - c := NewClient(nil, SetEndpoint(ts.URL), SetDDIAPI()) - - u := &account.User{ - Name: "name-1", - Username: "user-1", - Email: "email-1", - IPWhitelist: []string{"1.1.1.1"}, - IPWhitelistStrict: true, - Permissions: account.PermissionsMap{}, - } - - _, err := c.Users.Create(u) - require.NoError(t, err) -} diff --git a/rest/client.go b/rest/client.go index 7757f11..4743a50 100644 --- a/rest/client.go +++ b/rest/client.go @@ -54,9 +54,6 @@ type Client struct { // Whether the client should handle paginated responses automatically. FollowPagination bool - // Enables permissions compatibility with the DDI API. - DDI bool - // From the excellent github-go client. common service // Reuse a single struct instead of allocating one for each service on the heap. @@ -80,11 +77,6 @@ type Client struct { Zones *ZonesService Versions *VersionsService DNSSEC *DNSSECService - IPAM *IPAMService - ScopeGroup *ScopeGroupService - Scope *ScopeService - Reservation *ReservationService - OptionDef *OptionDefService TSIG *TsigService View *DNSViewService Network *NetworkService @@ -131,11 +123,6 @@ func NewClient(httpClient Doer, options ...func(*Client)) *Client { c.Zones = (*ZonesService)(&c.common) c.Versions = (*VersionsService)(&c.common) c.DNSSEC = (*DNSSECService)(&c.common) - c.IPAM = (*IPAMService)(&c.common) - c.ScopeGroup = (*ScopeGroupService)(&c.common) - c.Scope = (*ScopeService)(&c.common) - c.Reservation = (*ReservationService)(&c.common) - c.OptionDef = (*OptionDefService)(&c.common) c.TSIG = (*TsigService)(&c.common) c.View = (*DNSViewService)(&c.common) c.Network = (*NetworkService)(&c.common) @@ -185,11 +172,6 @@ func SetFollowPagination(shouldFollow bool) func(*Client) { return func(c *Client) { c.FollowPagination = shouldFollow } } -// SetDDIAPI configures the client to use permissions compatible with the DDI API. -func SetDDIAPI() func(*Client) { - return func(c *Client) { c.DDI = true } -} - // Param is a container struct which holds a `Key` and `Value` field corresponding to the values of a URL parameter. type Param struct { Key, Value string diff --git a/rest/ddi.go b/rest/ddi.go deleted file mode 100644 index 014e494..0000000 --- a/rest/ddi.go +++ /dev/null @@ -1,73 +0,0 @@ -package rest - -import "gopkg.in/ns1/ns1-go.v2/rest/model/account" - -// ddiTeam wraps an NS1 /accounts/teams resource for DDI. -// Used for internally mapping between DDI permissions to maintain backwards compatibility. -type ddiTeam struct { - ID string `json:"id,omitempty"` - Name string `json:"name"` - Permissions ddiPermissionsMap `json:"permissions"` - IPWhitelist []account.IPWhitelist `json:"ip_whitelist"` -} - -// ddiUser wraps an NS1 /account/users resource for DDI. -// Used for internally mapping between DDI permissions to maintain backwards compatibility. -type ddiUser struct { - // Read-only fields - LastAccess float64 `json:"last_access"` - - Name string `json:"name"` - Username string `json:"username"` - Email string `json:"email"` - TeamIDs []string `json:"teams"` - Notify account.NotificationSettings `json:"notify"` - IPWhitelist []string `json:"ip_whitelist"` - IPWhitelistStrict bool `json:"ip_whitelist_strict"` - - Permissions ddiPermissionsMap `json:"permissions"` -} - -// ddiAPIKey wraps an NS1 /account/apikeys resource for DDI specifically. -// Used for internally mapping between DDI permissions to maintain backwards compatibility. -type ddiAPIKey struct { - // Read-only fields - ID string `json:"id,omitempty"` - Key string `json:"key,omitempty"` - LastAccess int `json:"last_access,omitempty"` - - Name string `json:"name"` - TeamIDs []string `json:"teams"` - IPWhitelist []string `json:"ip_whitelist"` - IPWhitelistStrict bool `json:"ip_whitelist_strict"` - - Permissions ddiPermissionsMap `json:"permissions"` -} - -// ddiPermissionsMap wraps a User's "permissions" attribute for DDI. -// Used for internally mapping between DDI permissions to maintain backwards compatibility. -type ddiPermissionsMap struct { - DNS account.PermissionsDNS `json:"dns"` - Data account.PermissionsData `json:"data"` - Account permissionsDDIAccount `json:"account"` - Security permissionsDDISecurity `json:"security"` - DHCP account.PermissionsDHCP `json:"dhcp"` - IPAM account.PermissionsIPAM `json:"ipam"` -} - -// permissionsDDIAccount wraps a User's "permissions.account" attribute for DDI. -// Used for internally mapping between DDI permissions to maintain backwards compatibility. -type permissionsDDIAccount struct { - ManageUsers bool `json:"manage_users"` - ManageTeams bool `json:"manage_teams"` - ManageApikeys bool `json:"manage_apikeys"` - ManageAccountSettings bool `json:"manage_account_settings"` - ViewActivityLog bool `json:"view_activity_log"` -} - -// permissionsDDISecurity wraps a User's "permissions.security" attribute for DDI. -// Used for internally mapping between DDI permissions to maintain backwards compatibility. -type permissionsDDISecurity struct { - ManageGlobal2FA bool `json:"manage_global_2fa"` - ManageActiveDirectory bool `json:"manage_active_directory"` -} diff --git a/rest/ipam.go b/rest/ipam.go deleted file mode 100644 index eab5927..0000000 --- a/rest/ipam.go +++ /dev/null @@ -1,244 +0,0 @@ -package rest - -import ( - "errors" - "fmt" - "net/http" - - "gopkg.in/ns1/ns1-go.v2/rest/model/ipam" -) - -// IPAMService handles the 'ipam' endpoint. -type IPAMService service - -// ListAddrs returns a list of all root addresses (i.e. Parent == 0) in all -// networks. -// -// NS1 API docs: https://ns1.com/api#getview-a-list-of-root-addresses -func (s *IPAMService) ListAddrs() ([]ipam.Address, *http.Response, error) { - req, err := s.client.NewRequest(http.MethodGet, "ipam/address", nil) - if err != nil { - return nil, nil, err - } - - addrs := []ipam.Address{} - var resp *http.Response - if s.client.FollowPagination { - resp, err = s.client.DoWithPagination(req, &addrs, s.nextAddrs) - } else { - resp, err = s.client.Do(req, &addrs) - } - if err != nil { - return nil, resp, err - } - - return addrs, resp, nil -} - -// GetSubnet returns the subnet corresponding to the provided address ID. -// -// NS1 API docs: https://ns1.com/api#getview-a-subnet -func (s *IPAMService) GetSubnet(addrID int) (*ipam.Address, *http.Response, error) { - reqPath := fmt.Sprintf("ipam/address/%d", addrID) - req, err := s.client.NewRequest(http.MethodGet, reqPath, nil) - if err != nil { - return nil, nil, err - } - - addr := &ipam.Address{} - var resp *http.Response - resp, err = s.client.Do(req, addr) - if err != nil { - return nil, resp, err - } - - return addr, resp, nil -} - -// GetChildren requests a list of all child addresses (or subnets) for the -// specified IP address. -// -// NS1 API docs: https://ns1.com/api#getview-address-children -func (s *IPAMService) GetChildren(addrID int) ([]*ipam.Address, *http.Response, error) { - reqPath := fmt.Sprintf("ipam/address/%d/children", addrID) - req, err := s.client.NewRequest(http.MethodGet, reqPath, nil) - if err != nil { - return nil, nil, err - } - - addrs := []*ipam.Address{} - var resp *http.Response - if s.client.FollowPagination { - resp, err = s.client.DoWithPagination(req, &addrs, s.nextAddrs) - } else { - resp, err = s.client.Do(req, &addrs) - } - if err != nil { - return nil, resp, err - } - - return addrs, resp, nil -} - -// GetParent fetches the addresses parent. -// -// NS1 API docs: https://ns1.com/api#getview-address-parent -func (s *IPAMService) GetParent(addrID int) (*ipam.Address, *http.Response, error) { - reqPath := fmt.Sprintf("ipam/address/%d/parent", addrID) - req, err := s.client.NewRequest(http.MethodGet, reqPath, nil) - if err != nil { - return nil, nil, err - } - - addr := &ipam.Address{} - var resp *http.Response - resp, err = s.client.Do(req, addr) - if err != nil { - return nil, resp, err - } - - return addr, resp, nil -} - -// CreateSubnet creates an address or subnet. -// The Prefix and Network fields are required. -// -// NS1 API docs: https://ns1.com/api#putcreate-a-subnet -func (s *IPAMService) CreateSubnet(addr *ipam.Address) (*ipam.Address, *http.Response, error) { - switch { - case addr.Prefix == "": - return nil, nil, errors.New("the Prefix field is required") - case addr.Network == 0: - return nil, nil, errors.New("the Network field is required") - } - - req, err := s.client.NewRequest(http.MethodPut, "ipam/address", addr) - if err != nil { - return nil, nil, err - } - - respAddr := &ipam.Address{} - var resp *http.Response - resp, err = s.client.Do(req, respAddr) - if err != nil { - return nil, resp, err - } - - return respAddr, resp, nil -} - -// EditSubnet updates an existing subnet. -// The ID field is required. -// Parent is whether or not to include the parent in the parent field. -// -// NS1 API docs: https://ns1.com/api#postedit-a-subnet -func (s *IPAMService) EditSubnet(addr *ipam.Address, parent bool) (newAddr, parentAddr *ipam.Address, resp *http.Response, err error) { - if addr.ID == 0 { - return nil, nil, nil, errors.New("the ID field is required") - } - - reqPath := fmt.Sprintf("ipam/address/%d", addr.ID) - req, err := s.client.NewRequest(http.MethodPost, reqPath, addr) - if err != nil { - return nil, nil, nil, err - } - if parent { - q := req.URL.Query() - q.Add("parent", "true") - req.URL.RawQuery = q.Encode() - } - - data := struct { - ipam.Address - Parent ipam.Address `json:"parent"` - }{} - resp, err = s.client.Do(req, &data) - if err != nil { - return nil, nil, resp, err - } - - if parent { - return &data.Address, &data.Parent, resp, nil - } - return &data.Address, nil, resp, nil -} - -// SplitSubnet splits a block of unassigned IP space into equal pieces. -// This will not function with ranges or individual hosts. Normal breaking out -// of a subnet is done with the standard PUT route. (Eg. root address is a /24 -// and request for /29s will break it into 32 /29s) -// -// - Only planned subnets can be split -// - Name and description will be unset on children -// - KVPS and options will be copied; tags will be inherited -// -// NS1 API docs: https://ns1.com/api#postsplit-a-subnet -func (s *IPAMService) SplitSubnet(id, prefix int) (rootAddr int, prefixIDs []int, resp *http.Response, err error) { - reqPath := fmt.Sprintf("ipam/address/%d/split", id) - req, err := s.client.NewRequest(http.MethodPost, reqPath, struct { - Prefix int `json:"prefix"` - }{ - Prefix: prefix, - }) - if err != nil { - return 0, nil, nil, err - } - - data := &struct { - RootAddr int `json:"root_address_id"` - PrefixIDs []int `json:"prefix_ids"` - }{} - resp, err = s.client.Do(req, &data) - return data.RootAddr, data.PrefixIDs, resp, err -} - -// MergeSubnet merges several subnets together. -// -// NS1 API docs: https://ns1.com/api#postmerge-a-subnet -func (s *IPAMService) MergeSubnet(rootID, mergeID int) (*ipam.Address, *http.Response, error) { - req, err := s.client.NewRequest(http.MethodPost, "ipam/address/merge", struct { - Root int `json:"root_address_id"` - Merge int `json:"merged_address_id"` - }{ - Root: rootID, - Merge: mergeID, - }) - if err != nil { - return nil, nil, err - } - - addr := &ipam.Address{} - resp, err := s.client.Do(req, &addr) - return addr, resp, err -} - -// DeleteSubnet removes a subnet entirely. -// -// NS1 API docs: https://ns1.com/api#deletedelete-a-subnet -func (s *IPAMService) DeleteSubnet(id int) (*http.Response, error) { - reqPath := fmt.Sprintf("ipam/address/%d", id) - req, err := s.client.NewRequest(http.MethodDelete, reqPath, nil) - if err != nil { - return nil, err - } - - return s.client.Do(req, nil) -} - -// nextAddrs is a pagination helper than gets and appends another list of -// addresses to the passed list. -func (s *IPAMService) nextAddrs(v *interface{}, uri string) (*http.Response, error) { - addrs := []*ipam.Address{} - resp, err := s.client.getURI(&addrs, uri) - if err != nil { - return resp, err - } - addrList, ok := (*v).(*[]*ipam.Address) - if !ok { - return nil, fmt.Errorf( - "incorrect value for v, expected value of type *[]ipam.Address, got: %T", v, - ) - } - *addrList = append(*addrList, addrs...) - return resp, nil -} diff --git a/rest/ipam_test.go b/rest/ipam_test.go deleted file mode 100644 index 658c0f2..0000000 --- a/rest/ipam_test.go +++ /dev/null @@ -1,345 +0,0 @@ -package rest_test - -import ( - "net/http" - "testing" - - "gopkg.in/ns1/ns1-go.v2/mockns1" - api "gopkg.in/ns1/ns1-go.v2/rest" - "gopkg.in/ns1/ns1-go.v2/rest/model/ipam" -) - -func TestIPAMAddrs(t *testing.T) { - mock, doer, err := mockns1.New(t) - if err != nil { - t.Fatalf("Error creating mock service: %v", err) - } - defer mock.Shutdown() - - client := api.NewClient(doer, api.SetEndpoint("https://"+mock.Address+"/v1/")) - - t.Run("List", func(t *testing.T) { - t.Run("Pagination", func(t *testing.T) { - defer mock.ClearTestCases() - - client.FollowPagination = true - addrs := []ipam.Address{ - {Name: "a"}, - {Name: "b"}, - {Name: "c"}, - {Name: "d"}, - } - err := mock.AddTestCase(http.MethodGet, "/ipam/address", http.StatusOK, nil, nil, "", addrs) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respAddrs, _, err := client.IPAM.ListAddrs() - if err != nil { - t.Fatalf("error listing IPAM addresses: %v", err) - } - if len(respAddrs) != len(addrs) { - t.Errorf("wrong length: want=%d, got=%d", len(addrs), len(respAddrs)) - } - - for i, addr := range respAddrs { - if addr.Name != addrs[i].Name { - t.Errorf("Incorrect name for address %d: want=%q, got=%q", i, addrs[i].Name, addr.Name) - } - } - }) - t.Run("No Pagination", func(t *testing.T) { - defer mock.ClearTestCases() - - client.FollowPagination = false - addrs := []ipam.Address{ - {Name: "a"}, - {Name: "b"}, - } - - linkHeader := `; rel="next"` - header := http.Header{} - header.Set("Link", linkHeader) - - err := mock.AddTestCase(http.MethodGet, "/ipam/address", http.StatusOK, nil, header, "", addrs) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respAddrs, resp, err := client.IPAM.ListAddrs() - if err != nil { - t.Fatalf("error listing IPAM addresses: %v", err) - } - if len(respAddrs) != len(addrs) { - t.Errorf("wrong length: want=%d, got=%d", len(addrs), len(respAddrs)) - } - if l := resp.Header.Get("Link"); l != linkHeader { - t.Errorf("wrong Link header found: want=%q, got=%q", linkHeader, l) - } - - for i, addr := range respAddrs { - if addr.Name != addrs[i].Name { - t.Errorf("Incorrect name for address %d: want=%q, got=%q", i, addrs[i].Name, addr.Name) - } - } - }) - }) - - t.Run("GetSubnet", func(t *testing.T) { - defer mock.ClearTestCases() - - client.FollowPagination = false - addr := ipam.Address{Name: "a"} - - err := mock.AddTestCase(http.MethodGet, "/ipam/address/1", http.StatusOK, nil, nil, "", addr) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respAddr, _, err := client.IPAM.GetSubnet(1) - if err != nil { - t.Fatalf("error getting subnet: %v", err) - } - if respAddr.Name != addr.Name { - t.Errorf("wrong address returned, want=%+v, got=%+v", addr, respAddr) - } - }) - - t.Run("Children", func(t *testing.T) { - t.Run("Pagination", func(t *testing.T) { - defer mock.ClearTestCases() - - client.FollowPagination = true - addrs := []ipam.Address{ - {Name: "a"}, - {Name: "b"}, - {Name: "c"}, - {Name: "d"}, - } - err := mock.AddTestCase(http.MethodGet, "ipam/address/1/children", http.StatusOK, nil, nil, "", addrs) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respAddrs, _, err := client.IPAM.GetChildren(1) - if err != nil { - t.Fatalf("error listing child subnets: %v", err) - } - if len(respAddrs) != len(addrs) { - t.Errorf("wrong length: want=%d, got=%d", len(addrs), len(respAddrs)) - } - - for i, addr := range respAddrs { - if addr.Name != addrs[i].Name { - t.Errorf("Incorrect name for address %d: want=%q, got=%q", i, addrs[i].Name, addr.Name) - } - } - }) - t.Run("No Pagination", func(t *testing.T) { - defer mock.ClearTestCases() - - client.FollowPagination = false - addrs := []ipam.Address{ - {Name: "a"}, - {Name: "b"}, - } - - linkHeader := `; rel="next"` - header := http.Header{} - header.Set("Link", linkHeader) - - err := mock.AddTestCase(http.MethodGet, "ipam/address/1/children", http.StatusOK, nil, header, "", addrs) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respAddrs, resp, err := client.IPAM.GetChildren(1) - if err != nil { - t.Fatalf("error listing child subnets %v", err) - } - if len(respAddrs) != len(addrs) { - t.Errorf("wrong length: want=%d, got=%d", len(addrs), len(respAddrs)) - } - if l := resp.Header.Get("Link"); l != linkHeader { - t.Errorf("wrong Link header found: want=%q, got=%q", linkHeader, l) - } - - for i, addr := range respAddrs { - if addr.Name != addrs[i].Name { - t.Errorf("Incorrect name for address %d: want=%q, got=%q", i, addrs[i].Name, addr.Name) - } - } - }) - }) - - t.Run("GetParent", func(t *testing.T) { - defer mock.ClearTestCases() - - addr := ipam.Address{Name: "a"} - - err := mock.AddTestCase(http.MethodGet, "/ipam/address/1/parent", http.StatusOK, nil, nil, "", addr) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respAddr, _, err := client.IPAM.GetParent(1) - if err != nil { - t.Fatalf("error getting subnet: %v", err) - } - if respAddr.Name != addr.Name { - t.Errorf("wrong address returned, want=%+v, got=%+v", addr, respAddr) - } - }) - - t.Run("Create", func(t *testing.T) { - defer mock.ClearTestCases() - - t.Run("RequiredParams", func(t *testing.T) { - addr := &ipam.Address{Name: "a", Network: 1} - _, _, err = client.IPAM.CreateSubnet(addr) - if err == nil { - t.Errorf("expected a missing prefix to result in an error") - } - addr = &ipam.Address{Name: "a", Prefix: "127.0.1.0/24"} - _, _, err = client.IPAM.CreateSubnet(addr) - if err == nil { - t.Errorf("expected a missing network to result in an error") - } - }) - - addr := &ipam.Address{Name: "a", Prefix: "127.0.1.0/24", Network: 1} - err := mock.AddTestCase(http.MethodPut, "/ipam/address", http.StatusCreated, nil, nil, addr, addr) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - respAddr, _, err := client.IPAM.CreateSubnet(addr) - if err != nil { - t.Fatalf("error creating subnet: %v", err) - } - if respAddr.Name != addr.Name { - t.Errorf("wrong address returned: want=%+v, got=%+v", addr, respAddr) - } - }) - - t.Run("EditSubnet", func(t *testing.T) { - t.Run("RequiredParams", func(t *testing.T) { - addr := &ipam.Address{Name: "a"} - _, _, _, err = client.IPAM.EditSubnet(addr, true) - if err == nil { - t.Errorf("expected a missing ID to result in an error") - } - }) - - t.Run("No Parent", func(t *testing.T) { - defer mock.ClearTestCases() - - addr := &ipam.Address{ID: 1, Name: "a"} - err := mock.AddTestCase(http.MethodPost, "/ipam/address/1", http.StatusOK, nil, nil, addr, addr) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - respAddr, parent, _, err := client.IPAM.EditSubnet(addr, false) - if err != nil { - t.Fatalf("error editing subnet: %v", err) - } - if respAddr.Name != addr.Name { - t.Errorf("wrong address returned: want=%+v, got=%+v", addr, respAddr) - } - if parent != nil { - t.Errorf("non-nil parent unexpectedly returned") - } - }) - t.Run("With Parent", func(t *testing.T) { - defer mock.ClearTestCases() - - addr := &ipam.Address{ID: 1, Name: "a"} - err := mock.AddTestCase(http.MethodPost, "/ipam/address/1?parent=true", http.StatusOK, nil, nil, addr, addr) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - respAddr, parent, _, err := client.IPAM.EditSubnet(addr, true) - if err != nil { - t.Fatalf("error editing subnet: %v", err) - } - if respAddr.Name != addr.Name { - t.Errorf("wrong address returned: want=%+v, got=%+v", addr, respAddr) - } - if parent == nil { - t.Errorf("nil parent unexpectedly returned") - } - }) - }) - - t.Run("SplitSubnet", func(t *testing.T) { - defer mock.ClearTestCases() - - const prefix = 29 - addr := &ipam.Address{Name: "a", Prefix: "127.0.1.0/24", Network: 1} - err := mock.AddTestCase(http.MethodPost, "/ipam/address/1/split", http.StatusCreated, nil, nil, - struct { - Prefix int `json:"prefix"` - }{ - Prefix: prefix, - }, - struct { - RootAddr int `json:"root_address_id"` - PrefixIDs []int `json:"prefix_ids"` - }{ - RootAddr: 1, - PrefixIDs: []int{1}, - }, - ) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - rootAddr, prefixIDs, _, err := client.IPAM.SplitSubnet(1, prefix) - if err != nil { - t.Fatalf("error creating subnet: %v", err) - } - if len(prefixIDs) != 1 { - t.Errorf("wrong number of new subnets returned: want=1, got=%d", len(prefixIDs)) - } - if rootAddr != 1 { - t.Errorf("wrong root addr ID returned: want=%d, got=%d", addr.ID, rootAddr) - } - }) - - t.Run("MergeSubnet", func(t *testing.T) { - defer mock.ClearTestCases() - - addr := &ipam.Address{Name: "a"} - err := mock.AddTestCase(http.MethodPost, "/ipam/address/merge", http.StatusCreated, nil, nil, - struct { - Root int `json:"root_address_id"` - Merge int `json:"merged_address_id"` - }{ - Root: 1, - Merge: 2, - }, - addr, - ) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - newAddr, _, err := client.IPAM.MergeSubnet(1, 2) - if err != nil { - t.Fatalf("error creating subnet: %v", err) - } - if newAddr.Name != addr.Name { - t.Errorf("wrong addr returned: want=%+v, got=%+v", addr, newAddr) - } - }) - - t.Run("DeleteSubnet", func(t *testing.T) { - defer mock.ClearTestCases() - - err := mock.AddTestCase(http.MethodDelete, "/ipam/address/1", http.StatusCreated, nil, nil, "", nil) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - _, err = client.IPAM.DeleteSubnet(1) - if err != nil { - t.Fatalf("error deleting subnet: %v", err) - } - }) -} diff --git a/rest/model/account/permissions.go b/rest/model/account/permissions.go index 55982f2..46c5b92 100644 --- a/rest/model/account/permissions.go +++ b/rest/model/account/permissions.go @@ -7,10 +7,6 @@ type PermissionsMap struct { Account PermissionsAccount `json:"account"` Monitoring PermissionsMonitoring `json:"monitoring"` Security *PermissionsSecurity `json:"security,omitempty"` - - // DHCP and IPAM are only relevant for DDI and should not be provided in managed. - DHCP *PermissionsDHCP `json:"dhcp,omitempty"` - IPAM *PermissionsIPAM `json:"ipam,omitempty"` } // PermissionsDNS wraps a User's "permissions.dns" attribute @@ -63,31 +59,6 @@ type PermissionsMonitoring struct { DeleteJobs bool `json:"delete_jobs"` } -// PermissionsDHCP wraps a User's "permissions.dhcp" attribute for DDI. -type PermissionsDHCP struct { - ManageDHCP bool `json:"manage_dhcp"` - ViewDHCP bool `json:"view_dhcp"` - // The fields below are only relevant in DDI v2.5+ - TagsAllow *[]AuthTag `json:"tags_allow,omitempty"` - TagsDeny *[]AuthTag `json:"tags_deny,omitempty"` -} - -// PermissionsIPAM wraps a User's "permissions.ipam" attribute for DDI. -type PermissionsIPAM struct { - ManageIPAM bool `json:"manage_ipam"` - ViewIPAM bool `json:"view_ipam"` - // The fields below are only relevant in DDI v2.5+ - TagsAllow *[]AuthTag `json:"tags_allow,omitempty"` - TagsDeny *[]AuthTag `json:"tags_deny,omitempty"` -} - -// AuthTag wraps the tags used in "tags_allow" and "tags_deny" in DDI and IPAM permissions in DDI. -// Tag Names must start with prefix "auth:" -type AuthTag struct { - Name string `json:"name"` - Value string `json:"value"` -} - // PermissionsRecord wraps a User's "permissions.record" attribute type PermissionsRecord struct { Domain string `json:"domain"` diff --git a/rest/model/dhcp/doc.go b/rest/model/dhcp/doc.go deleted file mode 100644 index 2241225..0000000 --- a/rest/model/dhcp/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package dhcp contains definitions for scope groups, scopes, etc. -package dhcp diff --git a/rest/model/dhcp/option.go b/rest/model/dhcp/option.go deleted file mode 100644 index c94a954..0000000 --- a/rest/model/dhcp/option.go +++ /dev/null @@ -1,91 +0,0 @@ -package dhcp - -// Option encapsulates DHCPv4 and DHCPv6 option information. -// -// Serialized: -// -// '{"name": "dhcpv4/routers", "value": ["127.0.0.1"]}' -// '{"name": "dhcpv4/boot-file-name", "value": "/bootfilename"}' -// '{"name": "dhcpv6/dns-servers", "value": ["2001:db8::cafe"]}' -type Option struct { - Name string `json:"name"` - Value interface{} `json:"value"` - AlwaysSend *bool `json:"always_send,omitempty"` - Encapsulate *string `json:"encapsulate,omitempty"` -} - -// OptionSet is a convenience type for marshalling an array of options to and from a JSON field. -type OptionSet []Option - -// ItemType defines the type of the item as a string -type ItemType string - -// List of available types -const ( - ItemTypeBinary = "binary" - ItemTypeBoolean = "boolean" - ItemTypeEmpty = "empty" - ItemTypeFQDN = "fqdn" - ItemTypeInt16 = "int16" - ItemTypeInt32 = "int32" - ItemTypeInt8 = "int8" - ItemTypeIPv4Address = "ipv4_address" - ItemTypeIPv6Address = "ipv6_address" - ItemTypeIPv6Prefix = "ipv6_prefix" - ItemTypePSID = "psid" - ItemTypeString = "string" - ItemTypeTuple = "tuple" - ItemTypeUint16 = "uint16" - ItemTypeUint32 = "uint32" - ItemTypeUint8 = "uint8" -) - -// OptionDefSchemaItems is generated from https://apispec.ns1.com/v1/dhcp/schemas/option-definition-fields.json#/properties/schema/properties/fields/items -type OptionDefSchemaItems struct { - Name string `json:"name"` - Type ItemType `json:"type"` -} - -// SchemaType defines the type of the schema as a string -type SchemaType string - -// List of available types -const ( - SchemaTypeArray SchemaType = "array" - SchemaTypeBinary = "binary" - SchemaTypeBoolean = "boolean" - SchemaTypeEmpty = "empty" - SchemaTypeFQDN = "fqdn" - SchemaTypeInt16 = "int16" - SchemaTypeInt32 = "int32" - SchemaTypeInt8 = "int8" - SchemaTypeIPv4Address = "ipv4_address" - SchemaTypeIPv6Address = "ipv6_address" - SchemaTypeIPv6Prefix = "ipv6_prefix" - SchemaTypePSID = "psid" - SchemaTypeRecord = "record" - SchemaTypeString = "string" - SchemaTypeTuple = "tuple" - SchemaTypeUint16 = "uint16" - SchemaTypeUint32 = "uint32" - SchemaTypeUint8 = "uint8" -) - -// OptionDefSchema is a schema of the option def which describes the value that option can hold -type OptionDefSchema struct { - Fields []OptionDefSchemaItems `json:"fields"` - Items *string `json:"items,omitempty"` - MultipleFinalValue *bool `json:"multiple_final_value,omitempty"` - Type SchemaType `json:"type"` -} - -// OptionDef configures a custom option definition -// https://ftp.isc.org/isc/kea/1.4.0/doc/kea-guide.html#dhcp4-custom-options -type OptionDef struct { - Space *string `json:"space,omitempty"` - FriendlyName string `json:"friendly_name"` - Description string `json:"description"` - Code int `json:"code"` - Encapsulate *string `json:"encapsulate,omitempty"` - Schema OptionDefSchema `json:"schema"` -} diff --git a/rest/model/dhcp/reservation.go b/rest/model/dhcp/reservation.go deleted file mode 100644 index 4dd9a93..0000000 --- a/rest/model/dhcp/reservation.go +++ /dev/null @@ -1,38 +0,0 @@ -package dhcp - -// Reservation contains reservation related data -type Reservation struct { - ID *int `json:"id,omitempty"` - IDAddress *int `json:"address_id,omitempty"` - IDScopeGroup *int `json:"scope_group_id,omitempty"` - Mac string `json:"mac,omitempty"` - AddressDetails *AddressDetails `json:"address_details,omitempty"` - Identifier *Identifier `json:"identifier,omitempty"` - Options OptionSet `json:"options"` - DHCPv6 *bool `json:"dhcpv6,omitempty"` - ClientClasses []string `json:"client_classes,omitempty"` - NextServer string `json:"next_server,omitempty"` - BootFileName string `json:"boot_file_name,omitempty"` - ServerHostname string `json:"server_hostname,omitempty"` - - Tags map[string]string `json:"tags,omitempty"` - LocalTags []string `json:"local_tags,omitempty"` - BlockedTags []string `json:"blocked_tags,omitempty"` -} - -// IdentifierType is a type of the reservation identifier -type IdentifierType string - -// List of available Identifier types -const ( - HWAddressType IdentifierType = "hw-address" - CircuitIDType IdentifierType = "circuit-id" - DUIDType IdentifierType = "duid" - ClientIDType IdentifierType = "client-id" -) - -// Identifier is a reservation identifier -type Identifier struct { - Type IdentifierType `json:"type,omitempty"` - Value string `json:"value,omitempty"` -} diff --git a/rest/model/dhcp/scope.go b/rest/model/dhcp/scope.go deleted file mode 100644 index e2d9a02..0000000 --- a/rest/model/dhcp/scope.go +++ /dev/null @@ -1,30 +0,0 @@ -package dhcp - -// Scope contains scope related data -type Scope struct { - ID int `json:"id,omitempty"` - IDAddress *int `json:"address_id,omitempty"` - IDScopeGroup *int `json:"scope_group_id,omitempty"` - Options OptionSet `json:"options,omitempty"` - AddressDetails *AddressDetails `json:"address_details,omitempty"` - ValidLifetimeSecs *int `json:"valid_lifetime_secs,omitempty"` - ClientClass *string `json:"client_class,omitempty"` - RequireClientClasses *[]string `json:"require_client_classes,omitempty"` - MatchClientID []byte `json:"match_client_id,omitempty"` - Relays []string `json:"relays,omitempty"` - PingCheckEnabled *bool `json:"ping_check_enabled,omitempty"` - NextServer *string `json:"next_server,omitempty"` - BootFileName *string `json:"boot_file_name,omitempty"` - ServerHostname *string `json:"server_hostname,omitempty"` - Stateless *bool `json:"stateless,omitempty"` - - Tags map[string]string `json:"tags,omitempty"` - BlockedTags []string `json:"blocked_tags,omitempty"` - LocalTags []string `json:"local_tags,omitempty"` -} - -// AddressDetails scope address details -type AddressDetails struct { - Name string `json:"name,omitempty"` - Prefix string `json:"prefix,omitempty"` -} diff --git a/rest/model/dhcp/scopegroup.go b/rest/model/dhcp/scopegroup.go deleted file mode 100644 index 7322843..0000000 --- a/rest/model/dhcp/scopegroup.go +++ /dev/null @@ -1,113 +0,0 @@ -package dhcp - -// SynthesizeDNSRecords ddns configuration -type SynthesizeDNSRecords struct { - Enabled bool `json:"enabled,omitempty"` -} - -// Settings encapsulates common values between SettingsV4 and SettingsV6 -type Settings struct { - // Enabled indicates whether v4 or v6 is enabled - Enabled *bool `json:"enabled,omitempty"` - - // ValidLifetimeSecs how long leases given out by the server are valid - ValidLifetimeSecs *int `json:"valid_lifetime_secs,omitempty"` - - // RenewTimerSecs length of DHCP T1 timer - RenewTimerSecs *int `json:"renew_timer_secs,omitempty"` - - // RebindTimerSecs length of DHCP T2 timer - RebindTimerSecs *int `json:"rebind_timer_secs,omitempty"` - - // EchoClientID https://tools.ietf.org/html/rfc6842 - EchoClientID *bool `json:"echo_client_id,omitempty"` - - // Options base dhcp options -- will be combined with later levels; should effectively be unique by option name - Options OptionSet `json:"options"` - - SynthesizeDNSRecords *SynthesizeDNSRecords `json:"nsone-ddns,omitempty"` - - QualifyingSuffix *string `json:"qualifying_suffix,omitempty"` - GeneratedPrefix *string `json:"generated_prefix,omitempty"` - - // DeclineProbationPeriod how long lease will stay unavailable for assignment after DHCPDECLINE - DeclineProbationPeriod *int `json:"decline_probation_period,omitempty"` - - // ReclaimTimerWaitTime interval between reclamation cycles in seconds - ReclaimTimerWaitTime *int `json:"reclaim_timer_wait_time,omitempty"` - - // FlushReclaimedTimerWaitTime how often the server initiates lease reclamation in seconds - FlushReclaimedTimerWaitTime *int `json:"flush_reclaimed_timer_wait_time,omitempty"` - - // HoldReclaimedTime how long the lease should be kept after it is reclaimed in seconds - HoldReclaimedTime *int `json:"hold_reclaimed_time,omitempty"` - - // MaxReclaimLeases maximum number of leases to process at once (zero is unlimited) - MaxReclaimLeases *int `json:"max_reclaim_leases,omitempty"` - - // MaxReclaimTime upper limit to the length of time a lease reclamation procedure can take - // (in milliseconds) - MaxReclaimTime *int `json:"max_reclaim_time,omitempty"` - - // UnwarnedReclaimCycles how many consecutive cycles must end with remaining leases before a warning - // is printed - UnwarnedReclaimCycles *int `json:"unwarned_reclaim_cycles,omitempty"` -} - -// SettingsV4 defines those DHCPv4 settings which we expose to the user -type SettingsV4 struct { - Settings - - MatchClientID *bool `json:"match_client_id"` - NextServer *string `json:"next_server"` - BootFileName *string `json:"boot_file_name"` -} - -// SettingsV6 defines those DHCPv6 settings which we expose to the user -type SettingsV6 struct { - Settings - - // https://tools.ietf.org/html/rfc3315 - PreferredLifetimeSecs *int `json:"preferred_lifetime_secs,omitempty"` -} - -const ( - // PingCheckProbationPeriodDefault is a default value in minutes for the probation_period in the ping check config - PingCheckProbationPeriodDefault = 60 - - // PingCheckNumPingsDefault is a default value for the num_pings in the ping check config - PingCheckNumPingsDefault = 1 - - // PingCheckWaitTimeDefault is a default value in milliseconds for the wait_time in the ping check config - PingCheckWaitTimeDefault = 500 -) - -// PingCheckConf represents config for a ping check in a scope group -type PingCheckConf struct { - Enabled bool `json:"enabled,omitempty"` - NumPings *int `json:"num_pings,omitempty"` - ProbationPeriod *int `json:"probation_period,omitempty"` - Type string `json:"type,omitempty"` - WaitTime *int `json:"wait_time,omitempty"` -} - -// ScopeGroup wraps an NS1 /dhcp/scopegroup resource. -type ScopeGroup struct { - ID *int `json:"id,omitempty"` - Name string `json:"name,omitempty"` - IDDHCPService *int `json:"dhcp_service_id,omitempty"` - DHCP4 SettingsV4 `json:"dhcpv4,omitempty"` - DHCP6 SettingsV6 `json:"dhcpv6,omitempty"` - NetworkID *int `json:"network_id,omitempty"` - ReverseDNS *bool `json:"reverse_dns,omitempty"` - ClientClassIds []int `json:"client_class_ids,omitempty"` - PingCheck *PingCheckConf `json:"ping_check,omitempty"` - - Tags map[string]string `json:"tags,omitempty"` - LocalTags []string `json:"local_tags"` - BlockedTags []string `json:"blocked_tags"` - - // TemplateConfig is read-only field - TemplateConfig []string `json:"template_config,omitempty"` - Template *string `json:"template,omitempty"` -} diff --git a/rest/model/ipam/doc.go b/rest/model/ipam/doc.go deleted file mode 100644 index 7530f3e..0000000 --- a/rest/model/ipam/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package ipam contains definitions for networks, addresses, etc. -package ipam diff --git a/rest/model/ipam/network.go b/rest/model/ipam/network.go deleted file mode 100644 index 589df9b..0000000 --- a/rest/model/ipam/network.go +++ /dev/null @@ -1,30 +0,0 @@ -package ipam - -// AddrStatus is the status of an address. -type AddrStatus string - -// A list of valid address statuses. -// The default status is StatusPlanned. -const ( - StatusPlanned AddrStatus = "planned" - StatusAssigned AddrStatus = "assigned" -) - -// Address wraps an NS1 /ipam/address resource. -type Address struct { - ID int `json:"id"` - Desc string `json:"desc,omitempty"` - Prefix string `json:"prefix"` - Name string `json:"name,omitempty"` - Network int `json:"network_id,omitempty"` - Status AddrStatus `json:"status,omitempty"` - InheritedTags string `json:"inherited_tags,omitempty"` - Total string `json:"total_addresses"` - Children int `json:"children"` - Free string `json:"free_addresses"` - Used string `json:"used_addresses"` - KVPS map[string]interface{} `json:"kvps,omitempty"` - Tags map[string]interface{} `json:"tags"` - DHCPScoped bool `json:"dhcp_scoped"` - Parent int `json:"parent_id"` -} diff --git a/rest/optiondef.go b/rest/optiondef.go deleted file mode 100644 index ace2b96..0000000 --- a/rest/optiondef.go +++ /dev/null @@ -1,91 +0,0 @@ -package rest - -import ( - "errors" - "fmt" - "net/http" - - "gopkg.in/ns1/ns1-go.v2/rest/model/dhcp" -) - -// OptionDefService handles the 'scope group' endpoints. -type OptionDefService service - -// List returns a list of all option definitions. -// -// NS1 API docs: https://ns1.com/api#getlist-dhcp-option-definitions -func (s *OptionDefService) List() ([]dhcp.OptionDef, *http.Response, error) { - req, err := s.client.NewRequest(http.MethodGet, "dhcp/optiondef", nil) - if err != nil { - return nil, nil, err - } - - ods := make([]dhcp.OptionDef, 0) - resp, err := s.client.Do(req, &ods) - return ods, resp, err -} - -// Get returns the option definition corresponding to the provided ID. -// -// NS1 API docs: https://ns1.com/api#getview-dhcp-option-definition -func (s *OptionDefService) Get(odSpace, odKey string) (*dhcp.OptionDef, *http.Response, error) { - reqPath := fmt.Sprintf("dhcp/optiondef/%s/%s", odSpace, odKey) - req, err := s.client.NewRequest(http.MethodGet, reqPath, nil) - if err != nil { - return nil, nil, err - } - - od := &dhcp.OptionDef{} - var resp *http.Response - resp, err = s.client.Do(req, od) - if err != nil { - return nil, resp, err - } - - return od, resp, nil -} - -// Create creates or updates an option definition. -// The FriendlyName, Description, Code, Schema.Type fields are required. -// -// NS1 API docs: https://ns1.com/api#putcreate-an-custom-dhcp-option-definition -func (s *OptionDefService) Create(od *dhcp.OptionDef, odSpace, odKey string) (*dhcp.OptionDef, *http.Response, error) { - switch { - case od.FriendlyName == "": - return nil, nil, errors.New("the FriendlyName field is required") - case od.Description == "": - return nil, nil, errors.New("the Description field is required") - case od.Code == 0: - return nil, nil, errors.New("the Code field is required") - case od.Schema.Type == "": - return nil, nil, errors.New("the Schema.Type field is required") - } - - reqPath := fmt.Sprintf("dhcp/optiondef/%s/%s", odSpace, odKey) - req, err := s.client.NewRequest(http.MethodPut, reqPath, od) - if err != nil { - return nil, nil, err - } - - respOd := new(dhcp.OptionDef) - var resp *http.Response - resp, err = s.client.Do(req, respOd) - if err != nil { - return nil, resp, err - } - - return respOd, resp, nil -} - -// Delete removes a option definition entirely. -// -// NS1 API docs: https://ns1.com/api#deletedelete-a-custom-dhcp-option-definition -func (s *OptionDefService) Delete(odSpace, odKey string) (*http.Response, error) { - reqPath := fmt.Sprintf("dhcp/optiondef/%s/%s", odSpace, odKey) - req, err := s.client.NewRequest(http.MethodDelete, reqPath, nil) - if err != nil { - return nil, err - } - - return s.client.Do(req, nil) -} diff --git a/rest/optiondef_test.go b/rest/optiondef_test.go deleted file mode 100644 index 096999b..0000000 --- a/rest/optiondef_test.go +++ /dev/null @@ -1,165 +0,0 @@ -package rest_test - -import ( - "net/http" - "testing" - - "gopkg.in/ns1/ns1-go.v2/rest/model/dhcp" - - "gopkg.in/ns1/ns1-go.v2/mockns1" - api "gopkg.in/ns1/ns1-go.v2/rest" -) - -func TestDHCPOptionDef(t *testing.T) { - mock, doer, err := mockns1.New(t) - if err != nil { - t.Fatalf("Error creating mock service: %v", err) - } - defer mock.Shutdown() - - client := api.NewClient(doer, api.SetEndpoint("https://"+mock.Address+"/v1/")) - - t.Run("List", func(t *testing.T) { - defer mock.ClearTestCases() - - client.FollowPagination = true - sgs := []dhcp.OptionDef{ - { - Code: 1, - Description: "a", - FriendlyName: "a", - Schema: dhcp.OptionDefSchema{ - Type: dhcp.SchemaTypeString, - }, - }, - { - Code: 2, - Description: "b", - FriendlyName: "b", - Schema: dhcp.OptionDefSchema{ - Type: dhcp.SchemaTypeString, - }, - }, - } - err := mock.AddTestCase(http.MethodGet, "/dhcp/optiondef", http.StatusOK, nil, nil, "", sgs) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respSgs, _, err := client.OptionDef.List() - if err != nil { - t.Fatalf("error listing DHCP option definitions: %v", err) - } - if len(respSgs) != len(sgs) { - t.Errorf("wrong length: want=%d, got=%d", len(sgs), len(respSgs)) - } - - for i, sg := range respSgs { - if sg.FriendlyName != sgs[i].FriendlyName || sg.Code != sgs[i].Code || - sg.Description != sgs[i].Description || sg.Schema.Type != sgs[i].Schema.Type { - t.Errorf("Incorrect data for option definition %d: want=%+v, got=%+v", i, sgs[i], sg) - } - } - }) - - t.Run("Get", func(t *testing.T) { - defer mock.ClearTestCases() - - client.FollowPagination = false - od := dhcp.OptionDef{ - Code: 1, - Description: "a", - FriendlyName: "a", - Schema: dhcp.OptionDefSchema{ - Type: dhcp.SchemaTypeString, - }, - } - - err := mock.AddTestCase(http.MethodGet, "/dhcp/optiondef/space/key", http.StatusOK, nil, nil, "", od) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respOD, _, err := client.OptionDef.Get("space", "key") - if err != nil { - t.Fatalf("error getting scpe group: %v", err) - } - if od.FriendlyName != respOD.FriendlyName || od.Code != respOD.Code || - od.Description != respOD.Description || od.Schema.Type != respOD.Schema.Type { - t.Errorf("wrong option def returned, want=%+v, got=%+v", od, respOD) - } - }) - - t.Run("Create", func(t *testing.T) { - defer mock.ClearTestCases() - - t.Run("RequiredParams", func(t *testing.T) { - od := &dhcp.OptionDef{} - _, _, err = client.OptionDef.Create(od, "space", "key") - if err == nil { - t.Errorf("expected a missing code to result in an error") - } - - od = &dhcp.OptionDef{ - Code: 1, - } - _, _, err = client.OptionDef.Create(od, "space", "key") - if err == nil { - t.Errorf("expected a missing friendly name to result in an error") - } - - od = &dhcp.OptionDef{ - Code: 1, - Description: "a", - } - _, _, err = client.OptionDef.Create(od, "space", "key") - if err == nil { - t.Errorf("expected a missing description to result in an error") - } - - od = &dhcp.OptionDef{ - Code: 1, - Description: "a", - FriendlyName: "a", - } - _, _, err = client.OptionDef.Create(od, "space", "key") - if err == nil { - t.Errorf("expected a missing schema type to result in an error") - } - }) - - od := &dhcp.OptionDef{ - Code: 1, - Description: "a", - FriendlyName: "a", - Schema: dhcp.OptionDefSchema{ - Type: dhcp.SchemaTypeString, - }, - } - err := mock.AddTestCase(http.MethodPut, "/dhcp/optiondef/space/key", http.StatusCreated, nil, nil, od, od) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - respOD, _, err := client.OptionDef.Create(od, "space", "key") - if err != nil { - t.Fatalf("error creating option definition: %v", err) - } - if od.FriendlyName != respOD.FriendlyName || od.Code != respOD.Code || - od.Description != respOD.Description || od.Schema.Type != respOD.Schema.Type { - t.Errorf("wrong option def returned, want=%+v, got=%+v", od, respOD) - } - }) - - t.Run("Delete", func(t *testing.T) { - defer mock.ClearTestCases() - - err := mock.AddTestCase(http.MethodDelete, "/dhcp/optiondef/space/key", http.StatusNoContent, nil, nil, "", nil) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - _, err = client.OptionDef.Delete("space", "key") - if err != nil { - t.Fatalf("error deleting option definition: %v", err) - } - }) -} diff --git a/rest/reservation.go b/rest/reservation.go deleted file mode 100644 index a3747d0..0000000 --- a/rest/reservation.go +++ /dev/null @@ -1,114 +0,0 @@ -package rest - -import ( - "errors" - "fmt" - "net/http" - - "gopkg.in/ns1/ns1-go.v2/rest/model/dhcp" -) - -// ReservationService handles the 'reservation' endpoints. -type ReservationService service - -// List returns a list of all reservations. -// -// NS1 API docs: https://ns1.com/api#getlist-reservations -func (s *ReservationService) List() ([]dhcp.Reservation, *http.Response, error) { - req, err := s.client.NewRequest(http.MethodGet, "dhcp/reservation", nil) - if err != nil { - return nil, nil, err - } - - scs := make([]dhcp.Reservation, 0) - resp, err := s.client.Do(req, &scs) - if err != nil { - return nil, resp, err - } - - return scs, resp, nil -} - -// Get returns the reservation corresponding to the provided reservation ID. -// -// NS1 API docs: https://ns1.com/api#getview-a-reservations-details -func (s *ReservationService) Get(scID int) (*dhcp.Reservation, *http.Response, error) { - reqPath := fmt.Sprintf("dhcp/reservation/%d", scID) - req, err := s.client.NewRequest(http.MethodGet, reqPath, nil) - if err != nil { - return nil, nil, err - } - - sc := &dhcp.Reservation{} - var resp *http.Response - resp, err = s.client.Do(req, sc) - if err != nil { - return nil, resp, err - } - - return sc, resp, nil -} - -// Create creates a reservation. -// The Options field is required. -// -// NS1 API docs: https://ns1.com/api#putcreate-a-reservation -func (s *ReservationService) Create(sc *dhcp.Reservation) (*dhcp.Reservation, *http.Response, error) { - switch { - case sc.Options == nil: - return nil, nil, errors.New("the Options field is required") - } - - req, err := s.client.NewRequest(http.MethodPut, "dhcp/reservation", sc) - if err != nil { - return nil, nil, err - } - - respSc := new(dhcp.Reservation) - var resp *http.Response - resp, err = s.client.Do(req, respSc) - if err != nil { - return nil, resp, err - } - - return respSc, resp, nil -} - -// Edit updates an existing reservation. -// The ID, Options fields are required. -// -// NS1 API docs: https://ns1.com/api#postmodify-a-reservation -func (s *ReservationService) Edit(sc *dhcp.Reservation) (*dhcp.Reservation, *http.Response, error) { - switch { - case sc.ID == nil: - return nil, nil, errors.New("the ID field is required") - case sc.Options == nil: - return nil, nil, errors.New("the Options field is required") - } - - reqPath := fmt.Sprintf("dhcp/reservation/%d", *sc.ID) - req, err := s.client.NewRequest(http.MethodPost, reqPath, sc) - if err != nil { - return nil, nil, err - } - - resp, err := s.client.Do(req, sc) - if err != nil { - return nil, resp, err - } - - return sc, resp, nil -} - -// Delete removes a reservation entirely. -// -// NS1 API docs: https://ns1.com/api#deletedelete-a-reservation -func (s *ReservationService) Delete(id int) (*http.Response, error) { - reqPath := fmt.Sprintf("dhcp/reservation/%d", id) - req, err := s.client.NewRequest(http.MethodDelete, reqPath, nil) - if err != nil { - return nil, err - } - - return s.client.Do(req, nil) -} diff --git a/rest/reservation_test.go b/rest/reservation_test.go deleted file mode 100644 index 480e673..0000000 --- a/rest/reservation_test.go +++ /dev/null @@ -1,156 +0,0 @@ -package rest_test - -import ( - "net/http" - "testing" - - "gopkg.in/ns1/ns1-go.v2/rest/model/dhcp" - - "gopkg.in/ns1/ns1-go.v2/mockns1" - api "gopkg.in/ns1/ns1-go.v2/rest" -) - -func TestDHCPReservation(t *testing.T) { - mock, doer, err := mockns1.New(t) - if err != nil { - t.Fatalf("Error creating mock service: %v", err) - } - defer mock.Shutdown() - - client := api.NewClient(doer, api.SetEndpoint("https://"+mock.Address+"/v1/")) - - t.Run("List", func(t *testing.T) { - defer mock.ClearTestCases() - - client.FollowPagination = true - idAddr := 1 - sgs := []dhcp.Reservation{ - {IDAddress: &idAddr}, - {IDAddress: &idAddr}, - {IDAddress: &idAddr}, - {IDAddress: &idAddr}, - } - err := mock.AddTestCase(http.MethodGet, "/dhcp/reservation", http.StatusOK, nil, nil, "", sgs) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respSgs, _, err := client.Reservation.List() - if err != nil { - t.Fatalf("error listing reservations: %v", err) - } - if len(respSgs) != len(sgs) { - t.Errorf("wrong length: want=%d, got=%d", len(sgs), len(respSgs)) - } - - for i, sg := range respSgs { - if *sg.IDAddress != *sgs[i].IDAddress { - t.Errorf("Incorrect name for reservation %d: want=%q, got=%q", i, *sgs[i].IDAddress, *sg.IDAddress) - } - } - }) - - t.Run("Get", func(t *testing.T) { - defer mock.ClearTestCases() - - idAddr := 1 - sg := dhcp.Reservation{IDAddress: &idAddr} - - err := mock.AddTestCase(http.MethodGet, "/dhcp/reservation/1", http.StatusOK, nil, nil, "", sg) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respAddr, _, err := client.Reservation.Get(1) - if err != nil { - t.Fatalf("error getting reservation: %v", err) - } - if *respAddr.IDAddress != *sg.IDAddress { - t.Errorf("wrong reservation returned, want=%+v, got=%+v", sg, respAddr) - } - }) - - t.Run("Create", func(t *testing.T) { - defer mock.ClearTestCases() - - t.Run("RequiredParams", func(t *testing.T) { - sg := &dhcp.Reservation{} - _, _, err = client.Reservation.Create(sg) - if err == nil { - t.Errorf("expected a missing address id to result in an error") - } - }) - - idAddr := 123 - sg := &dhcp.Reservation{ - IDAddress: &idAddr, - Options: make([]dhcp.Option, 0), - } - err := mock.AddTestCase(http.MethodPut, "/dhcp/reservation", http.StatusCreated, nil, nil, sg, sg) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - respSG, _, err := client.Reservation.Create(sg) - if err != nil { - t.Fatalf("error creating reservation: %v", err) - } - if *respSG.IDAddress != *sg.IDAddress { - t.Errorf("wrong reservation returned: want=%+v, got=%+v", sg, respSG) - } - }) - - t.Run("Edit", func(t *testing.T) { - t.Run("RequiredParams", func(t *testing.T) { - idAddr := 123 - sg := &dhcp.Reservation{ - IDAddress: &idAddr, - Options: make([]dhcp.Option, 0), - } - _, _, err = client.Reservation.Edit(sg) - if err == nil { - t.Errorf("expected a missing ID to result in an error") - } - - sg = &dhcp.Reservation{} - _, _, err = client.Reservation.Edit(sg) - if err == nil { - t.Errorf("expected a missing reservation ID to result in an error") - } - }) - - defer mock.ClearTestCases() - - id := 1 - idAddr := 123 - sg := &dhcp.Reservation{ - ID: &id, - IDAddress: &idAddr, - Options: make([]dhcp.Option, 0), - } - err := mock.AddTestCase(http.MethodPost, "/dhcp/reservation/1", http.StatusOK, nil, nil, sg, sg) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respSG, _, err := client.Reservation.Edit(sg) - if err != nil { - t.Fatalf("error editing reservation: %v", err) - } - if respSG.IDAddress != sg.IDAddress { - t.Errorf("wrong reservation returned: want=%+v, got=%+v", sg, respSG) - } - }) - - t.Run("Delete", func(t *testing.T) { - defer mock.ClearTestCases() - - err := mock.AddTestCase(http.MethodDelete, "/dhcp/reservation/1", http.StatusNoContent, nil, nil, "", nil) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - _, err = client.Reservation.Delete(1) - if err != nil { - t.Fatalf("error deleting reservation: %v", err) - } - }) -} diff --git a/rest/scope.go b/rest/scope.go deleted file mode 100644 index 7c70b65..0000000 --- a/rest/scope.go +++ /dev/null @@ -1,112 +0,0 @@ -package rest - -import ( - "errors" - "fmt" - "net/http" - - "gopkg.in/ns1/ns1-go.v2/rest/model/dhcp" -) - -// ScopeService handles the 'scope' endpoints. -type ScopeService service - -// List returns a list of all scopes. -// -// NS1 API docs: https://ns1.com/api#getlist-scopes -func (s *ScopeService) List() ([]dhcp.Scope, *http.Response, error) { - req, err := s.client.NewRequest(http.MethodGet, "dhcp/scope", nil) - if err != nil { - return nil, nil, err - } - - scs := make([]dhcp.Scope, 0) - resp, err := s.client.Do(req, &scs) - if err != nil { - return nil, resp, err - } - - return scs, resp, nil -} - -// Get returns the scope corresponding to the provided scope ID. -// -// NS1 API docs: https://ns1.com/api#getview-scope-details -func (s *ScopeService) Get(scID int) (*dhcp.Scope, *http.Response, error) { - reqPath := fmt.Sprintf("dhcp/scope/%d", scID) - req, err := s.client.NewRequest(http.MethodGet, reqPath, nil) - if err != nil { - return nil, nil, err - } - - sc := &dhcp.Scope{} - var resp *http.Response - resp, err = s.client.Do(req, sc) - if err != nil { - return nil, resp, err - } - - return sc, resp, nil -} - -// Create creates a scope. -// The IDAddress field is required. -// -// NS1 API docs: https://ns1.com/api#putcreate-a-scope -func (s *ScopeService) Create(sc *dhcp.Scope) (*dhcp.Scope, *http.Response, error) { - switch { - case sc.IDAddress == nil: - return nil, nil, errors.New("the IDAddress field is required") - } - - req, err := s.client.NewRequest(http.MethodPut, "dhcp/scope", sc) - if err != nil { - return nil, nil, err - } - - respSc := new(dhcp.Scope) - var resp *http.Response - resp, err = s.client.Do(req, respSc) - if err != nil { - return nil, resp, err - } - - return respSc, resp, nil -} - -// Edit updates an existing scope. -// The IDAddress field is required. -// -// NS1 API docs: https://ns1.com/api#postmodify-a-scope -func (s *ScopeService) Edit(sc *dhcp.Scope) (*dhcp.Scope, *http.Response, error) { - switch { - case sc.IDAddress == nil: - return nil, nil, errors.New("the IDAddress field is required") - } - - reqPath := fmt.Sprintf("dhcp/scope/%d", sc.ID) - req, err := s.client.NewRequest(http.MethodPost, reqPath, sc) - if err != nil { - return nil, nil, err - } - - resp, err := s.client.Do(req, sc) - if err != nil { - return nil, resp, err - } - - return sc, resp, nil -} - -// Delete removes a scope entirely. -// -// NS1 API docs: https://ns1.com/api#deleteremove-a-scope -func (s *ScopeService) Delete(id int) (*http.Response, error) { - reqPath := fmt.Sprintf("dhcp/scope/%d", id) - req, err := s.client.NewRequest(http.MethodDelete, reqPath, nil) - if err != nil { - return nil, err - } - - return s.client.Do(req, nil) -} diff --git a/rest/scope_test.go b/rest/scope_test.go deleted file mode 100644 index 24c24fa..0000000 --- a/rest/scope_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package rest_test - -import ( - "net/http" - "testing" - - "gopkg.in/ns1/ns1-go.v2/rest/model/dhcp" - - "gopkg.in/ns1/ns1-go.v2/mockns1" - api "gopkg.in/ns1/ns1-go.v2/rest" -) - -func TestDHCPScope(t *testing.T) { - mock, doer, err := mockns1.New(t) - if err != nil { - t.Fatalf("Error creating mock service: %v", err) - } - defer mock.Shutdown() - - client := api.NewClient(doer, api.SetEndpoint("https://"+mock.Address+"/v1/")) - - t.Run("List", func(t *testing.T) { - defer mock.ClearTestCases() - - client.FollowPagination = true - idAddr := 1 - sgs := []dhcp.Scope{ - {IDAddress: &idAddr}, - {IDAddress: &idAddr}, - {IDAddress: &idAddr}, - {IDAddress: &idAddr}, - } - err := mock.AddTestCase(http.MethodGet, "/dhcp/scope", http.StatusOK, nil, nil, "", sgs) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respSgs, _, err := client.Scope.List() - if err != nil { - t.Fatalf("error listing scopes: %v", err) - } - if len(respSgs) != len(sgs) { - t.Errorf("wrong length: want=%d, got=%d", len(sgs), len(respSgs)) - } - - for i, sg := range respSgs { - if *sg.IDAddress != *sgs[i].IDAddress { - t.Errorf("Incorrect name for scope %d: want=%q, got=%q", i, *sgs[i].IDAddress, *sg.IDAddress) - } - } - }) - - t.Run("Get", func(t *testing.T) { - defer mock.ClearTestCases() - - idAddr := 1 - sg := dhcp.Scope{IDAddress: &idAddr} - - err := mock.AddTestCase(http.MethodGet, "/dhcp/scope/1", http.StatusOK, nil, nil, "", sg) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respAddr, _, err := client.Scope.Get(1) - if err != nil { - t.Fatalf("error getting scope: %v", err) - } - if *respAddr.IDAddress != *sg.IDAddress { - t.Errorf("wrong scope returned, want=%+v, got=%+v", sg, respAddr) - } - }) - - t.Run("Create", func(t *testing.T) { - defer mock.ClearTestCases() - - t.Run("RequiredParams", func(t *testing.T) { - sg := &dhcp.Scope{} - _, _, err = client.Scope.Create(sg) - if err == nil { - t.Errorf("expected a missing address id to result in an error") - } - }) - - idAddr := 123 - sg := &dhcp.Scope{ - IDAddress: &idAddr, - } - err := mock.AddTestCase(http.MethodPut, "/dhcp/scope", http.StatusCreated, nil, nil, sg, sg) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - respSG, _, err := client.Scope.Create(sg) - if err != nil { - t.Fatalf("error creating scope: %v", err) - } - if *respSG.IDAddress != *sg.IDAddress { - t.Errorf("wrong scope returned: want=%+v, got=%+v", sg, respSG) - } - }) - - t.Run("Edit", func(t *testing.T) { - t.Run("RequiredParams", func(t *testing.T) { - idAddr := 123 - sg := &dhcp.Scope{IDAddress: &idAddr} - _, _, err = client.Scope.Edit(sg) - if err == nil { - t.Errorf("expected a missing ID to result in an error") - } - - sg = &dhcp.Scope{} - _, _, err = client.Scope.Edit(sg) - if err == nil { - t.Errorf("expected a missing address ID to result in an error") - } - }) - - defer mock.ClearTestCases() - - idAddr := 123 - sg := &dhcp.Scope{ - ID: 1, - IDAddress: &idAddr, - } - err := mock.AddTestCase(http.MethodPost, "/dhcp/scope/1", http.StatusOK, nil, nil, sg, sg) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respSG, _, err := client.Scope.Edit(sg) - if err != nil { - t.Fatalf("error editing scope: %v", err) - } - if respSG.IDAddress != sg.IDAddress { - t.Errorf("wrong address returned: want=%+v, got=%+v", sg, respSG) - } - }) - - t.Run("Delete", func(t *testing.T) { - defer mock.ClearTestCases() - - err := mock.AddTestCase(http.MethodDelete, "/dhcp/scope/1", http.StatusNoContent, nil, nil, "", nil) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - _, err = client.Scope.Delete(1) - if err != nil { - t.Fatalf("error deleting scope: %v", err) - } - }) -} diff --git a/rest/scopegroup.go b/rest/scopegroup.go deleted file mode 100644 index 8912e89..0000000 --- a/rest/scopegroup.go +++ /dev/null @@ -1,107 +0,0 @@ -package rest - -import ( - "errors" - "fmt" - "net/http" - - "gopkg.in/ns1/ns1-go.v2/rest/model/dhcp" -) - -// ScopeGroupService handles the 'scope group' endpoints. -type ScopeGroupService service - -// List returns a list of all scope groups. -// -// NS1 API docs: https://ns1.com/api#getlist-scope-groups -func (s *ScopeGroupService) List() ([]dhcp.ScopeGroup, *http.Response, error) { - req, err := s.client.NewRequest(http.MethodGet, "dhcp/scopegroup", nil) - if err != nil { - return nil, nil, err - } - - sgs := make([]dhcp.ScopeGroup, 0) - resp, err := s.client.Do(req, &sgs) - return sgs, resp, err -} - -// Get returns the Scope Group corresponding to the provided scope group ID. -// -// NS1 API docs: https://ns1.com/api#getview-scope-group -func (s *ScopeGroupService) Get(sgID int) (*dhcp.ScopeGroup, *http.Response, error) { - reqPath := fmt.Sprintf("dhcp/scopegroup/%d", sgID) - req, err := s.client.NewRequest(http.MethodGet, reqPath, nil) - if err != nil { - return nil, nil, err - } - - sg := &dhcp.ScopeGroup{} - var resp *http.Response - resp, err = s.client.Do(req, sg) - if err != nil { - return nil, resp, err - } - - return sg, resp, nil -} - -// Create creates a scope group. -// The Name field is required. -// -// NS1 API docs: https://ns1.com/api#putcreate-a-scope-group -func (s *ScopeGroupService) Create(sg *dhcp.ScopeGroup) (*dhcp.ScopeGroup, *http.Response, error) { - switch { - case sg.Name == "": - return nil, nil, errors.New("the Name field is required") - } - - req, err := s.client.NewRequest(http.MethodPut, "dhcp/scopegroup", sg) - if err != nil { - return nil, nil, err - } - - respSg := new(dhcp.ScopeGroup) - var resp *http.Response - resp, err = s.client.Do(req, respSg) - if err != nil { - return nil, resp, err - } - - return respSg, resp, nil -} - -// Edit updates an existing scope group. -// The ID field is required. -// -// NS1 API docs: https://ns1.com/api#postedit-scope-group -func (s *ScopeGroupService) Edit(sg *dhcp.ScopeGroup) (*dhcp.ScopeGroup, *http.Response, error) { - if sg.ID == nil { - return nil, nil, errors.New("the ID field is required") - } - - reqPath := fmt.Sprintf("dhcp/scopegroup/%d", *sg.ID) - req, err := s.client.NewRequest(http.MethodPost, reqPath, sg) - if err != nil { - return nil, nil, err - } - - resp, err := s.client.Do(req, sg) - if err != nil { - return nil, resp, err - } - - return sg, resp, nil -} - -// Delete removes a Scope Group entirely. -// -// NS1 API docs: https://ns1.com/api#deleteremove-scope-group-by-id -func (s *ScopeGroupService) Delete(id int) (*http.Response, error) { - reqPath := fmt.Sprintf("dhcp/scopegroup/%d", id) - req, err := s.client.NewRequest(http.MethodDelete, reqPath, nil) - if err != nil { - return nil, err - } - - return s.client.Do(req, nil) -} diff --git a/rest/scopegroup_test.go b/rest/scopegroup_test.go deleted file mode 100644 index c3a3971..0000000 --- a/rest/scopegroup_test.go +++ /dev/null @@ -1,157 +0,0 @@ -package rest_test - -import ( - "net/http" - "testing" - - "gopkg.in/ns1/ns1-go.v2/rest/model/dhcp" - - "gopkg.in/ns1/ns1-go.v2/mockns1" - api "gopkg.in/ns1/ns1-go.v2/rest" -) - -func TestDHCPScopeGroup(t *testing.T) { - mock, doer, err := mockns1.New(t) - if err != nil { - t.Fatalf("Error creating mock service: %v", err) - } - defer mock.Shutdown() - - client := api.NewClient(doer, api.SetEndpoint("https://"+mock.Address+"/v1/")) - - t.Run("List", func(t *testing.T) { - defer mock.ClearTestCases() - - client.FollowPagination = true - sgs := []dhcp.ScopeGroup{ - {Name: "a"}, - {Name: "b"}, - {Name: "c"}, - {Name: "d"}, - } - err := mock.AddTestCase(http.MethodGet, "/dhcp/scopegroup", http.StatusOK, nil, nil, "", sgs) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respSgs, _, err := client.ScopeGroup.List() - if err != nil { - t.Fatalf("error listing IPAM addresses: %v", err) - } - if len(respSgs) != len(sgs) { - t.Errorf("wrong length: want=%d, got=%d", len(sgs), len(respSgs)) - } - - for i, sg := range respSgs { - if sg.Name != sgs[i].Name { - t.Errorf("Incorrect name for scope group %d: want=%q, got=%q", i, sgs[i].Name, sg.Name) - } - } - }) - - t.Run("Get", func(t *testing.T) { - defer mock.ClearTestCases() - - client.FollowPagination = false - sg := dhcp.ScopeGroup{Name: "a"} - - err := mock.AddTestCase(http.MethodGet, "/dhcp/scopegroup/1", http.StatusOK, nil, nil, "", sg) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respAddr, _, err := client.ScopeGroup.Get(1) - if err != nil { - t.Fatalf("error getting scpe group: %v", err) - } - if respAddr.Name != sg.Name { - t.Errorf("wrong address returned, want=%+v, got=%+v", sg, respAddr) - } - }) - - t.Run("Create", func(t *testing.T) { - defer mock.ClearTestCases() - - t.Run("RequiredParams", func(t *testing.T) { - sg := &dhcp.ScopeGroup{} - _, _, err = client.ScopeGroup.Create(sg) - if err == nil { - t.Errorf("expected a missing name to result in an error") - } - }) - - validSecs := 123 - enabled := true - sg := &dhcp.ScopeGroup{ - Name: "a", - DHCP4: dhcp.SettingsV4{ - Settings: dhcp.Settings{ - Enabled: &enabled, - ValidLifetimeSecs: &validSecs, - }, - }, - } - err := mock.AddTestCase(http.MethodPut, "/dhcp/scopegroup", http.StatusCreated, nil, nil, sg, sg) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - respSG, _, err := client.ScopeGroup.Create(sg) - if err != nil { - t.Fatalf("error creating scope group: %v", err) - } - if respSG.Name != sg.Name || *respSG.DHCP4.ValidLifetimeSecs != *sg.DHCP4.ValidLifetimeSecs { - t.Errorf("wrong scope group returned: want=%+v, got=%+v", sg, respSG) - } - }) - - t.Run("Edit", func(t *testing.T) { - t.Run("RequiredParams", func(t *testing.T) { - sg := &dhcp.ScopeGroup{Name: "a"} - _, _, err = client.ScopeGroup.Edit(sg) - if err == nil { - t.Errorf("expected a missing ID to result in an error") - } - }) - - defer mock.ClearTestCases() - - enabled := true - id := 1 - validSecs := 123 - sg := &dhcp.ScopeGroup{ - ID: &id, - Name: "a", - DHCP4: dhcp.SettingsV4{ - Settings: dhcp.Settings{ - Enabled: &enabled, - ValidLifetimeSecs: &validSecs, - }, - }, - } - err := mock.AddTestCase(http.MethodPost, "/dhcp/scopegroup/1", http.StatusOK, nil, nil, sg, sg) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - - respSG, _, err := client.ScopeGroup.Edit(sg) - if err != nil { - t.Fatalf("error editing scope group: %v", err) - } - if respSG.Name != sg.Name || *respSG.DHCP4.ValidLifetimeSecs != *sg.DHCP4.ValidLifetimeSecs { - t.Errorf("wrong address returned: want=%+v, got=%+v", sg, respSG) - } - }) - - t.Run("Delete", func(t *testing.T) { - defer mock.ClearTestCases() - - err := mock.AddTestCase(http.MethodDelete, "/dhcp/scopegroup/1", http.StatusNoContent, nil, nil, "", nil) - if err != nil { - t.Fatalf("error adding test case: %v", err) - } - _, err = client.ScopeGroup.Delete(1) - if err != nil { - t.Fatalf("error deleting scope group: %v", err) - } - }) -}