From 72a9b2e1163d0247b2693c7c92e79886f0cc06cb Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 00:49:06 +0000 Subject: [PATCH 01/16] Refactor connector api --- client/connector.go | 118 ++++++++++++++++++++++++++++---------------- 1 file changed, 75 insertions(+), 43 deletions(-) diff --git a/client/connector.go b/client/connector.go index e6ea230..dd5c333 100644 --- a/client/connector.go +++ b/client/connector.go @@ -21,6 +21,16 @@ type Connector struct { ConnectionStatus ConnectionStatus `json:"connectionStatus"` } +type ConnectorPageResponse struct { + Content []Connector `json:"content"` + NumberOfElements int `json:"numberOfElements"` + Page int `json:"page"` + Size int `json:"size"` + Success bool `json:"success"` + TotalElements int `json:"totalElements"` + TotalPages int `json:"totalPages"` +} + const ( NetworkItemTypeHost = "HOST" NetworkItemTypeNetwork = "NETWORK" @@ -31,85 +41,119 @@ const ( ConnectionStatusOnline ConnectionStatus = "ONLINE" ) -func (c *Client) GetConnectors() ([]Connector, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/connectors", c.BaseURL), nil) +func (c *Client) GetConnectorsByPage(page int, pageSize int) (ConnectorPageResponse, error) { + endpoint := fmt.Sprintf("%s/api/beta/connectors/page?page=%d&size=%d", c.BaseURL, page, pageSize) + req, err := http.NewRequest(http.MethodGet, endpoint, nil) if err != nil { - return nil, err + return ConnectorPageResponse{}, err } + body, err := c.DoRequest(req) if err != nil { - return nil, err + return ConnectorPageResponse{}, err } - var connectors []Connector - err = json.Unmarshal(body, &connectors) + + var response ConnectorPageResponse + err = json.Unmarshal(body, &response) if err != nil { - return nil, err + return ConnectorPageResponse{}, err + } + return response, nil +} + +func (c *Client) GetAllConnectors() ([]Connector, error) { + var allConnectors []Connector + page := 1 + pageSize := 10 + + for { + response, err := c.GetConnectorsByPage(page, pageSize) + if err != nil { + return nil, err + } + + allConnectors = append(allConnectors, response.Content...) + + if page >= response.TotalPages { + break + } + page++ } - return connectors, nil + return allConnectors, nil } func (c *Client) GetConnectorByName(name string) (*Connector, error) { - connectors, err := c.GetConnectors() + connectors, err := c.GetAllConnectors() if err != nil { return nil, err } - for _, c := range connectors { - if c.Name == name { - return &c, nil + + for _, connector := range connectors { + if connector.Name == name { + return &connector, nil } } return nil, nil } func (c *Client) GetConnectorById(connectorId string) (*Connector, error) { - connectors, err := c.GetConnectors() + connectors, err := c.GetAllConnectors() if err != nil { return nil, err } - for _, c := range connectors { - if c.Id == connectorId { - return &c, nil + + for _, connector := range connectors { + if connector.Id == connectorId { + return &connector, nil } } return nil, nil } func (c *Client) GetConnectorsForNetwork(networkId string) ([]Connector, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/connectors", c.BaseURL), nil) - if err != nil { - return nil, err - } - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - var connectors []Connector - err = json.Unmarshal(body, &connectors) + connectors, err := c.GetAllConnectors() if err != nil { return nil, err } + var networkConnectors []Connector - for _, v := range connectors { - if v.NetworkItemId == networkId { - networkConnectors = append(networkConnectors, v) + for _, connector := range connectors { + if connector.NetworkItemId == networkId { + networkConnectors = append(networkConnectors, connector) } } return networkConnectors, nil } -func (c *Client) AddConnector(connector Connector, networkItemId string) (*Connector, error) { +func (c *Client) GetConnectorProfile(id string) (string, error) { + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/connectors/%s/profile", c.BaseURL, id), nil) + if err != nil { + return "", err + } + + body, err := c.DoRequest(req) + if err != nil { + return "", err + } + return string(body), nil +} + +func (c *Client) CreateConnector(connector Connector, networkItemId string) (*Connector, error) { connectorJson, err := json.Marshal(connector) if err != nil { return nil, err } + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/connectors?networkItemId=%s&networkItemType=%s", c.BaseURL, networkItemId, connector.NetworkItemType), bytes.NewBuffer(connectorJson)) if err != nil { return nil, err } + body, err := c.DoRequest(req) if err != nil { return nil, err } + var conn Connector err = json.Unmarshal(body, &conn) if err != nil { @@ -123,19 +167,7 @@ func (c *Client) DeleteConnector(connectorId string, networkItemId string, netwo if err != nil { return err } + _, err = c.DoRequest(req) return err } - -func (c *Client) GetConnectorProfile(id string) (string, error) { - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/connectors/%s/profile", c.BaseURL, id), nil) - if err != nil { - return "", err - } - body, err := c.DoRequest(req) - if err != nil { - return "", err - } - - return string(body), nil -} From 72c62b226169db478af1c2934fd2c5e4fbbff687 Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 00:50:35 +0000 Subject: [PATCH 02/16] Refactor dns_record api --- client/dns_record.go | 80 ++++++++++++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/client/dns_record.go b/client/dns_record.go index 614754a..12cc300 100644 --- a/client/dns_record.go +++ b/client/dns_record.go @@ -15,47 +15,82 @@ type DnsRecord struct { IPV6Addresses []string `json:"ipv6Addresses"` } -func (c *Client) CreateDnsRecord(record DnsRecord) (*DnsRecord, error) { - recordJson, err := json.Marshal(record) - if err != nil { - return nil, err - } - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/dns-records", c.BaseURL), bytes.NewBuffer(recordJson)) +type DnsRecordPageResponse struct { + Content []DnsRecord `json:"content"` + NumberOfElements int `json:"numberOfElements"` + Page int `json:"page"` + Size int `json:"size"` + Success bool `json:"success"` + TotalElements int `json:"totalElements"` + TotalPages int `json:"totalPages"` +} + +func (c *Client) GetDnsRecordsByPage(page int, pageSize int) (DnsRecordPageResponse, error) { + endpoint := fmt.Sprintf("%s/api/beta/dns-records/page?page=%d&size=%d", c.BaseURL, page, pageSize) + req, err := http.NewRequest(http.MethodGet, endpoint, nil) if err != nil { - return nil, err + return DnsRecordPageResponse{}, err } + body, err := c.DoRequest(req) if err != nil { - return nil, err + return DnsRecordPageResponse{}, err } - var d DnsRecord - err = json.Unmarshal(body, &d) + + var response DnsRecordPageResponse + err = json.Unmarshal(body, &response) if err != nil { - return nil, err + return DnsRecordPageResponse{}, err } - return &d, nil + return response, nil } func (c *Client) GetDnsRecord(recordId string) (*DnsRecord, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/dns-records", c.BaseURL), nil) + pageSize := 10 + page := 1 + + for { + response, err := c.GetDnsRecordsByPage(page, pageSize) + if err != nil { + return nil, err + } + + for _, record := range response.Content { + if record.Id == recordId { + return &record, nil + } + } + + if page >= response.TotalPages { + break + } + page++ + } + return nil, fmt.Errorf("DNS record with ID %s not found", recordId) +} + +func (c *Client) CreateDnsRecord(record DnsRecord) (*DnsRecord, error) { + recordJson, err := json.Marshal(record) if err != nil { return nil, err } - body, err := c.DoRequest(req) + + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/dns-records", c.BaseURL), bytes.NewBuffer(recordJson)) if err != nil { return nil, err } - var records []DnsRecord - err = json.Unmarshal(body, &records) + + body, err := c.DoRequest(req) if err != nil { return nil, err } - for _, r := range records { - if r.Id == recordId { - return &r, nil - } + + var d DnsRecord + err = json.Unmarshal(body, &d) + if err != nil { + return nil, err } - return nil, nil + return &d, nil } func (c *Client) UpdateDnsRecord(record DnsRecord) error { @@ -63,10 +98,12 @@ func (c *Client) UpdateDnsRecord(record DnsRecord) error { if err != nil { return err } + req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/beta/dns-records/%s", c.BaseURL, record.Id), bytes.NewBuffer(recordJson)) if err != nil { return err } + _, err = c.DoRequest(req) return err } @@ -76,6 +113,7 @@ func (c *Client) DeleteDnsRecord(recordId string) error { if err != nil { return err } + _, err = c.DoRequest(req) return err } From cda570f791fbea0fb19897a9eb03724bb05b5afc Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 00:50:45 +0000 Subject: [PATCH 03/16] Refactor host api --- client/host.go | 61 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/client/host.go b/client/host.go index 0ad015b..b2c0ab7 100644 --- a/client/host.go +++ b/client/host.go @@ -16,28 +16,62 @@ type Host struct { Connectors []Connector `json:"connectors"` } -func (c *Client) GetHosts() ([]Host, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/hosts", c.BaseURL), nil) +type HostPageResponse struct { + Content []Host `json:"content"` + NumberOfElements int `json:"numberOfElements"` + Page int `json:"page"` + Size int `json:"size"` + Success bool `json:"success"` + TotalElements int `json:"totalElements"` + TotalPages int `json:"totalPages"` +} + +func (c *Client) GetHostsByPage(page int, size int) (HostPageResponse, error) { + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/hosts/page?page=%d&size=%d", c.BaseURL, page, size), nil) if err != nil { - return nil, err + return HostPageResponse{}, err } + body, err := c.DoRequest(req) if err != nil { - return nil, err + return HostPageResponse{}, err } - var hosts []Host - err = json.Unmarshal(body, &hosts) + + var response HostPageResponse + err = json.Unmarshal(body, &response) if err != nil { - return nil, err + return HostPageResponse{}, err } - return hosts, nil + return response, nil +} + +func (c *Client) GetAllHosts() ([]Host, error) { + var allHosts []Host + pageSize := 10 + page := 1 + + for { + response, err := c.GetHostsByPage(page, pageSize) + if err != nil { + return nil, err + } + + allHosts = append(allHosts, response.Content...) + + if page >= response.TotalPages { + break + } + page++ + } + return allHosts, nil } func (c *Client) GetHostByName(name string) (*Host, error) { - hosts, err := c.GetHosts() + hosts, err := c.GetAllHosts() if err != nil { return nil, err } + for _, h := range hosts { if h.Name == name { return &h, nil @@ -47,10 +81,11 @@ func (c *Client) GetHostByName(name string) (*Host, error) { } func (c *Client) GetHostById(hostId string) (*Host, error) { - hosts, err := c.GetHosts() + hosts, err := c.GetAllHosts() if err != nil { return nil, err } + for _, h := range hosts { if h.Id == hostId { return &h, nil @@ -64,14 +99,17 @@ func (c *Client) CreateHost(host Host) (*Host, error) { if err != nil { return nil, err } + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/hosts", c.BaseURL), bytes.NewBuffer(hostJson)) if err != nil { return nil, err } + body, err := c.DoRequest(req) if err != nil { return nil, err } + var h Host err = json.Unmarshal(body, &h) if err != nil { @@ -85,10 +123,12 @@ func (c *Client) UpdateHost(host Host) error { if err != nil { return err } + req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/beta/hosts/%s", c.BaseURL, host.Id), bytes.NewBuffer(hostJson)) if err != nil { return err } + _, err = c.DoRequest(req) return err } @@ -98,6 +138,7 @@ func (c *Client) DeleteHost(hostId string) error { if err != nil { return err } + _, err = c.DoRequest(req) return err } From 6b48ffb61af49782458a118e4ba715f5d607bd91 Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 00:50:53 +0000 Subject: [PATCH 04/16] Refactor network api --- client/network.go | 89 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 18 deletions(-) diff --git a/client/network.go b/client/network.go index 8069ed0..ff12653 100644 --- a/client/network.go +++ b/client/network.go @@ -7,15 +7,36 @@ import ( "net/http" ) +type NetworkConnector struct { + Description string `json:"description"` + Id string `json:"id"` + IPv4Address string `json:"ipV4Address"` + IPv6Address string `json:"ipV6Address"` + Name string `json:"name"` + NetworkItemId string `json:"networkItemId"` + NetworkItemType string `json:"networkItemType"` + VpnRegionId string `json:"vpnRegionId"` +} + type Network struct { - Id string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Egress bool `json:"egress"` - InternetAccess string `json:"internetAccess"` - SystemSubnets []string `json:"systemSubnets"` - Routes []Route `json:"routes"` - Connectors []Connector `json:"connectors"` + Connectors []NetworkConnector `json:"connectors"` + Description string `json:"description"` + Egress bool `json:"egress"` + Id string `json:"id"` + InternetAccess string `json:"internetAccess"` + Name string `json:"name"` + Routes []Route `json:"routes"` + SystemSubnets []string `json:"systemSubnets"` +} + +type NetworkPageResponse struct { + Content []Network `json:"content"` + NumberOfElements int `json:"numberOfElements"` + Page int `json:"page"` + Size int `json:"size"` + Success bool `json:"success"` + TotalElements int `json:"totalElements"` + TotalPages int `json:"totalPages"` } const ( @@ -24,28 +45,53 @@ const ( InternetAccessLocal = "LOCAL" ) -func (c *Client) GetNetworks() ([]Network, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/networks", c.BaseURL), nil) +func (c *Client) GetNetworksByPage(page int, size int) (NetworkPageResponse, error) { + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/networks/page?page=%d&size=%d", c.BaseURL, page, size), nil) if err != nil { - return nil, err + return NetworkPageResponse{}, err } + body, err := c.DoRequest(req) if err != nil { - return nil, err + return NetworkPageResponse{}, err } - var networks []Network - err = json.Unmarshal(body, &networks) + + var response NetworkPageResponse + err = json.Unmarshal(body, &response) if err != nil { - return nil, err + return NetworkPageResponse{}, err + } + + return response, nil +} + +func (c *Client) GetAllNetworks() ([]Network, error) { + var allNetworks []Network + pageSize := 10 + page := 1 + + for { + response, err := c.GetNetworksByPage(page, pageSize) + if err != nil { + return nil, err + } + + allNetworks = append(allNetworks, response.Content...) + + if page >= response.TotalPages { + break + } + page++ } - return networks, nil + return allNetworks, nil } func (c *Client) GetNetworkByName(name string) (*Network, error) { - networks, err := c.GetNetworks() + networks, err := c.GetAllNetworks() if err != nil { return nil, err } + for _, n := range networks { if n.Name == name { return &n, nil @@ -55,10 +101,11 @@ func (c *Client) GetNetworkByName(name string) (*Network, error) { } func (c *Client) GetNetworkById(networkId string) (*Network, error) { - networks, err := c.GetNetworks() + networks, err := c.GetAllNetworks() if err != nil { return nil, err } + for _, n := range networks { if n.Id == networkId { return &n, nil @@ -72,14 +119,17 @@ func (c *Client) CreateNetwork(network Network) (*Network, error) { if err != nil { return nil, err } + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/networks", c.BaseURL), bytes.NewBuffer(networkJson)) if err != nil { return nil, err } + body, err := c.DoRequest(req) if err != nil { return nil, err } + var n Network err = json.Unmarshal(body, &n) if err != nil { @@ -93,10 +143,12 @@ func (c *Client) UpdateNetwork(network Network) error { if err != nil { return err } + req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/beta/networks/%s", c.BaseURL, network.Id), bytes.NewBuffer(networkJson)) if err != nil { return err } + _, err = c.DoRequest(req) return err } @@ -106,6 +158,7 @@ func (c *Client) DeleteNetwork(networkId string) error { if err != nil { return err } + _, err = c.DoRequest(req) return err } From 66f41f5408f9a99dc15953123661274094f78949 Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 00:51:01 +0000 Subject: [PATCH 05/16] Refactor route api --- client/route.go | 160 ++++++++++++++++++++++++++++++------------------ 1 file changed, 100 insertions(+), 60 deletions(-) diff --git a/client/route.go b/client/route.go index e749670..e555f73 100644 --- a/client/route.go +++ b/client/route.go @@ -11,10 +11,18 @@ type Route struct { Id string `json:"id,omitempty"` Type string `json:"type,omitempty"` Subnet string `json:"subnet,omitempty"` - Domain string `json:"domain,omitempty"` - Value string `json:"value,omitempty"` - NetworkItemId string `json:"networkItemId,omitempty"` Description string `json:"description,omitempty"` + NetworkItemId string `json:"networkItemId,omitempty"` +} + +type RoutePageResponse struct { + Success bool `json:"success"` + Content []Route `json:"content"` + TotalElements int `json:"totalElements"` + TotalPages int `json:"totalPages"` + NumberOfElements int `json:"numberOfElements"` + Page int `json:"page"` + Size int `json:"size"` } const ( @@ -23,74 +31,52 @@ const ( RouteTypeDomain = "DOMAIN" ) -func (c *Client) CreateRoute(networkId string, route Route) (*Route, error) { - type newRoute struct { - Description string `json:"description"` - Value string `json:"value"` - Type string `json:"type"` - } - routeToCreate := newRoute{ - Description: route.Description, - Value: route.Value, - Type: route.Type, - } - routeJson, err := json.Marshal(routeToCreate) - if err != nil { - return nil, err - } - req, err := http.NewRequest( - http.MethodPost, - fmt.Sprintf("%s/api/beta/networks/%s/routes", c.BaseURL, networkId), - bytes.NewBuffer(routeJson), - ) +func (c *Client) GetRoutesByPage(networkId string, page int, size int) (RoutePageResponse, error) { + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/networks/%s/routes/page?page=%d&size=%d", c.BaseURL, networkId, page, size), nil) if err != nil { - return nil, err + return RoutePageResponse{}, err } + body, err := c.DoRequest(req) if err != nil { - return nil, err - } - var r Route - err = json.Unmarshal(body, &r) - if err != nil { - return nil, err + return RoutePageResponse{}, err } - // The API does not return the route Value, so we set it manually. - r.Value = routeToCreate.Value - return &r, nil -} -func (c *Client) DeleteRoute(networkId string, routeId string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/beta/networks/%s/routes/%s", c.BaseURL, networkId, routeId), nil) + var response RoutePageResponse + err = json.Unmarshal(body, &response) if err != nil { - return err + return RoutePageResponse{}, err } - _, err = c.DoRequest(req) - return err + return response, nil } -func (c *Client) GetRoutes(networkId string) ([]Route, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/networks/%s/routes", c.BaseURL, networkId), nil) - if err != nil { - return nil, err - } - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - var routes []Route - err = json.Unmarshal(body, &routes) - if err != nil { - return nil, err +func (c *Client) GetAllRoutes(networkId string) ([]Route, error) { + var allRoutes []Route + pageSize := 10 + page := 1 + + for { + response, err := c.GetRoutesByPage(networkId, page, pageSize) + if err != nil { + return nil, err + } + + allRoutes = append(allRoutes, response.Content...) + + if page >= response.TotalPages { + break + } + page++ } - return routes, nil + return allRoutes, nil } func (c *Client) GetNetworkRoute(networkId string, routeId string) (*Route, error) { - routes, err := c.GetRoutes(networkId) + routes, err := c.GetAllRoutes(networkId) if err != nil { return nil, err } + for _, r := range routes { if r.Id == routeId { return &r, nil @@ -100,28 +86,71 @@ func (c *Client) GetNetworkRoute(networkId string, routeId string) (*Route, erro } func (c *Client) GetRouteById(routeId string) (*Route, error) { - networks, err := c.GetNetworks() + networks, err := c.GetAllNetworks() if err != nil { return nil, err } + for _, n := range networks { - r, err := c.GetNetworkRoute(n.Id, routeId) + routes, err := c.GetAllRoutes(n.Id) if err != nil { - return nil, err + continue } - if r != nil { - r.NetworkItemId = n.Id - return r, nil + for _, r := range routes { + if r.Id == routeId { + r.NetworkItemId = n.Id + return &r, nil + } } } return nil, nil } +func (c *Client) CreateRoute(networkId string, route Route) (*Route, error) { + type newRoute struct { + Description string `json:"description"` + Subnet string `json:"subnet"` + } + routeToCreate := newRoute{ + Description: route.Description, + Subnet: route.Subnet, + } + routeJson, err := json.Marshal(routeToCreate) + if err != nil { + return nil, err + } + + req, err := http.NewRequest( + http.MethodPost, + fmt.Sprintf("%s/api/beta/networks/%s/routes", c.BaseURL, networkId), + bytes.NewBuffer(routeJson), + ) + if err != nil { + return nil, err + } + + body, err := c.DoRequest(req) + if err != nil { + return nil, err + } + + var r Route + err = json.Unmarshal(body, &r) + if err != nil { + return nil, err + } + + // The API does not return the route Value, so we set it manually. + r.Subnet = routeToCreate.Subnet + return &r, nil +} + func (c *Client) UpdateRoute(networkId string, route Route) error { routeJson, err := json.Marshal(route) if err != nil { return err } + req, err := http.NewRequest( http.MethodPut, fmt.Sprintf("%s/api/beta/networks/%s/routes/%s", c.BaseURL, networkId, route.Id), @@ -130,6 +159,17 @@ func (c *Client) UpdateRoute(networkId string, route Route) error { if err != nil { return err } + + _, err = c.DoRequest(req) + return err +} + +func (c *Client) DeleteRoute(networkId string, routeId string) error { + req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/beta/networks/%s/routes/%s", c.BaseURL, networkId, routeId), nil) + if err != nil { + return err + } + _, err = c.DoRequest(req) return err } From 3381a9590b1b63a8a3b6ca3617ffa16f25786ba9 Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 00:51:05 +0000 Subject: [PATCH 06/16] Refactor user api --- client/user.go | 103 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/client/user.go b/client/user.go index 7c1de71..9c22040 100644 --- a/client/user.go +++ b/client/user.go @@ -20,6 +20,16 @@ type User struct { Devices []Device `json:"devices"` } +type UserPageResponse struct { + Content []User `json:"content"` + NumberOfElements int `json:"numberOfElements"` + Page int `json:"page"` + Size int `json:"size"` + Success bool `json:"success"` + TotalElements int `json:"totalElements"` + TotalPages int `json:"totalPages"` +} + type Device struct { Id string `json:"id"` Name string `json:"name"` @@ -28,58 +38,90 @@ type Device struct { IPv6Address string `json:"ipV6Address"` } -func (c *Client) CreateUser(user User) (*User, error) { - userJson, err := json.Marshal(user) +func (c *Client) GetUsersByPage(page int, pageSize int) (UserPageResponse, error) { + endpoint := fmt.Sprintf("%s/api/beta/users/page?page=%d&size=%d", c.BaseURL, page, pageSize) + req, err := http.NewRequest(http.MethodGet, endpoint, nil) if err != nil { - return nil, err - } - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/users", c.BaseURL), bytes.NewBuffer(userJson)) - if err != nil { - return nil, err + return UserPageResponse{}, err } + body, err := c.DoRequest(req) if err != nil { - return nil, err + return UserPageResponse{}, err } - var u User - err = json.Unmarshal(body, &u) + + var response UserPageResponse + err = json.Unmarshal(body, &response) if err != nil { - return nil, err + return UserPageResponse{}, err } - return &u, nil + return response, nil } func (c *Client) GetUser(username string, role string) (*User, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/users", c.BaseURL), nil) - if err != nil { - return nil, err + pageSize := 10 + page := 1 + + for { + response, err := c.GetUsersByPage(page, pageSize) + if err != nil { + return nil, err + } + + for _, user := range response.Content { + if user.Username == username && user.Role == role { + return &user, nil + } + } + + if page >= response.TotalPages { + break + } + page++ } - body, err := c.DoRequest(req) - if err != nil { - return nil, err + return nil, fmt.Errorf("user with username %s and role %s not found", username, role) +} + +func (c *Client) GetUserById(userId string) (*User, error) { + pageSize := 10 + page := 1 + + for { + response, err := c.GetUsersByPage(page, pageSize) + if err != nil { + return nil, err + } + + for _, user := range response.Content { + if user.Id == userId { + return &user, nil + } + } + + if page >= response.TotalPages { + break + } + page++ } - var users []User - err = json.Unmarshal(body, &users) + return nil, fmt.Errorf("user with ID %s not found", userId) +} + +func (c *Client) CreateUser(user User) (*User, error) { + userJson, err := json.Marshal(user) if err != nil { return nil, err } - for _, u := range users { - if u.Username == username && u.Role == role { - return &u, nil - } - } - return nil, nil -} -func (c *Client) GetUserById(userId string) (*User, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/users/%s", c.BaseURL, userId), nil) + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/users", c.BaseURL), bytes.NewBuffer(userJson)) if err != nil { return nil, err } + body, err := c.DoRequest(req) if err != nil { return nil, err } + var u User err = json.Unmarshal(body, &u) if err != nil { @@ -93,10 +135,12 @@ func (c *Client) UpdateUser(user User) error { if err != nil { return err } + req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/beta/users/%s", c.BaseURL, user.Id), bytes.NewBuffer(userJson)) if err != nil { return err } + _, err = c.DoRequest(req) if err != nil { return err @@ -109,6 +153,7 @@ func (c *Client) DeleteUser(userId string) error { if err != nil { return err } + _, err = c.DoRequest(req) return err } From 96fae73c75209745d9265464e0de862ff14661a4 Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 00:51:13 +0000 Subject: [PATCH 07/16] Refactor user_group api --- client/user_group.go | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/client/user_group.go b/client/user_group.go index 4f6eb4d..35e0b2a 100644 --- a/client/user_group.go +++ b/client/user_group.go @@ -22,10 +22,12 @@ func (c *Client) GetUserGroups() ([]UserGroup, error) { if err != nil { return nil, err } + body, err := c.DoRequest(req) if err != nil { return nil, err } + var userGroups []UserGroup err = json.Unmarshal(body, &userGroups) if err != nil { @@ -38,6 +40,7 @@ func (c *Client) GetUserGroupByName(name string) (*UserGroup, error) { if err != nil { return nil, err } + for _, ug := range userGroups { if ug.Name == name { return &ug, nil @@ -51,6 +54,7 @@ func (c *Client) GetUserGroupById(id string) (*UserGroup, error) { if err != nil { return nil, err } + for _, ug := range userGroups { if ug.Id == id { return &ug, nil @@ -64,14 +68,17 @@ func (c *Client) CreateUserGroup(userGroup *UserGroup) (*UserGroup, error) { if err != nil { return nil, err } + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/user-groups", c.BaseURL), bytes.NewBuffer(userGroupJson)) if err != nil { return nil, err } + body, err := c.DoRequest(req) if err != nil { return nil, err } + var ug UserGroup err = json.Unmarshal(body, &ug) if err != nil { @@ -80,27 +87,17 @@ func (c *Client) CreateUserGroup(userGroup *UserGroup) (*UserGroup, error) { return &ug, nil } -func (c *Client) DeleteUserGroup(id string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/beta/user-groups/%s", c.BaseURL, id), nil) - if err != nil { - return err - } - _, err = c.DoRequest(req) - if err != nil { - return err - } - return nil -} - func (c *Client) UpdateUserGroup(id string, userGroup *UserGroup) (*UserGroup, error) { userGroupJson, err := json.Marshal(userGroup) if err != nil { return nil, err } + req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/beta/user-groups/%s", c.BaseURL, id), bytes.NewBuffer(userGroupJson)) if err != nil { return nil, err } + body, err := c.DoRequest(req) if err != nil { return nil, err @@ -112,3 +109,16 @@ func (c *Client) UpdateUserGroup(id string, userGroup *UserGroup) (*UserGroup, e } return &ug, nil } + +func (c *Client) DeleteUserGroup(id string) error { + req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/beta/user-groups/%s", c.BaseURL, id), nil) + if err != nil { + return err + } + + _, err = c.DoRequest(req) + if err != nil { + return err + } + return nil +} From 675db23e17b7520b33656cccaa270e9175f69e65 Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 00:51:19 +0000 Subject: [PATCH 08/16] Refactor vpn_region api --- client/vpn_region.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/vpn_region.go b/client/vpn_region.go index 01aa014..d21522d 100644 --- a/client/vpn_region.go +++ b/client/vpn_region.go @@ -19,15 +19,18 @@ func (c *Client) GetVpnRegion(regionId string) (*VpnRegion, error) { if err != nil { return nil, err } + body, err := c.DoRequest(req) if err != nil { return nil, err } + var vpnRegions []VpnRegion err = json.Unmarshal(body, &vpnRegions) if err != nil { return nil, err } + for _, r := range vpnRegions { if r.Id == regionId { return &r, nil From c2d6aff0bbea47637f1250492bc1259e7eaac48b Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 00:51:37 +0000 Subject: [PATCH 09/16] =?UTF-8?q?Refactor=20service=20=E2=80=94>=20ip=5Fse?= =?UTF-8?q?rvice=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/service.go | 128 ---------------------------------------------- 1 file changed, 128 deletions(-) delete mode 100644 client/service.go diff --git a/client/service.go b/client/service.go deleted file mode 100644 index 3a67580..0000000 --- a/client/service.go +++ /dev/null @@ -1,128 +0,0 @@ -package client - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "net/url" -) - -type Range struct { - LowerValue int `json:"lowerValue"` - UpperValue int `json:"upperValue"` -} - -type CustomServiceType struct { - IcmpType []Range `json:"icmpType"` - Port []Range `json:"port"` - Protocol string `json:"protocol"` -} - -type ServiceConfig struct { - CustomServiceTypes []*CustomServiceType `json:"customServiceTypes"` - ServiceTypes []string `json:"serviceTypes"` -} - -type Service struct { - Id string `json:"id,omitempty"` - Name string `json:"name"` - Description string `json:"description"` - NetworkItemType string `json:"networkItemType"` - NetworkItemId string `json:"networkItemId"` - Type string `json:"type"` - Routes []*Route `json:"routes"` - Config *ServiceConfig `json:"config"` -} - -func (c *Client) CreateService(service *Service) (*Service, error) { - serviceJson, err := json.Marshal(service) - if err != nil { - return nil, err - } - - params := networkUrlParams(service.NetworkItemType, service.NetworkItemId) - endpoint := fmt.Sprintf("%s/api/beta/services?%s", c.BaseURL, params.Encode()) - - req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBuffer(serviceJson)) - if err != nil { - return nil, err - } - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - var s Service - err = json.Unmarshal(body, &s) - if err != nil { - return nil, err - } - return &s, nil -} - -func (c *Client) GetService(serviceId, networkItemType, networkItemId string) (*Service, error) { - params := networkUrlParams(networkItemType, networkItemId) - params.Add("serviceId", serviceId) - - endpoint := fmt.Sprintf("%s/api/beta/services/single?%s", c.BaseURL, params.Encode()) - req, err := http.NewRequest(http.MethodGet, endpoint, nil) - if err != nil { - return nil, err - } - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - var s Service - err = json.Unmarshal(body, &s) - if err != nil { - return nil, err - } - return &s, nil -} - -func (c *Client) DeleteService(serviceId, networkItemType, networkItemId string) error { - params := networkUrlParams(networkItemType, networkItemId).Encode() - endpoint := fmt.Sprintf("%s/api/beta/services/%s?%s", c.BaseURL, serviceId, params) - req, err := http.NewRequest(http.MethodDelete, endpoint, nil) - if err != nil { - return err - } - _, err = c.DoRequest(req) - if err != nil { - return err - } - return nil -} - -func (c *Client) UpdateService(id, networkItemType, networkItemId string, service *Service) (*Service, error) { - serviceJson, err := json.Marshal(service) - if err != nil { - return nil, err - } - - params := networkUrlParams(networkItemType, networkItemId).Encode() - endpoint := fmt.Sprintf("%s/api/beta/services/%s?%s", c.BaseURL, id, params) - - req, err := http.NewRequest(http.MethodPut, endpoint, bytes.NewBuffer(serviceJson)) - if err != nil { - return nil, err - } - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - var s Service - err = json.Unmarshal(body, &s) - if err != nil { - return nil, err - } - return &s, nil -} - -func networkUrlParams(networkItemType string, networkItemId string) url.Values { - params := url.Values{} - params.Add("networkItemId", networkItemId) - params.Add("networkItemType", networkItemType) - return params -} From 672a9ce1c774f69e8400ab0c044eb4d46f458d82 Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 00:55:52 +0000 Subject: [PATCH 10/16] Added new ip_service client api --- client/ip_service.go | 160 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 client/ip_service.go diff --git a/client/ip_service.go b/client/ip_service.go new file mode 100644 index 0000000..19c04be --- /dev/null +++ b/client/ip_service.go @@ -0,0 +1,160 @@ +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/url" +) + +type Range struct { + LowerValue int `json:"lowerValue"` + UpperValue int `json:"upperValue"` +} + +type CustomIpServiceType struct { + IcmpType []Range `json:"icmpType"` + Port []Range `json:"port"` + Protocol string `json:"protocol"` +} + +type IpServiceConfig struct { + CustomServiceTypes []*CustomIpServiceType `json:"customServiceTypes"` + ServiceTypes []string `json:"serviceTypes"` +} + +type IpService struct { + Name string `json:"name"` + Description string `json:"description"` + NetworkItemType string `json:"networkItemType"` + NetworkItemId string `json:"networkItemId"` + Id string `json:"id"` + Type string `json:"type"` + Routes []*Route `json:"routes"` + Config *IpServiceConfig `json:"config"` +} + +type IpServicePageResponse struct { + Content []IpService `json:"content"` + NumberOfElements int `json:"numberOfElements"` + Page int `json:"page"` + Size int `json:"size"` + Success bool `json:"success"` + TotalElements int `json:"totalElements"` + TotalPages int `json:"totalPages"` +} + +func (c *Client) GetIpServicesByPage(page int, pageSize int) (IpServicePageResponse, error) { + endpoint := fmt.Sprintf("%s/api/beta/ip-services/page?page=%d&size=%d", c.BaseURL, page, pageSize) + req, err := http.NewRequest(http.MethodGet, endpoint, nil) + if err != nil { + return IpServicePageResponse{}, err + } + + body, err := c.DoRequest(req) + if err != nil { + return IpServicePageResponse{}, err + } + + var response IpServicePageResponse + err = json.Unmarshal(body, &response) + if err != nil { + return IpServicePageResponse{}, err + } + return response, nil +} + +func (c *Client) GetAllIpServices() ([]IpService, error) { + var allIpServices []IpService + page := 1 + pageSize := 10 + + for { + response, err := c.GetIpServicesByPage(page, pageSize) + if err != nil { + return nil, err + } + + allIpServices = append(allIpServices, response.Content...) + if page >= response.TotalPages { + break + } + page++ + } + return allIpServices, nil +} + +func (c *Client) CreateIpService(ipService *IpService) (*IpService, error) { + ipServiceJson, err := json.Marshal(ipService) + if err != nil { + return nil, err + } + + params := networkUrlParams(ipService.NetworkItemType, ipService.NetworkItemId) + endpoint := fmt.Sprintf("%s/api/beta/ip-services?%s", c.BaseURL, params.Encode()) + + req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBuffer(ipServiceJson)) + if err != nil { + return nil, err + } + + body, err := c.DoRequest(req) + if err != nil { + return nil, err + } + + var s IpService + err = json.Unmarshal(body, &s) + if err != nil { + return nil, err + } + return &s, nil +} + +func (c *Client) UpdateIpService(id string, service *IpService) (*IpService, error) { + serviceJson, err := json.Marshal(service) + if err != nil { + return nil, err + } + + endpoint := fmt.Sprintf("%s/api/beta/ip-services/%s?%s", c.BaseURL, id) + + req, err := http.NewRequest(http.MethodPut, endpoint, bytes.NewBuffer(serviceJson)) + if err != nil { + return nil, err + } + + body, err := c.DoRequest(req) + if err != nil { + return nil, err + } + + var s IpService + err = json.Unmarshal(body, &s) + if err != nil { + return nil, err + } + return &s, nil +} + +func (c *Client) DeleteIpService(ipServiceId string) error { + endpoint := fmt.Sprintf("%s/api/beta/ip-services/%s?%s", c.BaseURL, ipServiceId) + req, err := http.NewRequest(http.MethodDelete, endpoint, nil) + if err != nil { + return err + } + + _, err = c.DoRequest(req) + if err != nil { + return err + } + return nil +} + +func networkUrlParams(networkItemType string, networkItemId string) url.Values { + params := url.Values{} + params.Add("networkItemId", networkItemId) + params.Add("networkItemType", networkItemType) + return params +} From 5b7d40919cbf1e83c98bec31381550dc37cffbfc Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 01:07:05 +0000 Subject: [PATCH 11/16] Update examples --- example/backend.tf | 2 +- example/connectors.tf | 6 +++--- example/hosts.tf | 2 +- example/networks.tf | 2 +- example/routes.tf | 4 ++-- example/services.tf | 6 +++--- example/variables.tf | 40 ++++++++++++++++++++-------------------- 7 files changed, 31 insertions(+), 31 deletions(-) diff --git a/example/backend.tf b/example/backend.tf index ad895c3..890b596 100644 --- a/example/backend.tf +++ b/example/backend.tf @@ -3,7 +3,7 @@ terraform { required_providers { openvpncloud = { source = "OpenVPN/openvpn-cloud" - version = "0.0.7" + version = "0.0.11" } } } diff --git a/example/connectors.tf b/example/connectors.tf index 98251f1..f842d68 100644 --- a/example/connectors.tf +++ b/example/connectors.tf @@ -1,10 +1,10 @@ -data "openvpncloud_network" "this" { +data "openvpncloud_network" "test-net" { name = "test-net" } -resource "openvpncloud_connector" "this" { +resource "openvpncloud_connector" "test-connector" { name = "test-connector" vpn_region_id = "eu-central-1" network_item_type = "NETWORK" - network_item_id = data.openvpncloud_network.this.network_id + network_item_id = data.openvpncloud_network.test-net.network_id } diff --git a/example/hosts.tf b/example/hosts.tf index 8dcb6f1..85cedce 100644 --- a/example/hosts.tf +++ b/example/hosts.tf @@ -1,4 +1,4 @@ -resource "openvpncloud_host" "this" { +resource "openvpncloud_host" "test-host" { name = "test-host" connector { name = "test-connector" diff --git a/example/networks.tf b/example/networks.tf index bb5a747..8c1bd73 100644 --- a/example/networks.tf +++ b/example/networks.tf @@ -1,4 +1,4 @@ -resource "openvpncloud_network" "this" { +resource "openvpncloud_network" "test-network" { name = "test-network" egress = false default_route { diff --git a/example/routes.tf b/example/routes.tf index 4f38152..d36c66b 100644 --- a/example/routes.tf +++ b/example/routes.tf @@ -1,6 +1,6 @@ -resource "openvpncloud_route" "example-routes" { +resource "openvpncloud_route" "this" { for_each = { - for key, route in var.example-terraform_ipv4_routes : route.value => route + for key, route in var.routes : route.value => route } network_item_id = var.networks["example-network"] type = "IP_V4" diff --git a/example/services.tf b/example/services.tf index e30b759..2b39249 100644 --- a/example/services.tf +++ b/example/services.tf @@ -1,14 +1,14 @@ -data "openvpncloud_network" "this" { +data "openvpncloud_network" "test-net" { name = "test-net" } -resource "openvpncloud_service" "this" { +resource "openvpncloud_service" "test-service" { name = "test-service" type = "IP_SOURCE" description = "test-description" routes = ["10.0.0.2/32"] network_item_type = "NETWORK" - network_item_id = data.openvpncloud_network.this.network_id + network_item_id = data.openvpncloud_network.test-net.network_id config { service_types = ["ANY"] diff --git a/example/variables.tf b/example/variables.tf index 6669f52..918cfbe 100644 --- a/example/variables.tf +++ b/example/variables.tf @@ -1,6 +1,6 @@ variable "company_name" { type = string - description = "Company name in OpenVPN Cloud" + description = "Company name in CloudConnexa" # default = "" } @@ -14,21 +14,21 @@ variable "users" { }) ) default = { - "Denis_Arslanbekov" = { - username = "Arslanbekov_admin" - email = "admin@arslanbekov.com" + "Username1" = { + username = "Username1" + email = "username1@company.com" group = "Default" role = "ADMIN" } - "Vladimir_Kozyrev" = { - username = "Arslanbekov_developer" - email = "developer@arslanbekov.com" + "Username2" = { + username = "Username2" + email = "username2@company.com" group = "Developer" role = "MEMBER" } - "Antonio_Graziano" = { - username = "Arslanbekov_support" - email = "support@arslanbekov.com" + "Username3" = { + username = "Username3" + email = "username3@company.com" group = "Support" role = "MEMBER" } @@ -38,33 +38,33 @@ variable "users" { variable "groups" { type = map(string) default = { - "Default" = "12312312-1234-1234-1234-123123123123" - "Developer" = "12312312-1234-1234-1234-123123123123" - "Support" = "12312312-1234-1234-1234-123123123123" + "Default" = "11111111-1111-1111-1111-111111111111" + "Developer" = "22222222-1111-1111-1111-111111111111" + "Support" = "33333333-1111-1111-1111-111111111111" } } variable "networks" { type = map(string) default = { - "example-network" = "12312312-1234-1234-1234-123123123123" + "example-network" = "11111111-2222-3333-4444-555555555555" } } -variable "example-terraform_ipv4_routes" { +variable "routes" { type = list(map(string)) default = [ { - value = "10.0.0.0/24" - description = "Example route 1" + value = "10.0.0.0/18" + description = "Example Route with subnet /18" }, { - value = "10.10.0.0/24" - description = "Example route 2" + value = "10.10.0.0/20" + description = "Example Route with subnet /20" }, { value = "10.20.0.0/24" - description = "Example route 3" + description = "Example Route with subnet /24" }, ] } From 3caf17cc677ce3a7385ef6ab364b16d49fd4d6df Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 01:08:55 +0000 Subject: [PATCH 12/16] Update variables.tf example --- example/variables.tf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/example/variables.tf b/example/variables.tf index 918cfbe..2f0f1be 100644 --- a/example/variables.tf +++ b/example/variables.tf @@ -38,16 +38,16 @@ variable "users" { variable "groups" { type = map(string) default = { - "Default" = "11111111-1111-1111-1111-111111111111" - "Developer" = "22222222-1111-1111-1111-111111111111" - "Support" = "33333333-1111-1111-1111-111111111111" + "Default" = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + "Developer" = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + "Support" = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } } variable "networks" { type = map(string) default = { - "example-network" = "11111111-2222-3333-4444-555555555555" + "example-network" = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } } From 7811891fabf60e5c5536631ec30d5a74b6859104 Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Thu, 11 Jan 2024 21:44:45 +0000 Subject: [PATCH 13/16] Refactor --- .env.example | 4 +- .github/workflows/test.yml | 8 +- .gitignore | 2 +- Makefile | 6 +- client/connector.go | 173 ---------- client/dns_record.go | 119 ------- client/errors.go | 5 - client/host.go | 144 --------- client/ip_service.go | 160 ---------- client/network.go | 164 ---------- client/route.go | 175 ---------- client/user.go | 159 --------- client/user_group.go | 124 -------- client/vpn_region.go | 40 --- docs/data-sources/connector.md | 8 +- docs/data-sources/host.md | 8 +- docs/data-sources/network.md | 8 +- docs/data-sources/network_routes.md | 8 +- docs/data-sources/user.md | 8 +- docs/data-sources/user_group.md | 8 +- docs/data-sources/vpn_region.md | 8 +- docs/index.md | 18 +- docs/resources/connector.md | 10 +- docs/resources/dns_record.md | 10 +- docs/resources/host.md | 10 +- docs/resources/network.md | 10 +- docs/resources/route.md | 10 +- docs/resources/user.md | 10 +- e2e/integration_test.go | 18 +- e2e/setup/main.tf | 12 +- example/connectors.tf | 6 +- example/hosts.tf | 2 +- example/networks.tf | 2 +- example/provider.tf | 4 +- example/routes.tf | 2 +- example/services.tf | 6 +- example/user_groups.tf | 2 +- example/users.tf | 2 +- go.mod | 9 +- go.sum | 26 +- main.go | 4 +- openvpncloud/data_source_connector.go | 67 ---- openvpncloud/data_source_host.go | 97 ------ openvpncloud/data_source_network.go | 167 ---------- openvpncloud/data_source_network_routes.go | 68 ---- openvpncloud/data_source_service.go | 67 ---- openvpncloud/data_source_user.go | 138 -------- openvpncloud/data_source_user_group.go | 77 ----- openvpncloud/data_source_vpn_region.go | 65 ---- openvpncloud/provider.go | 80 ----- openvpncloud/provider_test.go | 59 ---- openvpncloud/resource_connector.go | 137 -------- openvpncloud/resource_connector_test.go | 87 ----- openvpncloud/resource_dns_record.go | 146 --------- openvpncloud/resource_dns_record_test.go | 71 ----- openvpncloud/resource_host.go | 268 ---------------- openvpncloud/resource_network.go | 354 --------------------- openvpncloud/resource_route.go | 132 -------- openvpncloud/resource_route_test.go | 132 -------- openvpncloud/resource_service.go | 286 ----------------- openvpncloud/resource_service_test.go | 113 ------- openvpncloud/resource_user.go | 226 ------------- openvpncloud/resource_user_group.go | 168 ---------- openvpncloud/resource_user_group_test.go | 107 ------- openvpncloud/resource_user_test.go | 118 ------- templates/data-sources/host.md | 8 +- templates/data-sources/network.md | 8 +- templates/data-sources/network_routes.md | 8 +- templates/data-sources/user.md | 8 +- templates/index.md.tmpl | 4 +- templates/resources/connector.md | 10 +- templates/resources/dns_record.md | 10 +- templates/resources/host.md | 10 +- templates/resources/network.md | 10 +- templates/resources/route.md | 10 +- templates/resources/user.md | 10 +- 76 files changed, 185 insertions(+), 4653 deletions(-) delete mode 100644 client/connector.go delete mode 100644 client/dns_record.go delete mode 100644 client/errors.go delete mode 100644 client/host.go delete mode 100644 client/ip_service.go delete mode 100644 client/network.go delete mode 100644 client/route.go delete mode 100644 client/user.go delete mode 100644 client/user_group.go delete mode 100644 client/vpn_region.go delete mode 100644 openvpncloud/data_source_connector.go delete mode 100644 openvpncloud/data_source_host.go delete mode 100644 openvpncloud/data_source_network.go delete mode 100644 openvpncloud/data_source_network_routes.go delete mode 100644 openvpncloud/data_source_service.go delete mode 100644 openvpncloud/data_source_user.go delete mode 100644 openvpncloud/data_source_user_group.go delete mode 100644 openvpncloud/data_source_vpn_region.go delete mode 100644 openvpncloud/provider.go delete mode 100644 openvpncloud/provider_test.go delete mode 100644 openvpncloud/resource_connector.go delete mode 100644 openvpncloud/resource_connector_test.go delete mode 100644 openvpncloud/resource_dns_record.go delete mode 100644 openvpncloud/resource_dns_record_test.go delete mode 100644 openvpncloud/resource_host.go delete mode 100644 openvpncloud/resource_network.go delete mode 100644 openvpncloud/resource_route.go delete mode 100644 openvpncloud/resource_route_test.go delete mode 100644 openvpncloud/resource_service.go delete mode 100644 openvpncloud/resource_service_test.go delete mode 100644 openvpncloud/resource_user.go delete mode 100644 openvpncloud/resource_user_group.go delete mode 100644 openvpncloud/resource_user_group_test.go delete mode 100644 openvpncloud/resource_user_test.go diff --git a/.env.example b/.env.example index 3aa0e82..a690ed5 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ OPENVPN_HOST= -OPENVPN_CLOUD_CLIENT_ID= -OPENVPN_CLOUD_CLIENT_SECRET= +CLOUDCONNEXA_CLIENT_ID= +CLOUDCONNEXA_CLIENT_SECRET= TF_ACC=0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2e64f52..b778154 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -66,8 +66,8 @@ jobs: timeout-minutes: 10 env: TF_ACC: "1" - OPENVPNCLOUD_TEST_ORGANIZATION: "terraform-community" - OPENVPN_CLOUD_CLIENT_ID: ${{ secrets.CVPN_CLIENT_ID }} - OPENVPN_CLOUD_CLIENT_SECRET: ${{ secrets.CVPN_CLIENT_SECRET }} + CLOUDCONNEXA_TEST_ORGANIZATION: "terraform-community" + CLOUDCONNEXA_CLIENT_ID: ${{ secrets.CVPN_CLIENT_ID }} + CLOUDCONNEXA_CLIENT_SECRET: ${{ secrets.CVPN_CLIENT_SECRET }} run: | - go test -v -cover ./openvpncloud + go test -v -cover ./cloudconnexa diff --git a/.gitignore b/.gitignore index 25ac2e4..b32167f 100644 --- a/.gitignore +++ b/.gitignore @@ -23,7 +23,7 @@ terraform.tfstate *.iml # Binaries -terraform-provider-sentry +terraform-provider-cloudconnexa main # Keep windows files with windows line endings diff --git a/Makefile b/Makefile index 5c47c4e..022d7f0 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ -HOSTNAME=openvpncloud.dev +HOSTNAME=cloudconnexa.dev NAMESPACE=openvpn -NAME=openvpncloud -VERSION=0.0.9 +NAME=cloudconnexa +VERSION=0.0.11 BINARY=terraform-provider-${NAME} OS_ARCH=darwin_arm64 diff --git a/client/connector.go b/client/connector.go deleted file mode 100644 index dd5c333..0000000 --- a/client/connector.go +++ /dev/null @@ -1,173 +0,0 @@ -package client - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" -) - -type ConnectionStatus string - -type Connector struct { - Id string `json:"id,omitempty"` - Name string `json:"name"` - NetworkItemId string `json:"networkItemId"` - NetworkItemType string `json:"networkItemType"` - VpnRegionId string `json:"vpnRegionId"` - IPv4Address string `json:"ipV4Address"` - IPv6Address string `json:"ipV6Address"` - Profile string `json:"profile"` - ConnectionStatus ConnectionStatus `json:"connectionStatus"` -} - -type ConnectorPageResponse struct { - Content []Connector `json:"content"` - NumberOfElements int `json:"numberOfElements"` - Page int `json:"page"` - Size int `json:"size"` - Success bool `json:"success"` - TotalElements int `json:"totalElements"` - TotalPages int `json:"totalPages"` -} - -const ( - NetworkItemTypeHost = "HOST" - NetworkItemTypeNetwork = "NETWORK" -) - -const ( - ConnectionStatusOffline ConnectionStatus = "OFFLINE" - ConnectionStatusOnline ConnectionStatus = "ONLINE" -) - -func (c *Client) GetConnectorsByPage(page int, pageSize int) (ConnectorPageResponse, error) { - endpoint := fmt.Sprintf("%s/api/beta/connectors/page?page=%d&size=%d", c.BaseURL, page, pageSize) - req, err := http.NewRequest(http.MethodGet, endpoint, nil) - if err != nil { - return ConnectorPageResponse{}, err - } - - body, err := c.DoRequest(req) - if err != nil { - return ConnectorPageResponse{}, err - } - - var response ConnectorPageResponse - err = json.Unmarshal(body, &response) - if err != nil { - return ConnectorPageResponse{}, err - } - return response, nil -} - -func (c *Client) GetAllConnectors() ([]Connector, error) { - var allConnectors []Connector - page := 1 - pageSize := 10 - - for { - response, err := c.GetConnectorsByPage(page, pageSize) - if err != nil { - return nil, err - } - - allConnectors = append(allConnectors, response.Content...) - - if page >= response.TotalPages { - break - } - page++ - } - return allConnectors, nil -} - -func (c *Client) GetConnectorByName(name string) (*Connector, error) { - connectors, err := c.GetAllConnectors() - if err != nil { - return nil, err - } - - for _, connector := range connectors { - if connector.Name == name { - return &connector, nil - } - } - return nil, nil -} - -func (c *Client) GetConnectorById(connectorId string) (*Connector, error) { - connectors, err := c.GetAllConnectors() - if err != nil { - return nil, err - } - - for _, connector := range connectors { - if connector.Id == connectorId { - return &connector, nil - } - } - return nil, nil -} - -func (c *Client) GetConnectorsForNetwork(networkId string) ([]Connector, error) { - connectors, err := c.GetAllConnectors() - if err != nil { - return nil, err - } - - var networkConnectors []Connector - for _, connector := range connectors { - if connector.NetworkItemId == networkId { - networkConnectors = append(networkConnectors, connector) - } - } - return networkConnectors, nil -} - -func (c *Client) GetConnectorProfile(id string) (string, error) { - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/connectors/%s/profile", c.BaseURL, id), nil) - if err != nil { - return "", err - } - - body, err := c.DoRequest(req) - if err != nil { - return "", err - } - return string(body), nil -} - -func (c *Client) CreateConnector(connector Connector, networkItemId string) (*Connector, error) { - connectorJson, err := json.Marshal(connector) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/connectors?networkItemId=%s&networkItemType=%s", c.BaseURL, networkItemId, connector.NetworkItemType), bytes.NewBuffer(connectorJson)) - if err != nil { - return nil, err - } - - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - - var conn Connector - err = json.Unmarshal(body, &conn) - if err != nil { - return nil, err - } - return &conn, nil -} - -func (c *Client) DeleteConnector(connectorId string, networkItemId string, networkItemType string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/beta/connectors/%s?networkItemId=%s&networkItemType=%s", c.BaseURL, connectorId, networkItemId, networkItemType), nil) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - return err -} diff --git a/client/dns_record.go b/client/dns_record.go deleted file mode 100644 index 12cc300..0000000 --- a/client/dns_record.go +++ /dev/null @@ -1,119 +0,0 @@ -package client - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" -) - -type DnsRecord struct { - Id string `json:"id"` - Domain string `json:"domain"` - Description string `json:"description"` - IPV4Addresses []string `json:"ipv4Addresses"` - IPV6Addresses []string `json:"ipv6Addresses"` -} - -type DnsRecordPageResponse struct { - Content []DnsRecord `json:"content"` - NumberOfElements int `json:"numberOfElements"` - Page int `json:"page"` - Size int `json:"size"` - Success bool `json:"success"` - TotalElements int `json:"totalElements"` - TotalPages int `json:"totalPages"` -} - -func (c *Client) GetDnsRecordsByPage(page int, pageSize int) (DnsRecordPageResponse, error) { - endpoint := fmt.Sprintf("%s/api/beta/dns-records/page?page=%d&size=%d", c.BaseURL, page, pageSize) - req, err := http.NewRequest(http.MethodGet, endpoint, nil) - if err != nil { - return DnsRecordPageResponse{}, err - } - - body, err := c.DoRequest(req) - if err != nil { - return DnsRecordPageResponse{}, err - } - - var response DnsRecordPageResponse - err = json.Unmarshal(body, &response) - if err != nil { - return DnsRecordPageResponse{}, err - } - return response, nil -} - -func (c *Client) GetDnsRecord(recordId string) (*DnsRecord, error) { - pageSize := 10 - page := 1 - - for { - response, err := c.GetDnsRecordsByPage(page, pageSize) - if err != nil { - return nil, err - } - - for _, record := range response.Content { - if record.Id == recordId { - return &record, nil - } - } - - if page >= response.TotalPages { - break - } - page++ - } - return nil, fmt.Errorf("DNS record with ID %s not found", recordId) -} - -func (c *Client) CreateDnsRecord(record DnsRecord) (*DnsRecord, error) { - recordJson, err := json.Marshal(record) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/dns-records", c.BaseURL), bytes.NewBuffer(recordJson)) - if err != nil { - return nil, err - } - - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - - var d DnsRecord - err = json.Unmarshal(body, &d) - if err != nil { - return nil, err - } - return &d, nil -} - -func (c *Client) UpdateDnsRecord(record DnsRecord) error { - recordJson, err := json.Marshal(record) - if err != nil { - return err - } - - req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/beta/dns-records/%s", c.BaseURL, record.Id), bytes.NewBuffer(recordJson)) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - return err -} - -func (c *Client) DeleteDnsRecord(recordId string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/beta/dns-records/%s", c.BaseURL, recordId), nil) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - return err -} diff --git a/client/errors.go b/client/errors.go deleted file mode 100644 index 2b3295d..0000000 --- a/client/errors.go +++ /dev/null @@ -1,5 +0,0 @@ -package client - -import "errors" - -var ErrCredentialsRequired = errors.New("both client_id and client_secret credentials must be specified") diff --git a/client/host.go b/client/host.go deleted file mode 100644 index b2c0ab7..0000000 --- a/client/host.go +++ /dev/null @@ -1,144 +0,0 @@ -package client - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" -) - -type Host struct { - Id string `json:"id,omitempty"` - Name string `json:"name"` - Description string `json:"description"` - InternetAccess string `json:"internetAccess"` - SystemSubnets []string `json:"systemSubnets"` - Connectors []Connector `json:"connectors"` -} - -type HostPageResponse struct { - Content []Host `json:"content"` - NumberOfElements int `json:"numberOfElements"` - Page int `json:"page"` - Size int `json:"size"` - Success bool `json:"success"` - TotalElements int `json:"totalElements"` - TotalPages int `json:"totalPages"` -} - -func (c *Client) GetHostsByPage(page int, size int) (HostPageResponse, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/hosts/page?page=%d&size=%d", c.BaseURL, page, size), nil) - if err != nil { - return HostPageResponse{}, err - } - - body, err := c.DoRequest(req) - if err != nil { - return HostPageResponse{}, err - } - - var response HostPageResponse - err = json.Unmarshal(body, &response) - if err != nil { - return HostPageResponse{}, err - } - return response, nil -} - -func (c *Client) GetAllHosts() ([]Host, error) { - var allHosts []Host - pageSize := 10 - page := 1 - - for { - response, err := c.GetHostsByPage(page, pageSize) - if err != nil { - return nil, err - } - - allHosts = append(allHosts, response.Content...) - - if page >= response.TotalPages { - break - } - page++ - } - return allHosts, nil -} - -func (c *Client) GetHostByName(name string) (*Host, error) { - hosts, err := c.GetAllHosts() - if err != nil { - return nil, err - } - - for _, h := range hosts { - if h.Name == name { - return &h, nil - } - } - return nil, nil -} - -func (c *Client) GetHostById(hostId string) (*Host, error) { - hosts, err := c.GetAllHosts() - if err != nil { - return nil, err - } - - for _, h := range hosts { - if h.Id == hostId { - return &h, nil - } - } - return nil, nil -} - -func (c *Client) CreateHost(host Host) (*Host, error) { - hostJson, err := json.Marshal(host) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/hosts", c.BaseURL), bytes.NewBuffer(hostJson)) - if err != nil { - return nil, err - } - - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - - var h Host - err = json.Unmarshal(body, &h) - if err != nil { - return nil, err - } - return &h, nil -} - -func (c *Client) UpdateHost(host Host) error { - hostJson, err := json.Marshal(host) - if err != nil { - return err - } - - req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/beta/hosts/%s", c.BaseURL, host.Id), bytes.NewBuffer(hostJson)) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - return err -} - -func (c *Client) DeleteHost(hostId string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/beta/hosts/%s", c.BaseURL, hostId), nil) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - return err -} diff --git a/client/ip_service.go b/client/ip_service.go deleted file mode 100644 index 19c04be..0000000 --- a/client/ip_service.go +++ /dev/null @@ -1,160 +0,0 @@ -package client - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - "net/url" -) - -type Range struct { - LowerValue int `json:"lowerValue"` - UpperValue int `json:"upperValue"` -} - -type CustomIpServiceType struct { - IcmpType []Range `json:"icmpType"` - Port []Range `json:"port"` - Protocol string `json:"protocol"` -} - -type IpServiceConfig struct { - CustomServiceTypes []*CustomIpServiceType `json:"customServiceTypes"` - ServiceTypes []string `json:"serviceTypes"` -} - -type IpService struct { - Name string `json:"name"` - Description string `json:"description"` - NetworkItemType string `json:"networkItemType"` - NetworkItemId string `json:"networkItemId"` - Id string `json:"id"` - Type string `json:"type"` - Routes []*Route `json:"routes"` - Config *IpServiceConfig `json:"config"` -} - -type IpServicePageResponse struct { - Content []IpService `json:"content"` - NumberOfElements int `json:"numberOfElements"` - Page int `json:"page"` - Size int `json:"size"` - Success bool `json:"success"` - TotalElements int `json:"totalElements"` - TotalPages int `json:"totalPages"` -} - -func (c *Client) GetIpServicesByPage(page int, pageSize int) (IpServicePageResponse, error) { - endpoint := fmt.Sprintf("%s/api/beta/ip-services/page?page=%d&size=%d", c.BaseURL, page, pageSize) - req, err := http.NewRequest(http.MethodGet, endpoint, nil) - if err != nil { - return IpServicePageResponse{}, err - } - - body, err := c.DoRequest(req) - if err != nil { - return IpServicePageResponse{}, err - } - - var response IpServicePageResponse - err = json.Unmarshal(body, &response) - if err != nil { - return IpServicePageResponse{}, err - } - return response, nil -} - -func (c *Client) GetAllIpServices() ([]IpService, error) { - var allIpServices []IpService - page := 1 - pageSize := 10 - - for { - response, err := c.GetIpServicesByPage(page, pageSize) - if err != nil { - return nil, err - } - - allIpServices = append(allIpServices, response.Content...) - if page >= response.TotalPages { - break - } - page++ - } - return allIpServices, nil -} - -func (c *Client) CreateIpService(ipService *IpService) (*IpService, error) { - ipServiceJson, err := json.Marshal(ipService) - if err != nil { - return nil, err - } - - params := networkUrlParams(ipService.NetworkItemType, ipService.NetworkItemId) - endpoint := fmt.Sprintf("%s/api/beta/ip-services?%s", c.BaseURL, params.Encode()) - - req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewBuffer(ipServiceJson)) - if err != nil { - return nil, err - } - - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - - var s IpService - err = json.Unmarshal(body, &s) - if err != nil { - return nil, err - } - return &s, nil -} - -func (c *Client) UpdateIpService(id string, service *IpService) (*IpService, error) { - serviceJson, err := json.Marshal(service) - if err != nil { - return nil, err - } - - endpoint := fmt.Sprintf("%s/api/beta/ip-services/%s?%s", c.BaseURL, id) - - req, err := http.NewRequest(http.MethodPut, endpoint, bytes.NewBuffer(serviceJson)) - if err != nil { - return nil, err - } - - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - - var s IpService - err = json.Unmarshal(body, &s) - if err != nil { - return nil, err - } - return &s, nil -} - -func (c *Client) DeleteIpService(ipServiceId string) error { - endpoint := fmt.Sprintf("%s/api/beta/ip-services/%s?%s", c.BaseURL, ipServiceId) - req, err := http.NewRequest(http.MethodDelete, endpoint, nil) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - if err != nil { - return err - } - return nil -} - -func networkUrlParams(networkItemType string, networkItemId string) url.Values { - params := url.Values{} - params.Add("networkItemId", networkItemId) - params.Add("networkItemType", networkItemType) - return params -} diff --git a/client/network.go b/client/network.go deleted file mode 100644 index ff12653..0000000 --- a/client/network.go +++ /dev/null @@ -1,164 +0,0 @@ -package client - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" -) - -type NetworkConnector struct { - Description string `json:"description"` - Id string `json:"id"` - IPv4Address string `json:"ipV4Address"` - IPv6Address string `json:"ipV6Address"` - Name string `json:"name"` - NetworkItemId string `json:"networkItemId"` - NetworkItemType string `json:"networkItemType"` - VpnRegionId string `json:"vpnRegionId"` -} - -type Network struct { - Connectors []NetworkConnector `json:"connectors"` - Description string `json:"description"` - Egress bool `json:"egress"` - Id string `json:"id"` - InternetAccess string `json:"internetAccess"` - Name string `json:"name"` - Routes []Route `json:"routes"` - SystemSubnets []string `json:"systemSubnets"` -} - -type NetworkPageResponse struct { - Content []Network `json:"content"` - NumberOfElements int `json:"numberOfElements"` - Page int `json:"page"` - Size int `json:"size"` - Success bool `json:"success"` - TotalElements int `json:"totalElements"` - TotalPages int `json:"totalPages"` -} - -const ( - InternetAccessBlocked = "BLOCKED" - InternetAccessGlobalInternet = "GLOBAL_INTERNET" - InternetAccessLocal = "LOCAL" -) - -func (c *Client) GetNetworksByPage(page int, size int) (NetworkPageResponse, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/networks/page?page=%d&size=%d", c.BaseURL, page, size), nil) - if err != nil { - return NetworkPageResponse{}, err - } - - body, err := c.DoRequest(req) - if err != nil { - return NetworkPageResponse{}, err - } - - var response NetworkPageResponse - err = json.Unmarshal(body, &response) - if err != nil { - return NetworkPageResponse{}, err - } - - return response, nil -} - -func (c *Client) GetAllNetworks() ([]Network, error) { - var allNetworks []Network - pageSize := 10 - page := 1 - - for { - response, err := c.GetNetworksByPage(page, pageSize) - if err != nil { - return nil, err - } - - allNetworks = append(allNetworks, response.Content...) - - if page >= response.TotalPages { - break - } - page++ - } - return allNetworks, nil -} - -func (c *Client) GetNetworkByName(name string) (*Network, error) { - networks, err := c.GetAllNetworks() - if err != nil { - return nil, err - } - - for _, n := range networks { - if n.Name == name { - return &n, nil - } - } - return nil, nil -} - -func (c *Client) GetNetworkById(networkId string) (*Network, error) { - networks, err := c.GetAllNetworks() - if err != nil { - return nil, err - } - - for _, n := range networks { - if n.Id == networkId { - return &n, nil - } - } - return nil, nil -} - -func (c *Client) CreateNetwork(network Network) (*Network, error) { - networkJson, err := json.Marshal(network) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/networks", c.BaseURL), bytes.NewBuffer(networkJson)) - if err != nil { - return nil, err - } - - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - - var n Network - err = json.Unmarshal(body, &n) - if err != nil { - return nil, err - } - return &n, nil -} - -func (c *Client) UpdateNetwork(network Network) error { - networkJson, err := json.Marshal(network) - if err != nil { - return err - } - - req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/beta/networks/%s", c.BaseURL, network.Id), bytes.NewBuffer(networkJson)) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - return err -} - -func (c *Client) DeleteNetwork(networkId string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/beta/networks/%s", c.BaseURL, networkId), nil) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - return err -} diff --git a/client/route.go b/client/route.go deleted file mode 100644 index e555f73..0000000 --- a/client/route.go +++ /dev/null @@ -1,175 +0,0 @@ -package client - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" -) - -type Route struct { - Id string `json:"id,omitempty"` - Type string `json:"type,omitempty"` - Subnet string `json:"subnet,omitempty"` - Description string `json:"description,omitempty"` - NetworkItemId string `json:"networkItemId,omitempty"` -} - -type RoutePageResponse struct { - Success bool `json:"success"` - Content []Route `json:"content"` - TotalElements int `json:"totalElements"` - TotalPages int `json:"totalPages"` - NumberOfElements int `json:"numberOfElements"` - Page int `json:"page"` - Size int `json:"size"` -} - -const ( - RouteTypeIPV4 = "IP_V4" - RouteTypeIPV6 = "IP_V6" - RouteTypeDomain = "DOMAIN" -) - -func (c *Client) GetRoutesByPage(networkId string, page int, size int) (RoutePageResponse, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/networks/%s/routes/page?page=%d&size=%d", c.BaseURL, networkId, page, size), nil) - if err != nil { - return RoutePageResponse{}, err - } - - body, err := c.DoRequest(req) - if err != nil { - return RoutePageResponse{}, err - } - - var response RoutePageResponse - err = json.Unmarshal(body, &response) - if err != nil { - return RoutePageResponse{}, err - } - return response, nil -} - -func (c *Client) GetAllRoutes(networkId string) ([]Route, error) { - var allRoutes []Route - pageSize := 10 - page := 1 - - for { - response, err := c.GetRoutesByPage(networkId, page, pageSize) - if err != nil { - return nil, err - } - - allRoutes = append(allRoutes, response.Content...) - - if page >= response.TotalPages { - break - } - page++ - } - return allRoutes, nil -} - -func (c *Client) GetNetworkRoute(networkId string, routeId string) (*Route, error) { - routes, err := c.GetAllRoutes(networkId) - if err != nil { - return nil, err - } - - for _, r := range routes { - if r.Id == routeId { - return &r, nil - } - } - return nil, nil -} - -func (c *Client) GetRouteById(routeId string) (*Route, error) { - networks, err := c.GetAllNetworks() - if err != nil { - return nil, err - } - - for _, n := range networks { - routes, err := c.GetAllRoutes(n.Id) - if err != nil { - continue - } - for _, r := range routes { - if r.Id == routeId { - r.NetworkItemId = n.Id - return &r, nil - } - } - } - return nil, nil -} - -func (c *Client) CreateRoute(networkId string, route Route) (*Route, error) { - type newRoute struct { - Description string `json:"description"` - Subnet string `json:"subnet"` - } - routeToCreate := newRoute{ - Description: route.Description, - Subnet: route.Subnet, - } - routeJson, err := json.Marshal(routeToCreate) - if err != nil { - return nil, err - } - - req, err := http.NewRequest( - http.MethodPost, - fmt.Sprintf("%s/api/beta/networks/%s/routes", c.BaseURL, networkId), - bytes.NewBuffer(routeJson), - ) - if err != nil { - return nil, err - } - - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - - var r Route - err = json.Unmarshal(body, &r) - if err != nil { - return nil, err - } - - // The API does not return the route Value, so we set it manually. - r.Subnet = routeToCreate.Subnet - return &r, nil -} - -func (c *Client) UpdateRoute(networkId string, route Route) error { - routeJson, err := json.Marshal(route) - if err != nil { - return err - } - - req, err := http.NewRequest( - http.MethodPut, - fmt.Sprintf("%s/api/beta/networks/%s/routes/%s", c.BaseURL, networkId, route.Id), - bytes.NewBuffer(routeJson), - ) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - return err -} - -func (c *Client) DeleteRoute(networkId string, routeId string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/beta/networks/%s/routes/%s", c.BaseURL, networkId, routeId), nil) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - return err -} diff --git a/client/user.go b/client/user.go deleted file mode 100644 index 9c22040..0000000 --- a/client/user.go +++ /dev/null @@ -1,159 +0,0 @@ -package client - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" -) - -type User struct { - Id string `json:"id"` - Username string `json:"username"` - Role string `json:"role"` - Email string `json:"email"` - AuthType string `json:"authType"` - FirstName string `json:"firstName"` - LastName string `json:"lastName"` - GroupId string `json:"groupId"` - Status string `json:"status"` - Devices []Device `json:"devices"` -} - -type UserPageResponse struct { - Content []User `json:"content"` - NumberOfElements int `json:"numberOfElements"` - Page int `json:"page"` - Size int `json:"size"` - Success bool `json:"success"` - TotalElements int `json:"totalElements"` - TotalPages int `json:"totalPages"` -} - -type Device struct { - Id string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - IPv4Address string `json:"ipV4Address"` - IPv6Address string `json:"ipV6Address"` -} - -func (c *Client) GetUsersByPage(page int, pageSize int) (UserPageResponse, error) { - endpoint := fmt.Sprintf("%s/api/beta/users/page?page=%d&size=%d", c.BaseURL, page, pageSize) - req, err := http.NewRequest(http.MethodGet, endpoint, nil) - if err != nil { - return UserPageResponse{}, err - } - - body, err := c.DoRequest(req) - if err != nil { - return UserPageResponse{}, err - } - - var response UserPageResponse - err = json.Unmarshal(body, &response) - if err != nil { - return UserPageResponse{}, err - } - return response, nil -} - -func (c *Client) GetUser(username string, role string) (*User, error) { - pageSize := 10 - page := 1 - - for { - response, err := c.GetUsersByPage(page, pageSize) - if err != nil { - return nil, err - } - - for _, user := range response.Content { - if user.Username == username && user.Role == role { - return &user, nil - } - } - - if page >= response.TotalPages { - break - } - page++ - } - return nil, fmt.Errorf("user with username %s and role %s not found", username, role) -} - -func (c *Client) GetUserById(userId string) (*User, error) { - pageSize := 10 - page := 1 - - for { - response, err := c.GetUsersByPage(page, pageSize) - if err != nil { - return nil, err - } - - for _, user := range response.Content { - if user.Id == userId { - return &user, nil - } - } - - if page >= response.TotalPages { - break - } - page++ - } - return nil, fmt.Errorf("user with ID %s not found", userId) -} - -func (c *Client) CreateUser(user User) (*User, error) { - userJson, err := json.Marshal(user) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/users", c.BaseURL), bytes.NewBuffer(userJson)) - if err != nil { - return nil, err - } - - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - - var u User - err = json.Unmarshal(body, &u) - if err != nil { - return nil, err - } - return &u, nil -} - -func (c *Client) UpdateUser(user User) error { - userJson, err := json.Marshal(user) - if err != nil { - return err - } - - req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/beta/users/%s", c.BaseURL, user.Id), bytes.NewBuffer(userJson)) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - if err != nil { - return err - } - return nil -} - -func (c *Client) DeleteUser(userId string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/beta/users/%s", c.BaseURL, userId), nil) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - return err -} diff --git a/client/user_group.go b/client/user_group.go deleted file mode 100644 index 35e0b2a..0000000 --- a/client/user_group.go +++ /dev/null @@ -1,124 +0,0 @@ -package client - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" -) - -type UserGroup struct { - Id string `json:"id"` - Name string `json:"name"` - ConnectAuth string `json:"connectAuth"` - VpnRegionIds []string `json:"vpnRegionIds"` - InternetAccess string `json:"internetAccess"` - MaxDevice int `json:"maxDevice"` - SystemSubnets []string `json:"systemSubnets"` -} - -func (c *Client) GetUserGroups() ([]UserGroup, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/user-groups", c.BaseURL), nil) - if err != nil { - return nil, err - } - - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - - var userGroups []UserGroup - err = json.Unmarshal(body, &userGroups) - if err != nil { - return nil, err - } - return userGroups, nil -} -func (c *Client) GetUserGroupByName(name string) (*UserGroup, error) { - userGroups, err := c.GetUserGroups() - if err != nil { - return nil, err - } - - for _, ug := range userGroups { - if ug.Name == name { - return &ug, nil - } - } - return nil, fmt.Errorf("group %s does not exist", name) -} - -func (c *Client) GetUserGroupById(id string) (*UserGroup, error) { - userGroups, err := c.GetUserGroups() - if err != nil { - return nil, err - } - - for _, ug := range userGroups { - if ug.Id == id { - return &ug, nil - } - } - return nil, fmt.Errorf("group %s does not exist", id) -} - -func (c *Client) CreateUserGroup(userGroup *UserGroup) (*UserGroup, error) { - userGroupJson, err := json.Marshal(userGroup) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/api/beta/user-groups", c.BaseURL), bytes.NewBuffer(userGroupJson)) - if err != nil { - return nil, err - } - - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - - var ug UserGroup - err = json.Unmarshal(body, &ug) - if err != nil { - return nil, err - } - return &ug, nil -} - -func (c *Client) UpdateUserGroup(id string, userGroup *UserGroup) (*UserGroup, error) { - userGroupJson, err := json.Marshal(userGroup) - if err != nil { - return nil, err - } - - req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("%s/api/beta/user-groups/%s", c.BaseURL, id), bytes.NewBuffer(userGroupJson)) - if err != nil { - return nil, err - } - - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - var ug UserGroup - err = json.Unmarshal(body, &ug) - if err != nil { - return nil, err - } - return &ug, nil -} - -func (c *Client) DeleteUserGroup(id string) error { - req, err := http.NewRequest(http.MethodDelete, fmt.Sprintf("%s/api/beta/user-groups/%s", c.BaseURL, id), nil) - if err != nil { - return err - } - - _, err = c.DoRequest(req) - if err != nil { - return err - } - return nil -} diff --git a/client/vpn_region.go b/client/vpn_region.go deleted file mode 100644 index d21522d..0000000 --- a/client/vpn_region.go +++ /dev/null @@ -1,40 +0,0 @@ -package client - -import ( - "encoding/json" - "fmt" - "net/http" -) - -type VpnRegion struct { - Id string `json:"id"` - Continent string `json:"continent"` - Country string `json:"country"` - CountryISO string `json:"countryIso"` - RegionName string `json:"regionName"` -} - -func (c *Client) GetVpnRegion(regionId string) (*VpnRegion, error) { - req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/beta/regions", c.BaseURL), nil) - if err != nil { - return nil, err - } - - body, err := c.DoRequest(req) - if err != nil { - return nil, err - } - - var vpnRegions []VpnRegion - err = json.Unmarshal(body, &vpnRegions) - if err != nil { - return nil, err - } - - for _, r := range vpnRegions { - if r.Id == regionId { - return &r, nil - } - } - return nil, nil -} diff --git a/docs/data-sources/connector.md b/docs/data-sources/connector.md index 019e262..bfb4e34 100644 --- a/docs/data-sources/connector.md +++ b/docs/data-sources/connector.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_connector Data Source - terraform-provider-openvpncloud" +page_title: "cloudconnexa_connector Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use an openvpncloud_connector data source to read an existing OpenVPN Cloud connector. + Use an cloudconnexa_connector data source to read an existing OpenVPN Cloud connector. --- -# openvpncloud_connector (Data Source) +# cloudconnexa_connector (Data Source) -Use an `openvpncloud_connector` data source to read an existing OpenVPN Cloud connector. +Use an `cloudconnexa_connector` data source to read an existing OpenVPN Cloud connector. diff --git a/docs/data-sources/host.md b/docs/data-sources/host.md index 582d4a5..08f92ff 100644 --- a/docs/data-sources/host.md +++ b/docs/data-sources/host.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_host Data Source - terraform-provider-openvpncloud" +page_title: "cloudconnexa_host Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use an openvpncloud_host data source to read an existing OpenVPN Cloud connector. + Use an cloudconnexa_host data source to read an existing OpenVPN Cloud connector. --- -# openvpncloud_host (Data Source) +# cloudconnexa_host (Data Source) -Use an `openvpncloud_host` data source to read an existing OpenVPN Cloud connector. +Use an `cloudconnexa_host` data source to read an existing OpenVPN Cloud connector. diff --git a/docs/data-sources/network.md b/docs/data-sources/network.md index 81e2d39..bb8c3f0 100644 --- a/docs/data-sources/network.md +++ b/docs/data-sources/network.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_network Data Source - terraform-provider-openvpncloud" +page_title: "cloudconnexa_network Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use a openvpncloud_network data source to read an OpenVPN Cloud network. + Use a cloudconnexa_network data source to read an OpenVPN Cloud network. --- -# openvpncloud_network (Data Source) +# cloudconnexa_network (Data Source) -Use a `openvpncloud_network` data source to read an OpenVPN Cloud network. +Use a `cloudconnexa_network` data source to read an OpenVPN Cloud network. diff --git a/docs/data-sources/network_routes.md b/docs/data-sources/network_routes.md index 2f3f0fc..3149367 100644 --- a/docs/data-sources/network_routes.md +++ b/docs/data-sources/network_routes.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_network_routes Data Source - terraform-provider-openvpncloud" +page_title: "cloudconnexa_network_routes Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use an openvpncloud_network_routes data source to read all the routes associated with an OpenVPN Cloud network. + Use an cloudconnexa_network_routes data source to read all the routes associated with an OpenVPN Cloud network. --- -# openvpncloud_network_routes (Data Source) +# cloudconnexa_network_routes (Data Source) -Use an `openvpncloud_network_routes` data source to read all the routes associated with an OpenVPN Cloud network. +Use an `cloudconnexa_network_routes` data source to read all the routes associated with an OpenVPN Cloud network. diff --git a/docs/data-sources/user.md b/docs/data-sources/user.md index b27ae24..784f834 100644 --- a/docs/data-sources/user.md +++ b/docs/data-sources/user.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_user Data Source - terraform-provider-openvpncloud" +page_title: "cloudconnexa_user Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use a openvpncloud_user data source to read a specific OpenVPN Cloud user. + Use a cloudconnexa_user data source to read a specific OpenVPN Cloud user. --- -# openvpncloud_user (Data Source) +# cloudconnexa_user (Data Source) -Use a `openvpncloud_user` data source to read a specific OpenVPN Cloud user. +Use a `cloudconnexa_user` data source to read a specific OpenVPN Cloud user. diff --git a/docs/data-sources/user_group.md b/docs/data-sources/user_group.md index 2bd9462..8d5d120 100644 --- a/docs/data-sources/user_group.md +++ b/docs/data-sources/user_group.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_user_group Data Source - terraform-provider-openvpncloud" +page_title: "cloudconnexa_user_group Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use an openvpncloud_user_group data source to read an OpenVPN Cloud user group. + Use an cloudconnexa_user_group data source to read an OpenVPN Cloud user group. --- -# openvpncloud_user_group (Data Source) +# cloudconnexa_user_group (Data Source) -Use an `openvpncloud_user_group` data source to read an OpenVPN Cloud user group. +Use an `cloudconnexa_user_group` data source to read an OpenVPN Cloud user group. diff --git a/docs/data-sources/vpn_region.md b/docs/data-sources/vpn_region.md index 3dba055..41e5fca 100644 --- a/docs/data-sources/vpn_region.md +++ b/docs/data-sources/vpn_region.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_vpn_region Data Source - terraform-provider-openvpncloud" +page_title: "cloudconnexa_vpn_region Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use a openvpncloud_vpn_region data source to read an OpenVPN Cloud VPN region. + Use a cloudconnexa_vpn_region data source to read an OpenVPN Cloud VPN region. --- -# openvpncloud_vpn_region (Data Source) +# cloudconnexa_vpn_region (Data Source) -Use a `openvpncloud_vpn_region` data source to read an OpenVPN Cloud VPN region. +Use a `cloudconnexa_vpn_region` data source to read an OpenVPN Cloud VPN region. diff --git a/docs/index.md b/docs/index.md index 87c00e1..4ba6dbf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,36 +1,36 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud Provider" +page_title: "cloudconnexa Provider" subcategory: "" description: |- --- -# openvpncloud Provider +# CloudConnexa Provider !> **WARNING:** This provider is experimental and support for it is on a best-effort basis. Additionally, the underlying API for OpenVPN Cloud is on Beta, which means that future versions of the provider may introduce breaking changes. Should that happen, migration documentation and support will also be provided on a best-effort basis. -Use this provider to interact with the [OpenVPN Cloud API](https://openvpn.net/cloud-docs/api-guide/). +Use this provider to interact with the [Cloud Connexa API](https://openvpn.net/cloud-docs/developer/index.html). ## Schema ### Required -- **base_url** (String) The base url of your OpenVPN Cloud accout. +- **base_url** (String) The base url of your Cloud Connexa account. ### Optional -- **client_id** (String, Sensitive) If not provided, it will default to the value of the `OPENVPN_CLOUD_CLIENT_ID` environment variable. -- **client_secret** (String, Sensitive) If not provided, it will default to the value of the `OPENVPN_CLOUD_CLIENT_SECRET` environment variable. +- **client_id** (String, Sensitive) If not provided, it will default to the value of the `CLOUDCONNEXA_CLIENT_ID` environment variable. +- **client_secret** (String, Sensitive) If not provided, it will default to the value of the `CLOUDCONNEXA_CLIENT_SECRET` environment variable. ### Credentials -To authenticate with the OpenVPN Cloud API, you'll need the client_id and client_secret. -These credentials can be found in the OpenVPN Cloud Portal. +To authenticate with the Cloud Connexa API, you'll need the client_id and client_secret. +These credentials can be found in the Cloud Connexa Portal. Go to the Settings page and click on the API tab. From there, you can enable the API and generate new authentication credentials. Additionally, you'll find Swagger documentation for the API in the same location. More documentation on the OpenVPN API can be found here: -[OpenVPN API Documentation](https://openvpn.net/cloud-docs/developer/openvpn-api.html) \ No newline at end of file +[Cloud Connexa API Documentation](https://openvpn.net/cloud-docs/developer/cloudconnexa-api.html) \ No newline at end of file diff --git a/docs/resources/connector.md b/docs/resources/connector.md index 2842aa2..a3ea7c7 100644 --- a/docs/resources/connector.md +++ b/docs/resources/connector.md @@ -1,15 +1,15 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_connector Resource - terraform-provider-openvpncloud" +page_title: "cloudconnexa_connector Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use openvpncloud_connector to create an OpenVPN Cloud connector. + Use cloudconnexa_connector to create an OpenVPN Cloud connector. ~> NOTE: This only creates the OpenVPN Cloud connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information. --- -# openvpncloud_connector (Resource) +# cloudconnexa_connector (Resource) -Use `openvpncloud_connector` to create an OpenVPN Cloud connector. +Use `cloudconnexa_connector` to create an OpenVPN Cloud connector. ~> NOTE: This only creates the OpenVPN Cloud connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information. @@ -36,7 +36,7 @@ Use `openvpncloud_connector` to create an OpenVPN Cloud connector. A connector can be imported using the connector ID, which can be fetched directly from the API. ``` -terraform import openvpncloud_connector.connector +terraform import cloudconnexa_connector.connector ``` ~> NOTE: If the Terraform resource settings are different from the imported connector, the next time you run `terraform apply` the provider will attempt to delete and recreate the connector, which will require you to re-configure the instance manually. \ No newline at end of file diff --git a/docs/resources/dns_record.md b/docs/resources/dns_record.md index d4981c6..ab826ed 100644 --- a/docs/resources/dns_record.md +++ b/docs/resources/dns_record.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_dns_record Resource - terraform-provider-openvpncloud" +page_title: "cloudconnexa_dns_record Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use openvpncloud_dns_record to create a DNS record on your VPN. + Use cloudconnexa_dns_record to create a DNS record on your VPN. --- -# openvpncloud_dns_record (Resource) +# cloudconnexa_dns_record (Resource) -Use `openvpncloud_dns_record` to create a DNS record on your VPN. +Use `cloudconnexa_dns_record` to create a DNS record on your VPN. @@ -33,5 +33,5 @@ Use `openvpncloud_dns_record` to create a DNS record on your VPN. A connector can be imported using the DNS record ID, which can be fetched directly from the API. ``` -terraform import openvpncloud_dns_record.record +terraform import cloudconnexa_dns_record.record ``` \ No newline at end of file diff --git a/docs/resources/host.md b/docs/resources/host.md index c37afbd..fb51956 100644 --- a/docs/resources/host.md +++ b/docs/resources/host.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_host Resource - terraform-provider-openvpncloud" +page_title: "cloudconnexa_host Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use openvpncloud_host to create an OpenVPN Cloud host. + Use cloudconnexa_host to create an OpenVPN Cloud host. --- -# openvpncloud_host (Resource) +# cloudconnexa_host (Resource) -Use `openvpncloud_host` to create an OpenVPN Cloud host. +Use `cloudconnexa_host` to create an OpenVPN Cloud host. @@ -51,5 +51,5 @@ Read-Only: A host can be imported using the DNS record ID, which can be fetched directly from the API. ``` -terraform import openvpncloud_host.host +terraform import cloudconnexa_host.host ``` \ No newline at end of file diff --git a/docs/resources/network.md b/docs/resources/network.md index 3cc141b..6ded36e 100644 --- a/docs/resources/network.md +++ b/docs/resources/network.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_network Resource - terraform-provider-openvpncloud" +page_title: "cloudconnexa_network Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use openvpncloud_network to create an OpenVPN Cloud Network. + Use cloudconnexa_network to create an OpenVPN Cloud Network. --- -# openvpncloud_network (Resource) +# cloudconnexa_network (Resource) -Use `openvpncloud_network` to create an OpenVPN Cloud Network. +Use `cloudconnexa_network` to create an OpenVPN Cloud Network. @@ -67,7 +67,7 @@ Read-Only: A network can be imported using the network ID, which can be fetched directly from the API. ``` -terraform import openvpncloud_network.network +terraform import cloudconnexa_network.network ``` ~> NOTE: This will only import the network itslef, but it'll create a new connector and a new route as its defaults. There is currently no way to import an existing connector and route along with a network. The existing connector(s)/route(s) will continue to work, but you'll need to set a `default_connector` and a `default_route` that don't collide with your existing resources. \ No newline at end of file diff --git a/docs/resources/route.md b/docs/resources/route.md index 66e31ae..019cadd 100644 --- a/docs/resources/route.md +++ b/docs/resources/route.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_route Resource - terraform-provider-openvpncloud" +page_title: "cloudconnexa_route Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use openvpncloud_route to create a route on an OpenVPN Cloud network. + Use cloudconnexa_route to create a route on an OpenVPN Cloud network. --- -# openvpncloud_route (Resource) +# cloudconnexa_route (Resource) -Use `openvpncloud_route` to create a route on an OpenVPN Cloud network. +Use `cloudconnexa_route` to create a route on an OpenVPN Cloud network. @@ -30,5 +30,5 @@ Use `openvpncloud_route` to create a route on an OpenVPN Cloud network. A route can be imported using the route ID, which can be fetched directly from the API. ``` -terraform import openvpncloud_route.route +terraform import cloudconnexa_route.route ``` \ No newline at end of file diff --git a/docs/resources/user.md b/docs/resources/user.md index 990d12f..af8a8ba 100644 --- a/docs/resources/user.md +++ b/docs/resources/user.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_user Resource - terraform-provider-openvpncloud" +page_title: "cloudconnexa_user Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use openvpncloud_user to create an OpenVPN Cloud user. + Use cloudconnexa_user to create an OpenVPN Cloud user. --- -# openvpncloud_user (Resource) +# cloudconnexa_user (Resource) -Use `openvpncloud_user` to create an OpenVPN Cloud user. +Use `cloudconnexa_user` to create an OpenVPN Cloud user. @@ -49,5 +49,5 @@ Optional: A user can be imported using the user ID using the format below. ``` -terraform import openvpncloud_user.user username@accountname +terraform import cloudconnexa_user.user username@accountname ``` diff --git a/e2e/integration_test.go b/e2e/integration_test.go index f1f15f8..a8528c5 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -12,9 +12,9 @@ import ( ) const ( - OpenvpnHostKey = "OVPN_HOST" - OpenvpnCloudClientIdKey = "OPENVPN_CLOUD_CLIENT_ID" - OpenvpnCloudClientSecretKey = "OPENVPN_CLOUD_CLIENT_SECRET" + CloudConnexaHostKey = "OVPN_HOST" + CloudConnexaClientIdKey = "CLOUDCONNEXA_CLIENT_ID" + CloudConnexaClientSecretKey = "CLOUDCONNEXA_CLIENT_SECRET" ) func TestCreationDeletion(t *testing.T) { @@ -47,9 +47,9 @@ func TestCreationDeletion(t *testing.T) { assert.NotEmpty(t, connectorID) client, err := api.NewClient( - os.Getenv(OpenvpnHostKey), - os.Getenv(OpenvpnCloudClientIdKey), - os.Getenv(OpenvpnCloudClientSecretKey), + os.Getenv(CloudConnexaHostKey), + os.Getenv(CloudConnexaClientIdKey), + os.Getenv(CloudConnexaClientSecretKey), ) require.NoError(t, err) @@ -72,9 +72,9 @@ func TestCreationDeletion(t *testing.T) { } func validateEnvVars(t *testing.T) { - validateEnvVar(t, OpenvpnHostKey) - validateEnvVar(t, OpenvpnCloudClientIdKey) - validateEnvVar(t, OpenvpnCloudClientSecretKey) + validateEnvVar(t, CloudConnexaHostKey) + validateEnvVar(t, CloudConnexaClientIdKey) + validateEnvVar(t, CloudConnexaClientSecretKey) } func validateEnvVar(t *testing.T, envVar string) { diff --git a/e2e/setup/main.tf b/e2e/setup/main.tf index 2a00a60..77e3a31 100644 --- a/e2e/setup/main.tf +++ b/e2e/setup/main.tf @@ -1,8 +1,8 @@ terraform { required_providers { openvpn-cloud = { - version = "0.0.7" - source = "openvpncloud.dev/openvpn/openvpncloud" + version = "0.0.11" + source = "cloudconnexa.dev/openvpn/openvpncloud" } } } @@ -16,7 +16,7 @@ variable "host_name" { type = string } -resource "openvpncloud_host" "host" { +resource "cloudconnexa_host" "host" { name = "TEST_HOST_NAME" description = "Terraform test description 2" internet_access = "LOCAL" @@ -30,14 +30,14 @@ resource "openvpncloud_host" "host" { } locals { - connector_profile = [for connector in openvpncloud_host.host.connector : connector.profile][0] + connector_profile = [for connector in cloudconnexa_host.host.connector : connector.profile][0] } output "host_id" { - value = openvpncloud_host.host.id + value = cloudconnexa_host.host.id } output "connector_id" { - value = [for connector in openvpncloud_host.host.connector : connector.id][0] + value = [for connector in cloudconnexa_host.host.connector : connector.id][0] } diff --git a/example/connectors.tf b/example/connectors.tf index f842d68..a345ff9 100644 --- a/example/connectors.tf +++ b/example/connectors.tf @@ -1,10 +1,10 @@ -data "openvpncloud_network" "test-net" { +data "cloudconnexa_network" "test-net" { name = "test-net" } -resource "openvpncloud_connector" "test-connector" { +resource "cloudconnexa_connector" "test-connector" { name = "test-connector" vpn_region_id = "eu-central-1" network_item_type = "NETWORK" - network_item_id = data.openvpncloud_network.test-net.network_id + network_item_id = data.cloudconnexa_network.test-net.network_id } diff --git a/example/hosts.tf b/example/hosts.tf index 85cedce..d0466ac 100644 --- a/example/hosts.tf +++ b/example/hosts.tf @@ -1,4 +1,4 @@ -resource "openvpncloud_host" "test-host" { +resource "cloudconnexa_host" "test-host" { name = "test-host" connector { name = "test-connector" diff --git a/example/networks.tf b/example/networks.tf index 8c1bd73..3920eb1 100644 --- a/example/networks.tf +++ b/example/networks.tf @@ -1,4 +1,4 @@ -resource "openvpncloud_network" "test-network" { +resource "cloudconnexa_network" "test-network" { name = "test-network" egress = false default_route { diff --git a/example/provider.tf b/example/provider.tf index 9083d2b..0005b48 100644 --- a/example/provider.tf +++ b/example/provider.tf @@ -3,5 +3,5 @@ provider "openvpncloud" { } ## Environment Variables example: -# export OPENVPN_CLOUD_CLIENT_ID="" -# export OPENVPN_CLOUD_CLIENT_SECRET="" +# export CLOUDCONNEXA_CLIENT_ID="" +# export CLOUDCONNEXA_CLIENT_SECRET="" diff --git a/example/routes.tf b/example/routes.tf index d36c66b..55bdb1c 100644 --- a/example/routes.tf +++ b/example/routes.tf @@ -1,4 +1,4 @@ -resource "openvpncloud_route" "this" { +resource "cloudconnexa_route" "this" { for_each = { for key, route in var.routes : route.value => route } diff --git a/example/services.tf b/example/services.tf index 2b39249..3e8d3d5 100644 --- a/example/services.tf +++ b/example/services.tf @@ -1,14 +1,14 @@ -data "openvpncloud_network" "test-net" { +data "cloudconnexa_network" "test-net" { name = "test-net" } -resource "openvpncloud_service" "test-service" { +resource "cloudconnexa_service" "test-service" { name = "test-service" type = "IP_SOURCE" description = "test-description" routes = ["10.0.0.2/32"] network_item_type = "NETWORK" - network_item_id = data.openvpncloud_network.test-net.network_id + network_item_id = data.cloudconnexa_network.test-net.network_id config { service_types = ["ANY"] diff --git a/example/user_groups.tf b/example/user_groups.tf index abb9fd2..ce8e587 100644 --- a/example/user_groups.tf +++ b/example/user_groups.tf @@ -1,4 +1,4 @@ -resource "openvpncloud_user_group" "this" { +resource "cloudconnexa_user_group" "this" { name = "test-group" vpn_region_ids = ["eu-central-1"] connect_auth = "AUTH" diff --git a/example/users.tf b/example/users.tf index 96dc9fa..f6ac19d 100644 --- a/example/users.tf +++ b/example/users.tf @@ -1,4 +1,4 @@ -resource "openvpncloud_user" "this" { +resource "cloudconnexa_user" "this" { for_each = var.users username = each.value.username email = each.value.email diff --git a/go.mod b/go.mod index d67a6db..530ec43 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,17 @@ module github.com/OpenVPN/terraform-provider-openvpn-cloud -go 1.19 +go 1.21 + +toolchain go1.21.3 require ( github.com/gruntwork-io/terratest v0.46.1 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0 + github.com/openvpn/cloudconnexa-go-client/v2 v2.0.0-20240111183827-7c0db1bee7e1 github.com/stretchr/testify v1.8.4 - golang.org/x/time v0.3.0 + golang.org/x/oauth2 v0.7.0 + golang.org/x/time v0.5.0 ) require ( @@ -74,7 +78,6 @@ require ( golang.org/x/crypto v0.14.0 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.17.0 // indirect - golang.org/x/oauth2 v0.7.0 // indirect golang.org/x/sync v0.1.0 // indirect golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect diff --git a/go.sum b/go.sum index 06cdad3..2f4c433 100644 --- a/go.sum +++ b/go.sum @@ -116,6 +116,7 @@ cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQn cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= @@ -186,14 +187,17 @@ cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuW cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= +github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= @@ -209,6 +213,7 @@ github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= +github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -234,6 +239,7 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs 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/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -249,8 +255,11 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= +github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= +github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -317,6 +326,7 @@ github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIG github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -407,7 +417,9 @@ github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKe github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a h1:zPPuIq2jAWWPTrGt70eK/BSch+gFAGrNzecsoENgu2o= github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -417,6 +429,7 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfC github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= @@ -456,7 +469,10 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/openvpn/cloudconnexa-go-client/v2 v2.0.0-20240111183827-7c0db1bee7e1 h1:92Qkf8eLZ0YXNoGQzMttkyCxUoQtCHBvEznh0swRJOY= +github.com/openvpn/cloudconnexa-go-client/v2 v2.0.0-20240111183827-7c0db1bee7e1/go.mod h1:zKYdNzbENBz0jI9EWBOjXCyjGuGcpnOwmoE8lwDXmNU= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= +github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -465,7 +481,9 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= +github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -496,6 +514,7 @@ github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgq github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -743,6 +762,7 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -760,8 +780,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1051,10 +1071,12 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 7d94bc9..80e7bc3 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/OpenVPN/terraform-provider-openvpn-cloud/openvpncloud" + "github.com/OpenVPN/terraform-provider-openvpn-cloud/cloudconnexa" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" @@ -10,7 +10,7 @@ import ( func main() { plugin.Serve(&plugin.ServeOpts{ ProviderFunc: func() *schema.Provider { - return openvpncloud.Provider() + return cloudconnexa.Provider() }, }) } diff --git a/openvpncloud/data_source_connector.go b/openvpncloud/data_source_connector.go deleted file mode 100644 index fd12f33..0000000 --- a/openvpncloud/data_source_connector.go +++ /dev/null @@ -1,67 +0,0 @@ -package openvpncloud - -import ( - "context" - "strconv" - "time" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourceConnector() *schema.Resource { - return &schema.Resource{ - Description: "Use an `openvpncloud_connector` data source to read an existing OpenVPN Cloud connector.", - ReadContext: dataSourceConnectorRead, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: "The name of the connector.", - }, - "network_item_id": { - Type: schema.TypeString, - Computed: true, - Description: "The id of the network or host with which the connector is associated.", - }, - "network_item_type": { - Type: schema.TypeString, - Computed: true, - Description: "The network object type of the connector. This typically will be set to either `NETWORK` or `HOST`.", - }, - "vpn_region_id": { - Type: schema.TypeString, - Computed: true, - Description: "The id of the region where the connector is deployed.", - }, - "ip_v4_address": { - Type: schema.TypeString, - Computed: true, - Description: "The IPV4 address of the connector.", - }, - "ip_v6_address": { - Type: schema.TypeString, - Computed: true, - Description: "The IPV6 address of the connector.", - }, - }, - } -} - -func dataSourceConnectorRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - connector, err := c.GetConnectorByName(d.Get("name").(string)) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - d.Set("name", connector.Name) - d.Set("network_item_id", connector.NetworkItemId) - d.Set("network_item_type", connector.NetworkItemType) - d.Set("vpn_region_id", connector.VpnRegionId) - d.Set("ip_v4_address", connector.IPv4Address) - d.Set("ip_v6_address", connector.IPv6Address) - d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) - return diags -} diff --git a/openvpncloud/data_source_host.go b/openvpncloud/data_source_host.go deleted file mode 100644 index b043137..0000000 --- a/openvpncloud/data_source_host.go +++ /dev/null @@ -1,97 +0,0 @@ -package openvpncloud - -import ( - "context" - "strconv" - "time" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourceHost() *schema.Resource { - return &schema.Resource{ - Description: "Use an `openvpncloud_host` data source to read an existing OpenVPN Cloud connector.", - ReadContext: dataSourceHostRead, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: "The name of the host.", - }, - "internet_access": { - Type: schema.TypeString, - Computed: true, - Description: "The type of internet access provided.", - }, - "system_subnets": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Description: "The IPV4 and IPV6 subnets automatically assigned to this host.", - }, - "connectors": { - Type: schema.TypeList, - Computed: true, - Description: "The list of connectors to be associated with this host.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - Description: "The connector id.", - }, - "name": { - Type: schema.TypeString, - Computed: true, - Description: "The connector name.", - }, - "network_item_id": { - Type: schema.TypeString, - Computed: true, - Description: "The id of the host with which the connector is associated.", - }, - "network_item_type": { - Type: schema.TypeString, - Computed: true, - Description: "The network object type of the connector. This typically will be set to `HOST`.", - }, - "vpn_region_id": { - Type: schema.TypeString, - Computed: true, - Description: "The id of the region where the connector is deployed.", - }, - "ip_v4_address": { - Type: schema.TypeString, - Computed: true, - Description: "The IPV4 address of the connector.", - }, - "ip_v6_address": { - Type: schema.TypeString, - Computed: true, - Description: "The IPV6 address of the connector.", - }, - }, - }, - }, - }, - } -} - -func dataSourceHostRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - host, err := c.GetHostByName(d.Get("name").(string)) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - d.Set("name", host.Name) - d.Set("internet_access", host.InternetAccess) - d.Set("system_subnets", host.SystemSubnets) - d.Set("connectors", getConnectorsSlice(&host.Connectors)) - d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) - return diags -} diff --git a/openvpncloud/data_source_network.go b/openvpncloud/data_source_network.go deleted file mode 100644 index 22a7fdd..0000000 --- a/openvpncloud/data_source_network.go +++ /dev/null @@ -1,167 +0,0 @@ -package openvpncloud - -import ( - "context" - "strconv" - "time" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourceNetwork() *schema.Resource { - return &schema.Resource{ - Description: "Use a `openvpncloud_network` data source to read an OpenVPN Cloud network.", - ReadContext: dataSourceNetworkRead, - Schema: map[string]*schema.Schema{ - "network_id": { - Type: schema.TypeString, - Computed: true, - Description: "The network ID.", - }, - "name": { - Type: schema.TypeString, - Required: true, - Description: "The network name.", - }, - "egress": { - Type: schema.TypeBool, - Computed: true, - Description: "Boolean to indicate whether this network provides an egress or not.", - }, - "internet_access": { - Type: schema.TypeString, - Computed: true, - Description: "The type of internet access provided. Valid values are `BLOCKED`, `GLOBAL_INTERNET`, or `LOCAL`. Defaults to `LOCAL`.", - }, - "system_subnets": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Description: "The IPV4 and IPV6 subnets automatically assigned to this network.", - }, - "routes": { - Type: schema.TypeList, - Computed: true, - Description: "The routes associated with this network.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - Description: "The route id.", - }, - "type": { - Type: schema.TypeString, - Computed: true, - Description: "The type of route. Valid values are `IP_V4`, `IP_V6`, and `DOMAIN`.", - }, - "subnet": { - Type: schema.TypeString, - Computed: true, - Description: "The value of the route, either an IPV4 address, an IPV6 address, or a DNS hostname.", - }, - }, - }, - }, - "connectors": { - Type: schema.TypeList, - Computed: true, - Description: "The list of connectors associated with this network.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - Description: "The connector id.", - }, - "name": { - Type: schema.TypeString, - Computed: true, - Description: "The connector name.", - }, - "network_item_id": { - Type: schema.TypeString, - Computed: true, - Description: "The id of the network with which the connector is associated.", - }, - "network_item_type": { - Type: schema.TypeString, - Computed: true, - Description: "The network object type of the connector. This typically will be set to `NETWORK`.", - }, - "vpn_region_id": { - Type: schema.TypeString, - Computed: true, - Description: "The id of the region where the connector is deployed.", - }, - "ip_v4_address": { - Type: schema.TypeString, - Computed: true, - Description: "The IPV4 address of the connector.", - }, - "ip_v6_address": { - Type: schema.TypeString, - Computed: true, - Description: "The IPV6 address of the connector.", - }, - }, - }, - }, - }, - } -} - -func dataSourceNetworkRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - networkName := d.Get("name").(string) - network, err := c.GetNetworkByName(networkName) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - if network == nil { - return append(diags, diag.Errorf("Network with name %s was not found", networkName)...) - } - d.Set("network_id", network.Id) - d.Set("name", network.Name) - d.Set("description", network.Description) - d.Set("egress", network.Egress) - d.Set("internet_access", network.InternetAccess) - d.Set("system_subnets", network.SystemSubnets) - d.Set("routes", getRoutesSlice(&network.Routes)) - d.Set("connectors", getConnectorsSlice(&network.Connectors)) - d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) - return diags -} - -func getRoutesSlice(networkRoutes *[]client.Route) []interface{} { - routes := make([]interface{}, len(*networkRoutes)) - for i, r := range *networkRoutes { - route := make(map[string]interface{}) - route["id"] = r.Id - route["subnet"] = r.Subnet - route["type"] = r.Type - routes[i] = route - } - return routes -} - -func getConnectorsSlice(connectors *[]client.Connector) []interface{} { - conns := make([]interface{}, len(*connectors)) - for i, c := range *connectors { - connector := make(map[string]interface{}) - connector["id"] = c.Id - connector["name"] = c.Name - connector["network_item_id"] = c.NetworkItemId - connector["network_item_type"] = c.NetworkItemType - connector["vpn_region_id"] = c.VpnRegionId - connector["ip_v4_address"] = c.IPv4Address - connector["ip_v6_address"] = c.IPv6Address - conns[i] = connector - } - return conns -} diff --git a/openvpncloud/data_source_network_routes.go b/openvpncloud/data_source_network_routes.go deleted file mode 100644 index 4f970d7..0000000 --- a/openvpncloud/data_source_network_routes.go +++ /dev/null @@ -1,68 +0,0 @@ -package openvpncloud - -import ( - "context" - "strconv" - "time" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourceNetworkRoutes() *schema.Resource { - return &schema.Resource{ - Description: "Use an `openvpncloud_network_routes` data source to read all the routes associated with an OpenVPN Cloud network.", - ReadContext: dataSourceNetworkRoutesRead, - Schema: map[string]*schema.Schema{ - "network_item_id": { - Type: schema.TypeString, - Required: true, - Description: "The id of the OpenVPN Cloud network of the routes to be discovered.", - }, - "routes": { - Type: schema.TypeList, - Computed: true, - Description: "The list of routes.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "type": { - Type: schema.TypeString, - Computed: true, - Description: "The type of route. Valid values are `IP_V4`, `IP_V6`, and `DOMAIN`.", - }, - "value": { - Type: schema.TypeString, - Computed: true, - Description: "The value of the route, either an IPV4 address, an IPV6 address, or a DNS hostname.", - }, - }, - }, - }, - }, - } -} - -func dataSourceNetworkRoutesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - routes, err := c.GetRoutes(d.Get("network_item_id").(string)) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - configRoutes := make([]map[string]interface{}, len(routes)) - for i, r := range routes { - route := make(map[string]interface{}) - routeType := r.Type - route["type"] = routeType - if routeType == client.RouteTypeIPV4 || routeType == client.RouteTypeIPV6 { - route["value"] = r.Subnet - } else if routeType == client.RouteTypeDomain { - route["value"] = r.Domain - } - configRoutes[i] = route - } - d.Set("routes", configRoutes) - d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) - return diags -} diff --git a/openvpncloud/data_source_service.go b/openvpncloud/data_source_service.go deleted file mode 100644 index 290f29b..0000000 --- a/openvpncloud/data_source_service.go +++ /dev/null @@ -1,67 +0,0 @@ -package openvpncloud - -import ( - "context" - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourceService() *schema.Resource { - return &schema.Resource{ - ReadContext: dataSourceServiceRead, - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Required: true, - }, - "name": { - Type: schema.TypeString, - Computed: true, - }, - "description": { - Type: schema.TypeString, - Computed: true, - }, - "type": { - Type: schema.TypeString, - Computed: true, - }, - "routes": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "config": { - Type: schema.TypeList, - Computed: true, - Elem: resourceServiceConfig(), - }, - "network_item_type": { - Type: schema.TypeString, - Computed: true, - }, - "network_item_id": { - Type: schema.TypeString, - Computed: true, - }, - }, - } -} - -func dataSourceServiceRead(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { - c := i.(*client.Client) - service, err := c.GetService( - data.Id(), - data.Get("network_item_type").(string), - data.Get("network_item_id").(string), - ) - - if err != nil { - return diag.FromErr(err) - } - setResourceData(data, service) - return nil -} diff --git a/openvpncloud/data_source_user.go b/openvpncloud/data_source_user.go deleted file mode 100644 index bce2209..0000000 --- a/openvpncloud/data_source_user.go +++ /dev/null @@ -1,138 +0,0 @@ -package openvpncloud - -import ( - "context" - "strconv" - "time" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourceUser() *schema.Resource { - return &schema.Resource{ - Description: "Use a `openvpncloud_user` data source to read a specific OpenVPN Cloud user.", - ReadContext: dataSourceUserRead, - Schema: map[string]*schema.Schema{ - "user_id": { - Type: schema.TypeString, - Computed: true, - Description: "The ID of this resource.", - }, - "username": { - Type: schema.TypeString, - Required: true, - Description: "The username of the user.", - }, - "role": { - Type: schema.TypeString, - Required: true, - Description: "The type of user role. Valid values are `ADMIN`, `MEMBER`, or `OWNER`.", - }, - "email": { - Type: schema.TypeString, - Computed: true, - Description: "The email address of the user.", - }, - "auth_type": { - Type: schema.TypeString, - Computed: true, - Description: "The authentication type of the user.", - }, - "first_name": { - Type: schema.TypeString, - Computed: true, - Description: "The user's first name.", - }, - "last_name": { - Type: schema.TypeString, - Computed: true, - Description: "The user's last name.", - }, - "group_id": { - Type: schema.TypeString, - Computed: true, - Description: "The user's group id.", - }, - "status": { - Type: schema.TypeString, - Computed: true, - Description: "The user's status.", - }, - "devices": { - Type: schema.TypeList, - Computed: true, - Description: "The list of user devices.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - Description: "The device's id.", - }, - "name": { - Type: schema.TypeString, - Computed: true, - Description: "The device's name.", - }, - "description": { - Type: schema.TypeString, - Computed: true, - Description: "The device's description.", - }, - "ip_v4_address": { - Type: schema.TypeString, - Computed: true, - Description: "The device's IPV4 address.", - }, - "ip_v6_address": { - Type: schema.TypeString, - Computed: true, - Description: "The device's IPV6 address.", - }, - }, - }, - }, - }, - } -} - -func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - userName := d.Get("username").(string) - user, err := c.GetUser(userName, d.Get("role").(string)) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - if user == nil { - return append(diags, diag.Errorf("User with name %s was not found", userName)...) - } - d.Set("user_id", user.Id) - d.Set("username", user.Username) - d.Set("role", user.Role) - d.Set("email", user.Email) - d.Set("auth_type", user.AuthType) - d.Set("first_name", user.FirstName) - d.Set("last_name", user.LastName) - d.Set("group_id", user.GroupId) - d.Set("status", user.Status) - d.Set("devices", getUserDevicesSlice(&user.Devices)) - d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) - return diags -} - -func getUserDevicesSlice(userDevices *[]client.Device) []interface{} { - devices := make([]interface{}, len(*userDevices)) - for i, d := range *userDevices { - device := make(map[string]interface{}) - device["id"] = d.Id - device["name"] = d.Name - device["description"] = d.Description - device["ip_v4_address"] = d.IPv4Address - device["ip_v6_address"] = d.IPv6Address - devices[i] = device - } - return devices -} diff --git a/openvpncloud/data_source_user_group.go b/openvpncloud/data_source_user_group.go deleted file mode 100644 index 9bc5acb..0000000 --- a/openvpncloud/data_source_user_group.go +++ /dev/null @@ -1,77 +0,0 @@ -package openvpncloud - -import ( - "context" - "strconv" - "time" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourceUserGroup() *schema.Resource { - return &schema.Resource{ - Description: "Use an `openvpncloud_user_group` data source to read an OpenVPN Cloud user group.", - ReadContext: dataSourceUserGroupRead, - Schema: map[string]*schema.Schema{ - "user_group_id": { - Type: schema.TypeString, - Computed: true, - Description: "The user group ID.", - }, - "name": { - Type: schema.TypeString, - Required: true, - Description: "The user group name.", - }, - "vpn_region_ids": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Description: "The list of VPN region IDs this user group is associated with.", - }, - "internet_access": { - Type: schema.TypeString, - Computed: true, - Description: "The type of internet access provided. Valid values are `BLOCKED`, `GLOBAL_INTERNET`, or `LOCAL`. Defaults to `LOCAL`.", - }, - "max_device": { - Type: schema.TypeInt, - Computed: true, - Description: "The maximum number of devices per user.", - }, - "system_subnets": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Description: "The IPV4 and IPV6 addresses of the subnets associated with this user group.", - }, - }, - } -} - -func dataSourceUserGroupRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - userGroupName := d.Get("name").(string) - userGroup, err := c.GetUserGroupByName(userGroupName) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - if userGroup == nil { - return append(diags, diag.Errorf("User group with name %s was not found", userGroupName)...) - } - d.Set("user_group_id", userGroup.Id) - d.Set("name", userGroup.Name) - d.Set("vpn_region_ids", userGroup.VpnRegionIds) - d.Set("internet_access", userGroup.InternetAccess) - d.Set("max_device", userGroup.MaxDevice) - d.Set("system_subnets", userGroup.SystemSubnets) - d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) - return diags -} diff --git a/openvpncloud/data_source_vpn_region.go b/openvpncloud/data_source_vpn_region.go deleted file mode 100644 index 7785e99..0000000 --- a/openvpncloud/data_source_vpn_region.go +++ /dev/null @@ -1,65 +0,0 @@ -package openvpncloud - -import ( - "context" - "strconv" - "time" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func dataSourceVpnRegion() *schema.Resource { - return &schema.Resource{ - Description: "Use a `openvpncloud_vpn_region` data source to read an OpenVPN Cloud VPN region.", - ReadContext: dataSourceVpnRegionRead, - Schema: map[string]*schema.Schema{ - "region_id": { - Type: schema.TypeString, - Required: true, - Description: "The id of the region.", - }, - "continent": { - Type: schema.TypeString, - Computed: true, - Description: "The continent of the region.", - }, - "country": { - Type: schema.TypeString, - Computed: true, - Description: "The country of the region.", - }, - "country_iso": { - Type: schema.TypeString, - Computed: true, - Description: "The ISO code of the country of the region.", - }, - "region_name": { - Type: schema.TypeString, - Computed: true, - Description: "The name of the region.", - }, - }, - } -} - -func dataSourceVpnRegionRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - vpnRegionId := d.Get("region_id").(string) - vpnRegion, err := c.GetVpnRegion(vpnRegionId) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - if vpnRegion == nil { - return append(diags, diag.Errorf("VPN region with id %s was not found", vpnRegionId)...) - } - d.Set("region_id", vpnRegion.Id) - d.Set("continent", vpnRegion.Continent) - d.Set("country", vpnRegion.Country) - d.Set("country_iso", vpnRegion.CountryISO) - d.Set("region_name", vpnRegion.RegionName) - d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) - return diags -} diff --git a/openvpncloud/provider.go b/openvpncloud/provider.go deleted file mode 100644 index 076cd3e..0000000 --- a/openvpncloud/provider.go +++ /dev/null @@ -1,80 +0,0 @@ -package openvpncloud - -import ( - "context" - "fmt" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -const ( - clientIDEnvVar = "OPENVPN_CLOUD_CLIENT_ID" - clientSecretEnvVar = "OPENVPN_CLOUD_CLIENT_SECRET" -) - -type Token struct { - AccessToken string `json:"access_token"` -} - -func Provider() *schema.Provider { - return &schema.Provider{ - Schema: map[string]*schema.Schema{ - "client_id": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - DefaultFunc: schema.EnvDefaultFunc(clientIDEnvVar, nil), - }, - "client_secret": { - Type: schema.TypeString, - Optional: true, - Sensitive: true, - DefaultFunc: schema.EnvDefaultFunc(clientSecretEnvVar, nil), - }, - "base_url": { - Type: schema.TypeString, - Required: true, - }, - }, - ResourcesMap: map[string]*schema.Resource{ - "openvpncloud_network": resourceNetwork(), - "openvpncloud_connector": resourceConnector(), - "openvpncloud_route": resourceRoute(), - "openvpncloud_dns_record": resourceDnsRecord(), - "openvpncloud_user": resourceUser(), - "openvpncloud_host": resourceHost(), - "openvpncloud_user_group": resourceUserGroup(), - "openvpncloud_service": resourceService(), - }, - DataSourcesMap: map[string]*schema.Resource{ - "openvpncloud_network": dataSourceNetwork(), - "openvpncloud_connector": dataSourceConnector(), - "openvpncloud_user": dataSourceUser(), - "openvpncloud_user_group": dataSourceUserGroup(), - "openvpncloud_vpn_region": dataSourceVpnRegion(), - "openvpncloud_network_routes": dataSourceNetworkRoutes(), - "openvpncloud_host": dataSourceHost(), - "openvpncloud_service": dataSourceService(), - }, - ConfigureContextFunc: providerConfigure, - } -} - -func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { - clientId := d.Get("client_id").(string) - clientSecret := d.Get("client_secret").(string) - baseUrl := d.Get("base_url").(string) - openVPNClient, err := client.NewClient(baseUrl, clientId, clientSecret) - var diags diag.Diagnostics - if err != nil { - diags = append(diags, diag.Diagnostic{ - Severity: diag.Error, - Summary: "Unable to create client", - Detail: fmt.Sprintf("Error: %v", err), - }) - return nil, diags - } - return openVPNClient, nil -} diff --git a/openvpncloud/provider_test.go b/openvpncloud/provider_test.go deleted file mode 100644 index 36dbecd..0000000 --- a/openvpncloud/provider_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package openvpncloud - -import ( - "context" - "os" - "strings" - "testing" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -const alphabet = "abcdefghigklmnopqrstuvwxyz" - -var testCloudID = os.Getenv("OPENVPNCLOUD_TEST_ORGANIZATION") -var testAccProvider *schema.Provider -var testAccProviders map[string]*schema.Provider -var testAccProviderFactories map[string]func() (*schema.Provider, error) - -func init() { - testAccProvider = Provider() - testAccProviders = map[string]*schema.Provider{ - "openvpncloud": testAccProvider, - } - testAccProviderFactories = map[string]func() (*schema.Provider, error){ - "openvpncloud": func() (*schema.Provider, error) { - return testAccProvider, nil - }, - } -} - -func TestProvider(t *testing.T) { - err := Provider().InternalValidate() - require.NoError(t, err) - - // must have the required error when the credentials are not set - t.Setenv(clientIDEnvVar, "") - t.Setenv(clientSecretEnvVar, "") - rc := terraform.ResourceConfig{} - diags := Provider().Configure(context.Background(), &rc) - assert.True(t, diags.HasError()) - - for _, d := range diags { - assert.Truef(t, strings.Contains(d.Detail, client.ErrCredentialsRequired.Error()), - "error message does not contain the expected error: %s", d.Detail) - } -} - -func testAccPreCheck(t *testing.T) { - if v := os.Getenv(clientIDEnvVar); v == "" { - t.Fatalf("%s must be set for acceptance tests", clientIDEnvVar) - } - if v := os.Getenv(clientSecretEnvVar); v == "" { - t.Fatalf("%s must be set for acceptance tests", clientSecretEnvVar) - } -} diff --git a/openvpncloud/resource_connector.go b/openvpncloud/resource_connector.go deleted file mode 100644 index 6cdf912..0000000 --- a/openvpncloud/resource_connector.go +++ /dev/null @@ -1,137 +0,0 @@ -package openvpncloud - -import ( - "context" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func resourceConnector() *schema.Resource { - return &schema.Resource{ - Description: "Use `openvpncloud_connector` to create an OpenVPN Cloud connector.\n\n~> NOTE: This only creates the OpenVPN Cloud connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", - CreateContext: resourceConnectorCreate, - ReadContext: resourceConnectorRead, - DeleteContext: resourceConnectorDelete, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The connector display name.", - }, - "vpn_region_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The id of the region where the connector will be deployed.", - }, - "network_item_type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{client.NetworkItemTypeHost, client.NetworkItemTypeNetwork}, false), - Description: "The type of network item of the connector. Supported values are `HOST` and `NETWORK`.", - }, - "network_item_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The id of the network with which this connector is associated.", - }, - "ip_v4_address": { - Type: schema.TypeString, - Computed: true, - Description: "The IPV4 address of the connector.", - }, - "ip_v6_address": { - Type: schema.TypeString, - Computed: true, - Description: "The IPV6 address of the connector.", - }, - }, - } -} - -func resourceConnectorCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - name := d.Get("name").(string) - networkItemId := d.Get("network_item_id").(string) - networkItemType := d.Get("network_item_type").(string) - vpnRegionId := d.Get("vpn_region_id").(string) - connector := client.Connector{ - Name: name, - NetworkItemId: networkItemId, - NetworkItemType: networkItemType, - VpnRegionId: vpnRegionId, - } - conn, err := c.AddConnector(connector, networkItemId) - if err != nil { - return diag.FromErr(err) - } - d.SetId(conn.Id) - return append(diags, diag.Diagnostic{ - Severity: diag.Warning, - Summary: "Connector needs to be set up manually", - Detail: "Terraform only creates the OpenVPN Cloud connector object, but additional manual steps are required to associate a host in your infrastructure with this connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", - }) -} - -func resourceConnectorRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - connector, err := c.GetConnectorById(d.Id()) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - if connector == nil { - d.SetId("") - } else { - d.SetId(connector.Id) - d.Set("name", connector.Name) - d.Set("vpn_region_id", connector.VpnRegionId) - d.Set("network_item_type", connector.NetworkItemType) - d.Set("network_item_id", connector.NetworkItemId) - d.Set("ip_v4_address", connector.IPv4Address) - d.Set("ip_v6_address", connector.IPv6Address) - } - return diags -} - -func resourceConnectorDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - err := c.DeleteConnector(d.Id(), d.Get("network_item_id").(string), d.Get("network_item_type").(string)) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - return diags -} - -func getConnectorSlice(connectors []client.Connector, networkItemId string, connectorName string) []interface{} { - if len(connectors) == 0 { - return nil - } - connectorsList := make([]interface{}, 1) - for _, c := range connectors { - if c.NetworkItemId == networkItemId && c.Name == connectorName { - connector := make(map[string]interface{}) - connector["id"] = c.Id - connector["name"] = c.Name - connector["network_item_id"] = c.NetworkItemId - connector["network_item_type"] = c.NetworkItemType - connector["vpn_region_id"] = c.VpnRegionId - connector["ip_v4_address"] = c.IPv4Address - connector["ip_v6_address"] = c.IPv6Address - connectorsList[0] = connector - break - } - } - return connectorsList -} diff --git a/openvpncloud/resource_connector_test.go b/openvpncloud/resource_connector_test.go deleted file mode 100644 index dbce088..0000000 --- a/openvpncloud/resource_connector_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package openvpncloud - -import ( - "fmt" - "testing" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" -) - -func TestAccOpenvpncloudConnector_basic(t *testing.T) { - rName := acctest.RandomWithPrefix("test-connector") - resourceName := "openvpncloud_connector.test" - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProviderFactories: testAccProviderFactories, - CheckDestroy: testAccCheckOpenvpncloudConnectorDestroy, - Steps: []resource.TestStep{ - { - Config: testAccOpenvpncloudConnectorConfigBasic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckOpenvpncloudConnectorExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "name", rName), - resource.TestCheckResourceAttrSet(resourceName, "vpn_region_id"), - resource.TestCheckResourceAttrSet(resourceName, "network_item_type"), - resource.TestCheckResourceAttrSet(resourceName, "network_item_id"), - resource.TestCheckResourceAttrSet(resourceName, "ip_v4_address"), - resource.TestCheckResourceAttrSet(resourceName, "ip_v6_address"), - ), - }, - }, - }) -} - -func testAccCheckOpenvpncloudConnectorExists(n string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - if rs.Primary.ID == "" { - return fmt.Errorf("No connector ID is set") - } - return nil - } -} - -func testAccCheckOpenvpncloudConnectorDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*client.Client) - - for _, rs := range s.RootModule().Resources { - if rs.Type != "openvpncloud_connector" { - continue - } - - connectorId := rs.Primary.ID - connector, err := client.GetConnectorById(connectorId) - - if err != nil { - return err - } - - if connector != nil { - return fmt.Errorf("Connector with ID '%s' still exists", connectorId) - } - } - - return nil -} - -func testAccOpenvpncloudConnectorConfigBasic(rName string) string { - return fmt.Sprintf(` -provider "openvpncloud" { - base_url = "https://%[1]s.api.openvpn.com" -} - -resource "openvpncloud_connector" "test" { - name = "%s" - vpn_region_id = "us-west-1" - network_item_type = "HOST" - network_item_id = "example_network_item_id" -} -`, testCloudID, rName) -} diff --git a/openvpncloud/resource_dns_record.go b/openvpncloud/resource_dns_record.go deleted file mode 100644 index 5951759..0000000 --- a/openvpncloud/resource_dns_record.go +++ /dev/null @@ -1,146 +0,0 @@ -package openvpncloud - -import ( - "context" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func resourceDnsRecord() *schema.Resource { - return &schema.Resource{ - Description: "Use `openvpncloud_dns_record` to create a DNS record on your VPN.", - CreateContext: resourceDnsRecordCreate, - ReadContext: resourceDnsRecordRead, - DeleteContext: resourceDnsRecordDelete, - UpdateContext: resourceDnsRecordUpdate, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - Schema: map[string]*schema.Schema{ - "domain": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The DNS record name.", - }, - "description": { - Type: schema.TypeString, - Optional: true, - Default: "Managed by Terraform", - ValidateFunc: validation.StringLenBetween(1, 120), - Description: "The description for the UI. Defaults to `Managed by Terraform`.", - }, - "ip_v4_addresses": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.IsIPv4Address, - }, - Description: "The list of IPV4 addresses to which this record will resolve.", - }, - "ip_v6_addresses": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.IsIPv6Address, - }, - Description: "The list of IPV6 addresses to which this record will resolve.", - }, - }, - } -} - -func resourceDnsRecordCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - domain := d.Get("domain").(string) - description := d.Get("description").(string) - ipV4Addresses := d.Get("ip_v4_addresses").([]interface{}) - ipV4AddressesSlice := make([]string, 0) - for _, a := range ipV4Addresses { - ipV4AddressesSlice = append(ipV4AddressesSlice, a.(string)) - } - ipV6Addresses := d.Get("ip_v6_addresses").([]interface{}) - ipV6AddressesSlice := make([]string, 0) - for _, a := range ipV6Addresses { - ipV6AddressesSlice = append(ipV6AddressesSlice, a.(string)) - } - dr := client.DnsRecord{ - Domain: domain, - Description: description, - IPV4Addresses: ipV4AddressesSlice, - IPV6Addresses: ipV6AddressesSlice, - } - dnsRecord, err := c.CreateDnsRecord(dr) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - d.SetId(dnsRecord.Id) - return diags -} - -func resourceDnsRecordRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - recordId := d.Id() - r, err := c.GetDnsRecord(recordId) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - if r == nil { - d.SetId("") - } else { - d.Set("domain", r.Domain) - d.Set("description", r.Description) - d.Set("ip_v4_addresses", r.IPV4Addresses) - d.Set("ip_v6_addresses", r.IPV6Addresses) - } - return diags -} - -func resourceDnsRecordUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - _, domain := d.GetChange("domain") - _, description := d.GetChange("description") - _, ipV4Addresses := d.GetChange("ip_v4_addresses") - ipV4AddressesSlice := getAddressesSlice(ipV4Addresses.([]interface{})) - _, ipV6Addresses := d.GetChange("ip_v6_addresses") - ipV6AddressesSlice := getAddressesSlice(ipV6Addresses.([]interface{})) - dr := client.DnsRecord{ - Id: d.Id(), - Domain: domain.(string), - Description: description.(string), - IPV4Addresses: ipV4AddressesSlice, - IPV6Addresses: ipV6AddressesSlice, - } - err := c.UpdateDnsRecord(dr) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - return diags -} - -func resourceDnsRecordDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - routeId := d.Id() - err := c.DeleteDnsRecord(routeId) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - return diags -} - -func getAddressesSlice(addresses []interface{}) []string { - addressesSlice := make([]string, 0) - for _, a := range addresses { - addressesSlice = append(addressesSlice, a.(string)) - } - return addressesSlice -} diff --git a/openvpncloud/resource_dns_record_test.go b/openvpncloud/resource_dns_record_test.go deleted file mode 100644 index 7853700..0000000 --- a/openvpncloud/resource_dns_record_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package openvpncloud - -import ( - "fmt" - "testing" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" -) - -func TestAccOpenvpncloudDnsRecord_basic(t *testing.T) { - resourceName := "openvpncloud_dns_record.test" - domainName := "test.openvpncloud.com" - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProviderFactories: testAccProviderFactories, - CheckDestroy: testAccCheckOpenvpncloudDnsRecordDestroy, - Steps: []resource.TestStep{ - { - Config: testAccOpenvpncloudDnsRecordConfig(domainName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr(resourceName, "domain", domainName), - resource.TestCheckResourceAttr(resourceName, "description", "test description"), - resource.TestCheckResourceAttr(resourceName, "ip_v4_addresses.0", "192.168.1.1"), - resource.TestCheckResourceAttr(resourceName, "ip_v4_addresses.1", "192.168.1.2"), - resource.TestCheckResourceAttr(resourceName, "ip_v6_addresses.0", "2001:db8:85a3:0:0:8a2e:370:7334"), - resource.TestCheckResourceAttr(resourceName, "ip_v6_addresses.1", "2001:db8:85a3:0:0:8a2e:370:7335"), - ), - }, - }, - }) -} - -func testAccCheckOpenvpncloudDnsRecordDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*client.Client) - - for _, rs := range s.RootModule().Resources { - if rs.Type != "openvpncloud_dns_record" { - continue - } - - recordId := rs.Primary.ID - r, err := client.GetDnsRecord(recordId) - - if err != nil { - return err - } - - if r != nil { - return fmt.Errorf("DNS record with ID '%s' still exists", recordId) - } - } - - return nil -} - -func testAccOpenvpncloudDnsRecordConfig(domainName string) string { - return fmt.Sprintf(` -provider "openvpncloud" { - base_url = "https://%[1]s.api.openvpn.com" -} - -resource "openvpncloud_dns_record" "test" { - domain = "%[2]s" - description = "test description" - ip_v4_addresses = ["192.168.1.1", "192.168.1.2"] - ip_v6_addresses = ["2001:db8:85a3:0:0:8a2e:370:7334", "2001:db8:85a3:0:0:8a2e:370:7335"] -} -`, testCloudID, domainName) -} diff --git a/openvpncloud/resource_host.go b/openvpncloud/resource_host.go deleted file mode 100644 index 778bd8b..0000000 --- a/openvpncloud/resource_host.go +++ /dev/null @@ -1,268 +0,0 @@ -package openvpncloud - -import ( - "context" - "hash/fnv" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func resourceHost() *schema.Resource { - return &schema.Resource{ - Description: "Use `openvpncloud_host` to create an OpenVPN Cloud host.", - CreateContext: resourceHostCreate, - ReadContext: resourceHostRead, - UpdateContext: resourceHostUpdate, - DeleteContext: resourceHostDelete, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: "The display name of the host.", - }, - "description": { - Type: schema.TypeString, - Optional: true, - Default: "Managed by Terraform", - ValidateFunc: validation.StringLenBetween(1, 120), - Description: "The description for the UI. Defaults to `Managed by Terraform`.", - }, - "internet_access": { - Type: schema.TypeString, - Optional: true, - Default: client.InternetAccessLocal, - ValidateFunc: validation.StringInSlice([]string{client.InternetAccessBlocked, client.InternetAccessGlobalInternet, client.InternetAccessLocal}, false), - Description: "The type of internet access provided. Valid values are `BLOCKED`, `GLOBAL_INTERNET`, or `LOCAL`. Defaults to `LOCAL`.", - }, - "system_subnets": { - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Description: "The IPV4 and IPV6 subnets automatically assigned to this host.", - }, - "connector": { - Type: schema.TypeSet, - Required: true, - Set: func(i interface{}) int { - n := i.(map[string]interface{})["name"] - h := fnv.New32a() - h.Write([]byte(n.(string))) - return int(h.Sum32()) - }, - Description: "The set of connectors to be associated with this host. Can be defined more than once.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - }, - "name": { - Type: schema.TypeString, - Required: true, - Description: "Name of the connector associated with this host.", - }, - "vpn_region_id": { - Type: schema.TypeString, - Required: true, - Description: "The id of the region where the connector will be deployed.", - }, - "network_item_type": { - Type: schema.TypeString, - Computed: true, - Description: "The network object type. This typically will be set to `HOST`.", - }, - "network_item_id": { - Type: schema.TypeString, - Computed: true, - Description: "The host id.", - }, - "ip_v4_address": { - Type: schema.TypeString, - Computed: true, - Description: "The IPV4 address of the connector.", - }, - "ip_v6_address": { - Type: schema.TypeString, - Computed: true, - Description: "The IPV6 address of the connector.", - }, - "profile": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, - }, - } -} - -func resourceHostCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - var connectors []client.Connector - configConnectors := d.Get("connector").(*schema.Set) - for _, c := range configConnectors.List() { - connectors = append(connectors, client.Connector{ - Name: c.(map[string]interface{})["name"].(string), - VpnRegionId: c.(map[string]interface{})["vpn_region_id"].(string), - }) - } - h := client.Host{ - Name: d.Get("name").(string), - Description: d.Get("description").(string), - InternetAccess: d.Get("internet_access").(string), - Connectors: connectors, - } - host, err := c.CreateHost(h) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - d.SetId(host.Id) - diagnostics := setConnectorsList(d, c, host.Connectors) - if diagnostics != nil { - return diagnostics - } - - return append(diags, diag.Diagnostic{ - Severity: diag.Warning, - Summary: "The connector for this host needs to be set up manually", - Detail: "Terraform only creates the OpenVPN Cloud connector object for this host, but additional manual steps are required to associate a host in your infrastructure with this connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", - }) -} - -func resourceHostRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - host, err := c.GetHostById(d.Id()) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - if host == nil { - d.SetId("") - return diags - } - d.Set("name", host.Name) - d.Set("description", host.Description) - d.Set("internet_access", host.InternetAccess) - d.Set("system_subnets", host.SystemSubnets) - - diagnostics := setConnectorsList(d, c, host.Connectors) - if diagnostics != nil { - return diagnostics - } - return diags -} - -func resourceHostUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - if d.HasChange("connector") { - old, new := d.GetChange("connector") - oldSet := old.(*schema.Set) - newSet := new.(*schema.Set) - if oldSet.Len() == 0 && newSet.Len() > 0 { - // This happens when importing the resource - newConnector := client.Connector{ - Name: newSet.List()[0].(map[string]interface{})["name"].(string), - VpnRegionId: newSet.List()[0].(map[string]interface{})["vpn_region_id"].(string), - NetworkItemType: client.NetworkItemTypeHost, - } - _, err := c.AddConnector(newConnector, d.Id()) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - } else { - for _, o := range oldSet.List() { - if !newSet.Contains(o) { - err := c.DeleteConnector(o.(map[string]interface{})["id"].(string), d.Id(), client.NetworkItemTypeHost) - if err != nil { - diags = append(diags, diag.FromErr(err)...) - } - } - } - for _, n := range newSet.List() { - if !oldSet.Contains(n) { - newConnector := client.Connector{ - Name: n.(map[string]interface{})["name"].(string), - VpnRegionId: n.(map[string]interface{})["vpn_region_id"].(string), - NetworkItemType: client.NetworkItemTypeHost, - } - _, err := c.AddConnector(newConnector, d.Id()) - if err != nil { - diags = append(diags, diag.FromErr(err)...) - } - } - } - } - } - if d.HasChanges("name", "description", "internet_access") { - _, newName := d.GetChange("name") - _, newDescription := d.GetChange("description") - _, newAccess := d.GetChange("internet_access") - err := c.UpdateHost(client.Host{ - Id: d.Id(), - Name: newName.(string), - Description: newDescription.(string), - InternetAccess: newAccess.(string), - }) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - } - return append(diags, resourceHostRead(ctx, d, m)...) -} - -func resourceHostDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - hostId := d.Id() - err := c.DeleteHost(hostId) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - return diags -} - -func setConnectorsList(data *schema.ResourceData, c *client.Client, connectors []client.Connector) diag.Diagnostics { - connectorsList := make([]interface{}, len(connectors)) - for i, connector := range connectors { - connectorsData, err := getConnectorsListItem(c, connector) - if err != nil { - return diag.FromErr(err) - } - connectorsList[i] = connectorsData - } - err := data.Set("connector", connectorsList) - if err != nil { - return diag.FromErr(err) - } - return nil -} - -func getConnectorsListItem(c *client.Client, connector client.Connector) (map[string]interface{}, error) { - connectorsData := map[string]interface{}{ - "id": connector.Id, - "name": connector.Name, - "vpn_region_id": connector.VpnRegionId, - "ip_v4_address": connector.IPv4Address, - "ip_v6_address": connector.IPv6Address, - "network_item_id": connector.NetworkItemId, - "network_item_type": connector.NetworkItemType, - } - - connectorProfile, err := c.GetConnectorProfile(connector.Id) - if err != nil { - return nil, err - } - connectorsData["profile"] = connectorProfile - return connectorsData, nil -} diff --git a/openvpncloud/resource_network.go b/openvpncloud/resource_network.go deleted file mode 100644 index 1b54905..0000000 --- a/openvpncloud/resource_network.go +++ /dev/null @@ -1,354 +0,0 @@ -package openvpncloud - -import ( - "context" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func resourceNetwork() *schema.Resource { - return &schema.Resource{ - Description: "Use `openvpncloud_network` to create an OpenVPN Cloud Network.", - CreateContext: resourceNetworkCreate, - ReadContext: resourceNetworkRead, - UpdateContext: resourceNetworkUpdate, - DeleteContext: resourceNetworkDelete, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - Description: "The display name of the network.", - }, - "description": { - Type: schema.TypeString, - Optional: true, - Default: "Managed by Terraform", - ValidateFunc: validation.StringLenBetween(1, 120), - Description: "The display description for this resource. Defaults to `Managed by Terraform`.", - }, - "egress": { - Type: schema.TypeBool, - Optional: true, - Default: true, - Description: "Boolean to control whether this network provides an egress or not.", - }, - "internet_access": { - Type: schema.TypeString, - Optional: true, - Default: client.InternetAccessLocal, - ValidateFunc: validation.StringInSlice([]string{client.InternetAccessBlocked, client.InternetAccessGlobalInternet, client.InternetAccessLocal}, false), - Description: "The type of internet access provided. Valid values are `BLOCKED`, `GLOBAL_INTERNET`, or `LOCAL`. Defaults to `LOCAL`.", - }, - "system_subnets": { - Type: schema.TypeSet, - Computed: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - Description: "The IPV4 and IPV6 subnets automatically assigned to this network.", - }, - "default_route": { - Type: schema.TypeList, - Required: true, - MaxItems: 1, - Description: "The default route of this network.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "type": { - Type: schema.TypeString, - Optional: true, - Default: client.RouteTypeIPV4, - ValidateFunc: validation.StringInSlice([]string{client.RouteTypeIPV4, client.RouteTypeIPV6, client.RouteTypeDomain}, false), - Description: "The type of route. Valid values are `IP_V4`, `IP_V6`, and `DOMAIN`.", - }, - "description": { - Type: schema.TypeString, - Optional: true, - Default: "Managed by Terraform.", - Description: "The default route description.", - }, - "value": { - Type: schema.TypeString, - Required: true, - Description: "The target value of the default route.", - }, - "id": { - Type: schema.TypeString, - Computed: true, - Description: "The ID of this resource.", - }, - }, - }, - }, - "default_connector": { - Type: schema.TypeList, - Required: true, - MaxItems: 1, - Description: "The default connector of this network.", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - Description: "The ID of this resource.", - }, - "name": { - Type: schema.TypeString, - Required: true, - Description: "Name of the connector automatically created and attached to this network.", - }, - "vpn_region_id": { - Type: schema.TypeString, - Required: true, - Description: "The id of the region where the default connector will be deployed.", - }, - "network_item_type": { - Type: schema.TypeString, - Computed: true, - Description: "The network object type. This typically will be set to `NETWORK`.", - }, - "network_item_id": { - Type: schema.TypeString, - Computed: true, - Description: "The parent network id.", - }, - "ip_v4_address": { - Type: schema.TypeString, - Computed: true, - Description: "The IPV4 address of the default connector.", - }, - "ip_v6_address": { - Type: schema.TypeString, - Computed: true, - Description: "The IPV6 address of the default connector.", - }, - }, - }, - }, - }, - } -} - -func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - configConnector := d.Get("default_connector").([]interface{})[0].(map[string]interface{}) - connectors := []client.Connector{ - { - Name: configConnector["name"].(string), - VpnRegionId: configConnector["vpn_region_id"].(string), - }, - } - n := client.Network{ - Name: d.Get("name").(string), - Description: d.Get("description").(string), - Egress: d.Get("egress").(bool), - InternetAccess: d.Get("internet_access").(string), - Connectors: connectors, - } - network, err := c.CreateNetwork(n) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - d.SetId(network.Id) - configRoute := d.Get("default_route").([]interface{})[0].(map[string]interface{}) - defaultRoute, err := c.CreateRoute(network.Id, client.Route{ - Type: configRoute["type"].(string), - Description: configRoute["description"].(string), - Value: configRoute["value"].(string), - }) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - defaultRouteWithIdSlice := make([]map[string]interface{}, 1) - defaultRouteWithIdSlice[0] = map[string]interface{}{ - "id": defaultRoute.Id, - "description": defaultRoute.Description, - "type": defaultRoute.Type, - "value": defaultRoute.Value, - } - d.Set("default_route", defaultRouteWithIdSlice) - return append(diags, diag.Diagnostic{ - Severity: diag.Warning, - Summary: "The default connector for this network needs to be set up manually", - Detail: "Terraform only creates the OpenVPN Cloud default connector object for this network, but additional manual steps are required to associate a host in your infrastructure with this connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", - }) -} - -func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - network, err := c.GetNetworkById(d.Id()) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - if network == nil { - d.SetId("") - return diags - } - d.Set("name", network.Name) - d.Set("description", network.Description) - d.Set("egress", network.Egress) - d.Set("internet_access", network.InternetAccess) - d.Set("system_subnets", network.SystemSubnets) - if len(d.Get("default_connector").([]interface{})) > 0 { - configConnector := d.Get("default_connector").([]interface{})[0].(map[string]interface{}) - connectorName := configConnector["name"].(string) - networkConnectors, err := c.GetConnectorsForNetwork(network.Id) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - err = d.Set("default_connector", getConnectorSlice(networkConnectors, network.Id, connectorName)) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - } - if len(d.Get("default_route").([]interface{})) > 0 { - configRoute := d.Get("default_route").([]interface{})[0].(map[string]interface{}) - route, err := c.GetNetworkRoute(d.Id(), configRoute["id"].(string)) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - if route == nil { - d.Set("default_route", []map[string]interface{}{}) - } else { - defaultRoute := []map[string]interface{}{ - { - "id": configRoute["id"].(string), - "type": route.Type, - "description": route.Description, - }, - } - if route.Type == client.RouteTypeIPV4 || route.Type == client.RouteTypeIPV6 { - defaultRoute[0]["value"] = route.Subnet - } else if route.Type == client.RouteTypeDomain { - defaultRoute[0]["value"] = route.Domain - } - d.Set("default_route", defaultRoute) - } - } - return diags -} - -func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - if d.HasChange("default_connector") { - old, new := d.GetChange("default_connector") - oldSlice := old.([]interface{}) - newSlice := new.([]interface{}) - if len(oldSlice) == 0 && len(newSlice) == 1 { - // This happens when importing the resource - newConnector := client.Connector{ - Name: newSlice[0].(map[string]interface{})["name"].(string), - VpnRegionId: newSlice[0].(map[string]interface{})["vpn_region_id"].(string), - NetworkItemType: client.NetworkItemTypeNetwork, - } - _, err := c.AddConnector(newConnector, d.Id()) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - } else { - oldMap := oldSlice[0].(map[string]interface{}) - newMap := newSlice[0].(map[string]interface{}) - if oldMap["name"].(string) != newMap["name"].(string) || oldMap["vpn_region_id"].(string) != newMap["vpn_region_id"].(string) { - newConnector := client.Connector{ - Name: newMap["name"].(string), - VpnRegionId: newMap["vpn_region_id"].(string), - NetworkItemType: client.NetworkItemTypeNetwork, - } - _, err := c.AddConnector(newConnector, d.Id()) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - if len(oldMap["id"].(string)) > 0 { - // This can sometimes happen when importing the resource - err = c.DeleteConnector(oldMap["id"].(string), d.Id(), oldMap["network_item_type"].(string)) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - } - } - } - } - if d.HasChange("default_route") { - old, new := d.GetChange("default_route") - oldSlice := old.([]interface{}) - newSlice := new.([]interface{}) - if len(oldSlice) == 0 && len(newSlice) == 1 { - // This happens when importing the resource - newMap := newSlice[0].(map[string]interface{}) - routeType := newMap["type"] - routeDesc := newMap["description"] - routeValue := newMap["value"] - route := client.Route{ - Type: routeType.(string), - Description: routeDesc.(string), - Value: routeValue.(string), - } - defaultRoute, err := c.CreateRoute(d.Id(), route) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - defaultRouteWithIdSlice := make([]map[string]interface{}, 1) - defaultRouteWithIdSlice[0] = map[string]interface{}{ - "id": defaultRoute.Id, - "description": defaultRoute.Description, - } - err = d.Set("default_route", defaultRouteWithIdSlice) - if err != nil { - diags = append(diags, diag.FromErr(err)...) - } - } else { - newMap := newSlice[0].(map[string]interface{}) - routeId := newMap["id"] - routeType := newMap["type"] - routeDesc := newMap["description"] - routeValue := newMap["value"] - route := client.Route{ - Id: routeId.(string), - Type: routeType.(string), - Description: routeDesc.(string), - Value: routeValue.(string), - } - err := c.UpdateRoute(d.Id(), route) - if err != nil { - diags = append(diags, diag.FromErr(err)...) - } - } - } - if d.HasChanges("name", "description", "internet_access", "egress") { - _, newName := d.GetChange("name") - _, newDescription := d.GetChange("description") - _, newEgress := d.GetChange("egress") - _, newAccess := d.GetChange("internet_access") - err := c.UpdateNetwork(client.Network{ - Id: d.Id(), - Name: newName.(string), - Description: newDescription.(string), - Egress: newEgress.(bool), - InternetAccess: newAccess.(string), - }) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - } - return append(diags, resourceNetworkRead(ctx, d, m)...) -} - -func resourceNetworkDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - networkId := d.Id() - err := c.DeleteNetwork(networkId) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - return diags -} diff --git a/openvpncloud/resource_route.go b/openvpncloud/resource_route.go deleted file mode 100644 index a48f142..0000000 --- a/openvpncloud/resource_route.go +++ /dev/null @@ -1,132 +0,0 @@ -package openvpncloud - -import ( - "context" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func resourceRoute() *schema.Resource { - return &schema.Resource{ - Description: "Use `openvpncloud_route` to create a route on an OpenVPN Cloud network.", - CreateContext: resourceRouteCreate, - UpdateContext: resourceRouteUpdate, - ReadContext: resourceRouteRead, - DeleteContext: resourceRouteDelete, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - Schema: map[string]*schema.Schema{ - "type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{client.RouteTypeIPV4, client.RouteTypeIPV6, client.RouteTypeDomain}, false), - Description: "The type of route. Valid values are `IP_V4`, `IP_V6`, and `DOMAIN`.", - }, - "value": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The target value of the default route.", - }, - "network_item_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Description: "The id of the network on which to create the route.", - }, - "description": { - Type: schema.TypeString, - Optional: true, - Default: "Managed by Terraform", - }, - }, - } -} - -func resourceRouteCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - networkItemId := d.Get("network_item_id").(string) - routeType := d.Get("type").(string) - routeValue := d.Get("value").(string) - descriptionValue := d.Get("description").(string) - r := client.Route{ - Type: routeType, - Value: routeValue, - Description: descriptionValue, - } - route, err := c.CreateRoute(networkItemId, r) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - d.SetId(route.Id) - if routeType == client.RouteTypeIPV4 || routeType == client.RouteTypeIPV6 { - d.Set("value", route.Subnet) - } else if routeType == client.RouteTypeDomain { - d.Set("value", route.Domain) - } - return diags -} - -func resourceRouteRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - routeId := d.Id() - r, err := c.GetRouteById(routeId) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - if r == nil { - d.SetId("") - } else { - d.Set("type", r.Type) - if r.Type == client.RouteTypeIPV4 || r.Type == client.RouteTypeIPV6 { - d.Set("value", r.Subnet) - } else if r.Type == client.RouteTypeDomain { - d.Set("resourceRouteRead", r.Domain) - } - d.Set("description", r.Description) - d.Set("network_item_id", r.NetworkItemId) - } - return diags -} - -func resourceRouteUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - if !d.HasChanges("description", "value") { - return diags - } - - networkItemId := d.Get("network_item_id").(string) - _, description := d.GetChange("description") - _, value := d.GetChange("value") - r := client.Route{ - Id: d.Id(), - Description: description.(string), - Value: value.(string), - } - - err := c.UpdateRoute(networkItemId, r) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - return diags -} - -func resourceRouteDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - routeId := d.Id() - networkItemId := d.Get("network_item_id").(string) - err := c.DeleteRoute(networkItemId, routeId) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - return diags -} diff --git a/openvpncloud/resource_route_test.go b/openvpncloud/resource_route_test.go deleted file mode 100644 index cc759e9..0000000 --- a/openvpncloud/resource_route_test.go +++ /dev/null @@ -1,132 +0,0 @@ -package openvpncloud - -import ( - "errors" - "fmt" - "testing" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - "github.com/stretchr/testify/require" -) - -func TestAccOpenvpncloudRoute_basic(t *testing.T) { - rn := "openvpncloud_route.test" - ip, err := acctest.RandIpAddress("10.0.0.0/8") - require.NoError(t, err) - route := client.Route{ - Description: "test" + acctest.RandString(10), - Type: client.RouteTypeIPV4, - Value: ip + "/32", - } - routeChanged := route - routeChanged.Description = acctest.RandStringFromCharSet(10, alphabet) - networkRandString := "test" + acctest.RandString(10) - var routeId string - - check := func(r client.Route) resource.TestCheckFunc { - return resource.ComposeTestCheckFunc( - testAccCheckOpenvpncloudRouteExists(rn, &routeId), - resource.TestCheckResourceAttr(rn, "description", r.Description), - resource.TestCheckResourceAttr(rn, "type", r.Type), - resource.TestCheckResourceAttr(rn, "value", r.Value), - ) - } - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProviderFactories: testAccProviderFactories, - CheckDestroy: testAccCheckOpenvpncloudRouteDestroy, - Steps: []resource.TestStep{ - { - Config: testAccOpenvpncloudRouteConfig(route, networkRandString), - Check: check(route), - }, - { - Config: testAccOpenvpncloudRouteConfig(routeChanged, networkRandString), - Check: check(routeChanged), - }, - { - ResourceName: rn, - ImportState: true, - ImportStateIdFunc: testAccOpenvpncloudRouteImportStateIdFunc(rn), - ImportStateVerify: true, - }, - }, - }) -} - -func testAccCheckOpenvpncloudRouteDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*client.Client) - for _, rs := range s.RootModule().Resources { - if rs.Type != "openvpncloud_route" { - continue - } - routeId := rs.Primary.ID - r, err := client.GetRouteById(routeId) - if err == nil { - return err - } - if r != nil { - return errors.New("route still exists") - } - } - return nil -} - -func testAccCheckOpenvpncloudRouteExists(n string, routeID *string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("not found: %s", n) - } - - if rs.Primary.ID == "" { - return errors.New("no ID is set") - } - - client := testAccProvider.Meta().(*client.Client) - _, err := client.GetRouteById(rs.Primary.ID) - if err != nil { - return err - } - return nil - } -} - -func testAccOpenvpncloudRouteImportStateIdFunc(n string) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[n] - if !ok { - return "", fmt.Errorf("not found: %s", n) - } - return rs.Primary.ID, nil - } -} - -func testAccOpenvpncloudRouteConfig(r client.Route, networkRandStr string) string { - return fmt.Sprintf(` -provider "openvpncloud" { - base_url = "https://%[1]s.api.openvpn.com" -} -resource "openvpncloud_network" "test" { - name = "%[5]s" - default_connector { - name = "%[5]s" - vpn_region_id = "fi-hel" - } - default_route { - value = "10.1.2.0/24" - type = "IP_V4" - } -} -resource "openvpncloud_route" "test" { - network_item_id = openvpncloud_network.test.id - description = "%[2]s" - value = "%[3]s" - type = "%[4]s" -} -`, testCloudID, r.Description, r.Value, r.Type, networkRandStr) -} diff --git a/openvpncloud/resource_service.go b/openvpncloud/resource_service.go deleted file mode 100644 index 43936f5..0000000 --- a/openvpncloud/resource_service.go +++ /dev/null @@ -1,286 +0,0 @@ -package openvpncloud - -import ( - "context" - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/go-cty/cty" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -var ( - validValues = []string{"ANY", "BGP", "CUSTOM", "DHCP", "DNS", "FTP", "HTTP", "HTTPS", "IMAP", "IMAPS", "NTP", "POP3", "POP3S", "SMTP", "SMTPS", "SNMP", "SSH", "TELNET", "TFTP"} -) - -func resourceService() *schema.Resource { - return &schema.Resource{ - CreateContext: resourceServiceCreate, - ReadContext: resourceServiceRead, - DeleteContext: resourceServiceDelete, - UpdateContext: resourceServiceUpdate, - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - }, - "name": { - Type: schema.TypeString, - Required: true, - }, - "description": { - Type: schema.TypeString, - Default: "Created by Terraform OpenVPN Cloud Provider", - ValidateFunc: validation.StringLenBetween(1, 255), - Optional: true, - }, - "type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{"IP_SOURCE", "SERVICE_DESTINATION"}, false), - }, - "routes": { - Type: schema.TypeList, - Required: true, - MinItems: 1, - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "config": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - Elem: resourceServiceConfig(), - }, - "network_item_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{"NETWORK", "HOST"}, false), - }, - "network_item_id": { - Type: schema.TypeString, - Required: true, - }, - }, - } -} - -func resourceServiceUpdate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { - c := i.(*client.Client) - networkItemId := data.Get("network_item_id").(string) - networkItemType := data.Get("network_item_type").(string) - - s, err := c.UpdateService(data.Id(), networkItemType, networkItemId, resourceDataToService(data)) - if err != nil { - return diag.FromErr(err) - } - - setResourceData(data, s) - return nil -} - -func resourceServiceConfig() *schema.Resource { - return &schema.Resource{ - Schema: map[string]*schema.Schema{ - "custom_service_types": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "icmp_type": { - Type: schema.TypeList, - Required: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "lower_value": { - Type: schema.TypeInt, - Required: true, - }, - "upper_value": { - Type: schema.TypeInt, - Required: true, - }, - }, - }, - }, - }, - }, - }, - "service_types": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateDiagFunc: func(i interface{}, path cty.Path) diag.Diagnostics { - - val := i.(string) - for _, validValue := range validValues { - if val == validValue { - return nil - } - } - return diag.Errorf("service type must be one of %s", validValues) - }, - }, - }, - }, - } -} - -func resourceServiceRead(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { - c := i.(*client.Client) - service, err := c.GetService( - data.Id(), - data.Get("network_item_type").(string), - data.Get("network_item_id").(string), - ) - - if err != nil { - return diag.FromErr(err) - } - setResourceData(data, service) - return nil -} - -func setResourceData(data *schema.ResourceData, service *client.Service) { - data.SetId(service.Id) - _ = data.Set("name", service.Name) - _ = data.Set("description", service.Description) - _ = data.Set("type", service.Type) - _ = data.Set("routes", flattenRoutes(service.Routes)) - _ = data.Set("config", flattenServiceConfig(service.Config)) - _ = data.Set("network_item_type", service.NetworkItemType) - _ = data.Set("network_item_id", service.NetworkItemId) -} - -func resourceServiceDelete(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { - c := i.(*client.Client) - err := c.DeleteService( - data.Id(), - data.Get("network_item_type").(string), - data.Get("network_item_id").(string), - ) - if err != nil { - return diag.FromErr(err) - } - return nil -} - -func flattenServiceConfig(config *client.ServiceConfig) interface{} { - var data = map[string]interface{}{ - "custom_service_types": flattenCustomServiceTypes(config.CustomServiceTypes), - "service_types": config.ServiceTypes, - } - return []interface{}{data} -} - -func flattenCustomServiceTypes(types []*client.CustomServiceType) interface{} { - var data []interface{} - for _, t := range types { - data = append( - data, - map[string]interface{}{ - "icmp_type": flattenIcmpType(t.IcmpType), - }, - ) - } - return data -} - -func flattenIcmpType(icmpType []client.Range) interface{} { - var data []interface{} - for _, t := range icmpType { - data = append( - data, - map[string]interface{}{ - "lower_value": t.LowerValue, - "upper_value": t.UpperValue, - }, - ) - } - return data -} - -func flattenRoutes(routes []*client.Route) []string { - var data []string - for _, route := range routes { - data = append( - data, - route.Domain, - ) - } - return data -} - -func resourceServiceCreate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { - c := i.(*client.Client) - var diags diag.Diagnostics - - service, err := c.CreateService(resourceDataToService(data)) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - - data.SetId(service.Id) - setResourceData(data, service) - return diags -} - -func resourceDataToService(data *schema.ResourceData) *client.Service { - routes := data.Get("routes").([]interface{}) - var configRoutes []*client.Route - for _, r := range routes { - configRoutes = append( - configRoutes, - &client.Route{ - Value: r.(string), - }, - ) - } - - config := client.ServiceConfig{} - configList := data.Get("config").([]interface{}) - if len(configList) > 0 && configList[0] != nil { - - config.CustomServiceTypes = []*client.CustomServiceType{} - config.ServiceTypes = []string{} - - mainConfig := configList[0].(map[string]interface{}) - for _, r := range mainConfig["custom_service_types"].([]interface{}) { - cst := r.(map[string]interface{}) - var icmpTypes []client.Range - for _, r := range cst["icmp_type"].([]interface{}) { - icmpType := r.(map[string]interface{}) - icmpTypes = append( - icmpTypes, - client.Range{ - LowerValue: icmpType["lower_value"].(int), - UpperValue: icmpType["upper_value"].(int), - }, - ) - } - config.CustomServiceTypes = append( - config.CustomServiceTypes, - &client.CustomServiceType{ - IcmpType: icmpTypes, - }, - ) - } - - for _, r := range mainConfig["service_types"].([]interface{}) { - config.ServiceTypes = append(config.ServiceTypes, r.(string)) - } - } - - s := &client.Service{ - Name: data.Get("name").(string), - Description: data.Get("description").(string), - NetworkItemId: data.Get("network_item_id").(string), - NetworkItemType: data.Get("network_item_type").(string), - Type: data.Get("type").(string), - Routes: configRoutes, - Config: &config, - } - return s -} diff --git a/openvpncloud/resource_service_test.go b/openvpncloud/resource_service_test.go deleted file mode 100644 index 1d2c776..0000000 --- a/openvpncloud/resource_service_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package openvpncloud - -import ( - "errors" - "fmt" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - "testing" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" -) - -func TestAccOpenvpncloudService_basic(t *testing.T) { - rn := "openvpncloud_service.test" - networkName := acctest.RandStringFromCharSet(10, alphabet) - service := client.Service{ - Name: acctest.RandStringFromCharSet(10, alphabet), - } - serviceChanged := service - serviceChanged.Name = fmt.Sprintf("changed-%s", acctest.RandStringFromCharSet(10, alphabet)) - - check := func(service client.Service) resource.TestCheckFunc { - return resource.ComposeTestCheckFunc( - testAccCheckOpenvpncloudServiceExists(rn, networkName), - resource.TestCheckResourceAttr(rn, "name", service.Name), - ) - } - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProviderFactories: testAccProviderFactories, - CheckDestroy: testAccCheckOpenvpncloudServiceDestroy, - Steps: []resource.TestStep{ - { - Config: testAccOpenvpncloudServiceConfig(service, networkName), - Check: check(service), - }, - { - Config: testAccOpenvpncloudServiceConfig(serviceChanged, networkName), - Check: check(serviceChanged), - }, - }, - }) -} - -func testAccCheckOpenvpncloudServiceExists(rn, networkId string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[rn] - if !ok { - return fmt.Errorf("not found: %s", rn) - } - - if rs.Primary.ID == "" { - return errors.New("no ID is set") - } - - c := testAccProvider.Meta().(*client.Client) - _, err := c.GetService(rs.Primary.ID, rs.Primary.Attributes["network_item_type"], rs.Primary.Attributes["network_item_id"]) - if err != nil { - return err - } - return nil - } -} - -func testAccCheckOpenvpncloudServiceDestroy(state *terraform.State) error { - c := testAccProvider.Meta().(*client.Client) - for _, rs := range state.RootModule().Resources { - if rs.Type != "openvpncloud_service" { - continue - } - id := rs.Primary.Attributes["id"] - s, err := c.GetService(id, "c63acae0-b569-4116-9b39-921c1dee62d2", "NETWORK") - if err == nil || s != nil { - return fmt.Errorf("service still exists") - } - } - return nil -} - -func testAccOpenvpncloudServiceConfig(service client.Service, networkName string) string { - return fmt.Sprintf(` -provider "openvpncloud" { - base_url = "https://%s.api.openvpn.com" -} - -resource "openvpncloud_network" "test" { - name = "%s" - - default_connector { - name = "%s" - vpn_region_id = "fi-hel" - } - default_route { - value = "10.1.2.0/24" - type = "IP_V4" - } -} - -resource "openvpncloud_service" "test" { - name = "%s" - type = "SERVICE_DESTINATION" - description = "test" - network_item_type = "NETWORK" - network_item_id = openvpncloud_network.test.id - routes = ["test.ua" ] - config { - service_types = ["ANY"] - } -} -`, testCloudID, networkName, fmt.Sprintf("connector_%s", networkName), service.Name) -} diff --git a/openvpncloud/resource_user.go b/openvpncloud/resource_user.go deleted file mode 100644 index 2cbce0e..0000000 --- a/openvpncloud/resource_user.go +++ /dev/null @@ -1,226 +0,0 @@ -package openvpncloud - -import ( - "context" - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func resourceUser() *schema.Resource { - return &schema.Resource{ - Description: "Use `openvpncloud_user` to create an OpenVPN Cloud user.", - CreateContext: resourceUserCreate, - ReadContext: resourceUserRead, - UpdateContext: resourceUserUpdate, - DeleteContext: resourceUserDelete, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - Schema: map[string]*schema.Schema{ - "username": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringLenBetween(1, 120), - Description: "A username for the user.", - }, - "email": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 120), - Description: "An invitation to OpenVPN cloud account will be sent to this email. It will include an initial password and a VPN setup guide.", - }, - "first_name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 20), - Description: "User's first name.", - }, - "last_name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 20), - Description: "User's last name.", - }, - "group_id": { - Type: schema.TypeString, - Optional: true, - Description: "The UUID of a user's group.", - }, - "role": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: "MEMBER", - Description: "The type of user role. Valid values are `ADMIN`, `MEMBER`, or `OWNER`.", - }, - "devices": { - Type: schema.TypeList, - Optional: true, - ForceNew: true, - MaxItems: 1, - Description: "When a user signs in, the device that they use will be added to their account. You can read more at [OpenVPN Cloud Device](https://openvpn.net/cloud-docs/device/).", - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 32), - Description: "A device name.", - }, - "description": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 120), - Description: "A device description.", - }, - "ipv4_address": { - Type: schema.TypeString, - Optional: true, - Description: "An IPv4 address of the device.", - }, - "ipv6_address": { - Type: schema.TypeString, - Optional: true, - Description: "An IPv6 address of the device.", - }, - }, - }, - }, - }, - } -} - -func resourceUserCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - username := d.Get("username").(string) - email := d.Get("email").(string) - firstName := d.Get("first_name").(string) - lastName := d.Get("last_name").(string) - groupId := d.Get("group_id").(string) - role := d.Get("role").(string) - configDevices := d.Get("devices").([]interface{}) - var devices []client.Device - for _, d := range configDevices { - device := d.(map[string]interface{}) - devices = append( - devices, - client.Device{ - Name: device["name"].(string), - Description: device["description"].(string), - IPv4Address: device["ipv4_address"].(string), - IPv6Address: device["ipv6_address"].(string), - }, - ) - - } - u := client.User{ - Username: username, - Email: email, - FirstName: firstName, - LastName: lastName, - GroupId: groupId, - Devices: devices, - Role: role, - } - user, err := c.CreateUser(u) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - d.SetId(user.Id) - return append(diags, diag.Diagnostic{ - Severity: diag.Warning, - Summary: "The user's role cannot be changed using the code.", - Detail: "There is a bug in OpenVPN Cloud API that prevents setting the user's role during the creation. All users are created as Members by default. Once it's fixed, the provider will be updated accordingly.", - }) -} - -func resourceUserRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - userId := d.Id() - u, err := c.GetUserById(userId) - - // If group_id is not set, OpenVPN cloud sets it to the default group. - var groupId string - if d.Get("group_id") == "" { - // The group has not been explicitly set. - // Set it to an empty string to keep the default group. - groupId = "" - } else { - groupId = u.GroupId - } - - if err != nil { - return append(diags, diag.FromErr(err)...) - } - if u == nil { - d.SetId("") - } else { - d.Set("username", u.Username) - d.Set("email", u.Email) - d.Set("first_name", u.FirstName) - d.Set("last_name", u.LastName) - d.Set("group_id", groupId) - d.Set("devices", u.Devices) - d.Set("role", u.Role) - } - return diags -} - -func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - if !d.HasChanges("first_name", "last_name", "group_id", "email") { - return diags - } - - u, err := c.GetUserById(d.Id()) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - - _, email := d.GetChange("email") - _, firstName := d.GetChange("first_name") - _, lastName := d.GetChange("last_name") - _, role := d.GetChange("role") - status := u.Status - oldGroupId, newGroupId := d.GetChange("group_id") - - groupId := newGroupId.(string) - // If both are empty strings, then the group has not been set explicitly. - // The update endpoint requires group_id to be set, so we should set it to the default group. - if oldGroupId.(string) == "" && groupId == "" { - g, err := c.GetUserGroupByName("Default") - if err != nil { - return append(diags, diag.FromErr(err)...) - } - groupId = g.Id - } - - err = c.UpdateUser(client.User{ - Id: d.Id(), - Email: email.(string), - FirstName: firstName.(string), - LastName: lastName.(string), - GroupId: groupId, - Role: role.(string), - Status: status, - }) - - return append(diags, diag.FromErr(err)...) -} - -func resourceUserDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - userId := d.Id() - err := c.DeleteUser(userId) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - return diags -} diff --git a/openvpncloud/resource_user_group.go b/openvpncloud/resource_user_group.go deleted file mode 100644 index 5ed33d5..0000000 --- a/openvpncloud/resource_user_group.go +++ /dev/null @@ -1,168 +0,0 @@ -package openvpncloud - -import ( - "context" - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/diag" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" -) - -func resourceUserGroup() *schema.Resource { - return &schema.Resource{ - Description: "Use `openvpncloud_user_group` to create an OpenVPN Cloud user group.", - CreateContext: resourceUserGroupCreate, - ReadContext: resourceUserGroupRead, - UpdateContext: resourceUserGroupUpdate, - DeleteContext: resourceUserGroupDelete, - Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, - }, - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Computed: true, - Description: "The ID of the user group.", - }, - "connect_auth": { - Type: schema.TypeString, - Optional: true, - Default: "AUTO", - ValidateFunc: validation.StringInSlice([]string{"AUTH", "AUTO", "STRICT_AUTH"}, false), - }, - "internet_access": { - Type: schema.TypeString, - Optional: true, - Default: "LOCAL", - ValidateFunc: validation.StringInSlice([]string{"LOCAL", "BLOCKED", "GLOBAL_INTERNET"}, false), - }, - "max_device": { - Type: schema.TypeInt, - Optional: true, - Default: 3, - Description: "The maximum number of devices that can be connected to the user group.", - }, - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 40), - Description: "The name of the user group.", - }, - "system_subnets": { - Type: schema.TypeList, - Optional: true, - Computed: true, - Default: nil, - Description: "A list of subnets that are accessible to the user group.", - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "vpn_region_ids": { - Type: schema.TypeList, - Required: true, - MinItems: 1, - Description: "A list of VPN regions that are accessible to the user group.", - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - }, - } -} - -func resourceUserGroupUpdate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { - c := i.(*client.Client) - var diags diag.Diagnostics - ug := resourceDataToUserGroup(data) - - userGroup, err := c.UpdateUserGroup(data.Id(), ug) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - - if userGroup == nil { - data.SetId("") - } else { - updateUserGroupData(data, userGroup) - } - - return diags -} - -func resourceDataToUserGroup(data *schema.ResourceData) *client.UserGroup { - name := data.Get("name").(string) - connectAuth := data.Get("connect_auth").(string) - maxDevice := data.Get("max_device").(int) - internetAccess := data.Get("internet_access").(string) - configSystemSubnets := data.Get("system_subnets").([]interface{}) - var systemSubnets []string - for _, s := range configSystemSubnets { - systemSubnets = append(systemSubnets, s.(string)) - } - configVpnRegionIds := data.Get("vpn_region_ids").([]interface{}) - var vpnRegionIds []string - for _, r := range configVpnRegionIds { - vpnRegionIds = append(vpnRegionIds, r.(string)) - } - - ug := &client.UserGroup{ - Name: name, - ConnectAuth: connectAuth, - MaxDevice: maxDevice, - SystemSubnets: systemSubnets, - VpnRegionIds: vpnRegionIds, - InternetAccess: internetAccess, - } - return ug -} - -func updateUserGroupData(data *schema.ResourceData, userGroup *client.UserGroup) { - data.SetId(userGroup.Id) - _ = data.Set("connect_auth", userGroup.ConnectAuth) - _ = data.Set("max_device", userGroup.MaxDevice) - _ = data.Set("name", userGroup.Name) - _ = data.Set("system_subnets", userGroup.SystemSubnets) - _ = data.Set("vpn_region_ids", userGroup.VpnRegionIds) - _ = data.Set("internet_access", userGroup.InternetAccess) -} - -func resourceUserGroupDelete(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { - c := i.(*client.Client) - var diags diag.Diagnostics - err := c.DeleteUserGroup(data.Id()) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - data.SetId("") - return diags -} - -func resourceUserGroupRead(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { - c := i.(*client.Client) - var diags diag.Diagnostics - userGroup, err := c.GetUserGroupById(data.Id()) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - - if userGroup == nil { - data.SetId("") - } else { - updateUserGroupData(data, userGroup) - } - return diags -} - -func resourceUserGroupCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - c := m.(*client.Client) - var diags diag.Diagnostics - ug := resourceDataToUserGroup(d) - - userGroup, err := c.CreateUserGroup(ug) - if err != nil { - return append(diags, diag.FromErr(err)...) - } - updateUserGroupData(d, userGroup) - return diags -} diff --git a/openvpncloud/resource_user_group_test.go b/openvpncloud/resource_user_group_test.go deleted file mode 100644 index e1edce0..0000000 --- a/openvpncloud/resource_user_group_test.go +++ /dev/null @@ -1,107 +0,0 @@ -package openvpncloud - -import ( - "encoding/json" - "errors" - "fmt" - "testing" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" -) - -func TestAccOpenvpncloudUserGroup_basic(t *testing.T) { - rn := "openvpncloud_user_group.test" - userGroup := client.UserGroup{ - Name: acctest.RandStringFromCharSet(10, alphabet), - VpnRegionIds: []string{ - "us-east-1", - }, - } - userGroupChanged := userGroup - userGroupChanged.Name = fmt.Sprintf("changed-%s", acctest.RandStringFromCharSet(10, alphabet)) - - check := func(userGroup client.UserGroup) resource.TestCheckFunc { - return resource.ComposeTestCheckFunc( - testAccCheckOpenvpncloudUserGroupExists(rn), - resource.TestCheckResourceAttr(rn, "name", userGroup.Name), - resource.TestCheckResourceAttr(rn, "vpn_region_ids.0", userGroup.VpnRegionIds[0]), - ) - } - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProviderFactories: testAccProviderFactories, - CheckDestroy: testAccCheckOpenvpncloudUserGroupDestroy, - Steps: []resource.TestStep{ - { - Config: testAccOpenvpncloudUserGroupConfig(userGroup), - Check: check(userGroup), - }, - { - Config: testAccOpenvpncloudUserGroupConfig(userGroupChanged), - Check: check(userGroupChanged), - }, - { - ResourceName: rn, - ImportState: true, - ImportStateIdFunc: testAccOpenvpncloudUserImportStateIdFunc(rn), - ImportStateVerify: true, - }, - }, - }) -} - -func testAccCheckOpenvpncloudUserGroupDestroy(s *terraform.State) error { - c := testAccProvider.Meta().(*client.Client) - for _, rs := range s.RootModule().Resources { - if rs.Type != "openvpncloud_user_group" { - continue - } - username := rs.Primary.Attributes["username"] - u, err := c.GetUserGroupById(username) - if err == nil { - if u != nil { - return errors.New("user still exists") - } - } - } - return nil -} - -func testAccCheckOpenvpncloudUserGroupExists(rn string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[rn] - if !ok { - return fmt.Errorf("not found: %s", rn) - } - - if rs.Primary.ID == "" { - return errors.New("no ID is set") - } - - c := testAccProvider.Meta().(*client.Client) - _, err := c.GetUserGroupById(rs.Primary.ID) - if err != nil { - return err - } - return nil - } -} - -func testAccOpenvpncloudUserGroupConfig(userGroup client.UserGroup) string { - idsStr, _ := json.Marshal(userGroup.VpnRegionIds) - - return fmt.Sprintf(` -provider "openvpncloud" { - base_url = "https://%s.api.openvpn.com" -} -resource "openvpncloud_user_group" "test" { - name = "%s" - vpn_region_ids = %s - -} -`, testCloudID, userGroup.Name, idsStr) -} diff --git a/openvpncloud/resource_user_test.go b/openvpncloud/resource_user_test.go deleted file mode 100644 index 514766a..0000000 --- a/openvpncloud/resource_user_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package openvpncloud - -import ( - "errors" - "fmt" - "testing" - - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" -) - -func TestAccOpenvpncloudUser_basic(t *testing.T) { - rn := "openvpncloud_user.test" - user := client.User{ - Username: acctest.RandStringFromCharSet(10, alphabet), - FirstName: acctest.RandStringFromCharSet(10, alphabet), - LastName: acctest.RandStringFromCharSet(10, alphabet), - Email: fmt.Sprintf("terraform-tests+%s@devopenvpn.in", acctest.RandString(10)), - } - userChanged := user - userChanged.Email = fmt.Sprintf("terraform-tests+changed%s@devopenvpn.in", acctest.RandString(10)) - var userID string - - check := func(user client.User) resource.TestCheckFunc { - return resource.ComposeTestCheckFunc( - testAccCheckOpenvpncloudUserExists(rn, &userID), - resource.TestCheckResourceAttr(rn, "username", user.Username), - resource.TestCheckResourceAttr(rn, "email", user.Email), - resource.TestCheckResourceAttr(rn, "first_name", user.FirstName), - resource.TestCheckResourceAttr(rn, "last_name", user.LastName), - ) - } - - resource.Test(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ProviderFactories: testAccProviderFactories, - CheckDestroy: testAccCheckOpenvpncloudUserDestroy, - Steps: []resource.TestStep{ - { - Config: testAccOpenvpncloudUserConfig(user), - Check: check(user), - }, - { - Config: testAccOpenvpncloudUserConfig(userChanged), - Check: check(userChanged), - }, - { - ResourceName: rn, - ImportState: true, - ImportStateIdFunc: testAccOpenvpncloudUserImportStateIdFunc(rn), - ImportStateVerify: true, - }, - }, - }) -} - -func testAccCheckOpenvpncloudUserDestroy(s *terraform.State) error { - client := testAccProvider.Meta().(*client.Client) - for _, rs := range s.RootModule().Resources { - if rs.Type != "openvpncloud_user" { - continue - } - username := rs.Primary.Attributes["username"] - u, err := client.GetUserById(username) - if err == nil { - if u != nil { - return errors.New("user still exists") - } - } - } - return nil -} - -func testAccCheckOpenvpncloudUserExists(n string, teamID *string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("not found: %s", n) - } - - if rs.Primary.ID == "" { - return errors.New("no ID is set") - } - - client := testAccProvider.Meta().(*client.Client) - _, err := client.GetUserById(rs.Primary.ID) - if err != nil { - return err - } - return nil - } -} - -func testAccOpenvpncloudUserImportStateIdFunc(n string) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[n] - if !ok { - return "", fmt.Errorf("not found: %s", n) - } - return rs.Primary.ID, nil - } -} - -func testAccOpenvpncloudUserConfig(user client.User) string { - return fmt.Sprintf(` -provider "openvpncloud" { - base_url = "https://%s.api.openvpn.com" -} -resource "openvpncloud_user" "test" { - username = "%s" - email = "%s" - first_name = "%s" - last_name = "%s" -} -`, testCloudID, user.Username, user.Email, user.FirstName, user.LastName) -} diff --git a/templates/data-sources/host.md b/templates/data-sources/host.md index 582d4a5..08f92ff 100644 --- a/templates/data-sources/host.md +++ b/templates/data-sources/host.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_host Data Source - terraform-provider-openvpncloud" +page_title: "cloudconnexa_host Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use an openvpncloud_host data source to read an existing OpenVPN Cloud connector. + Use an cloudconnexa_host data source to read an existing OpenVPN Cloud connector. --- -# openvpncloud_host (Data Source) +# cloudconnexa_host (Data Source) -Use an `openvpncloud_host` data source to read an existing OpenVPN Cloud connector. +Use an `cloudconnexa_host` data source to read an existing OpenVPN Cloud connector. diff --git a/templates/data-sources/network.md b/templates/data-sources/network.md index 81e2d39..bb8c3f0 100644 --- a/templates/data-sources/network.md +++ b/templates/data-sources/network.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_network Data Source - terraform-provider-openvpncloud" +page_title: "cloudconnexa_network Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use a openvpncloud_network data source to read an OpenVPN Cloud network. + Use a cloudconnexa_network data source to read an OpenVPN Cloud network. --- -# openvpncloud_network (Data Source) +# cloudconnexa_network (Data Source) -Use a `openvpncloud_network` data source to read an OpenVPN Cloud network. +Use a `cloudconnexa_network` data source to read an OpenVPN Cloud network. diff --git a/templates/data-sources/network_routes.md b/templates/data-sources/network_routes.md index 2f3f0fc..3149367 100644 --- a/templates/data-sources/network_routes.md +++ b/templates/data-sources/network_routes.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_network_routes Data Source - terraform-provider-openvpncloud" +page_title: "cloudconnexa_network_routes Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use an openvpncloud_network_routes data source to read all the routes associated with an OpenVPN Cloud network. + Use an cloudconnexa_network_routes data source to read all the routes associated with an OpenVPN Cloud network. --- -# openvpncloud_network_routes (Data Source) +# cloudconnexa_network_routes (Data Source) -Use an `openvpncloud_network_routes` data source to read all the routes associated with an OpenVPN Cloud network. +Use an `cloudconnexa_network_routes` data source to read all the routes associated with an OpenVPN Cloud network. diff --git a/templates/data-sources/user.md b/templates/data-sources/user.md index b27ae24..784f834 100644 --- a/templates/data-sources/user.md +++ b/templates/data-sources/user.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_user Data Source - terraform-provider-openvpncloud" +page_title: "cloudconnexa_user Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use a openvpncloud_user data source to read a specific OpenVPN Cloud user. + Use a cloudconnexa_user data source to read a specific OpenVPN Cloud user. --- -# openvpncloud_user (Data Source) +# cloudconnexa_user (Data Source) -Use a `openvpncloud_user` data source to read a specific OpenVPN Cloud user. +Use a `cloudconnexa_user` data source to read a specific OpenVPN Cloud user. diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl index 073bbc8..b0d7cea 100644 --- a/templates/index.md.tmpl +++ b/templates/index.md.tmpl @@ -21,5 +21,5 @@ Use this provider to interact with the [OpenVPN Cloud API](https://openvpn.net/c ### Optional -- **client_id** (String, Sensitive) If not provided, it will default to the value of the `OPENVPN_CLOUD_CLIENT_ID` environment variable. -- **client_secret** (String, Sensitive) If not provided, it will default to the value of the `OPENVPN_CLOUD_CLIENT_SECRET` environment variable. +- **client_id** (String, Sensitive) If not provided, it will default to the value of the `CLOUDCONNEXA_CLIENT_ID` environment variable. +- **client_secret** (String, Sensitive) If not provided, it will default to the value of the `CLOUDCONNEXA_CLIENT_SECRET` environment variable. diff --git a/templates/resources/connector.md b/templates/resources/connector.md index 2842aa2..a3ea7c7 100644 --- a/templates/resources/connector.md +++ b/templates/resources/connector.md @@ -1,15 +1,15 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_connector Resource - terraform-provider-openvpncloud" +page_title: "cloudconnexa_connector Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use openvpncloud_connector to create an OpenVPN Cloud connector. + Use cloudconnexa_connector to create an OpenVPN Cloud connector. ~> NOTE: This only creates the OpenVPN Cloud connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information. --- -# openvpncloud_connector (Resource) +# cloudconnexa_connector (Resource) -Use `openvpncloud_connector` to create an OpenVPN Cloud connector. +Use `cloudconnexa_connector` to create an OpenVPN Cloud connector. ~> NOTE: This only creates the OpenVPN Cloud connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information. @@ -36,7 +36,7 @@ Use `openvpncloud_connector` to create an OpenVPN Cloud connector. A connector can be imported using the connector ID, which can be fetched directly from the API. ``` -terraform import openvpncloud_connector.connector +terraform import cloudconnexa_connector.connector ``` ~> NOTE: If the Terraform resource settings are different from the imported connector, the next time you run `terraform apply` the provider will attempt to delete and recreate the connector, which will require you to re-configure the instance manually. \ No newline at end of file diff --git a/templates/resources/dns_record.md b/templates/resources/dns_record.md index d4981c6..ab826ed 100644 --- a/templates/resources/dns_record.md +++ b/templates/resources/dns_record.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_dns_record Resource - terraform-provider-openvpncloud" +page_title: "cloudconnexa_dns_record Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use openvpncloud_dns_record to create a DNS record on your VPN. + Use cloudconnexa_dns_record to create a DNS record on your VPN. --- -# openvpncloud_dns_record (Resource) +# cloudconnexa_dns_record (Resource) -Use `openvpncloud_dns_record` to create a DNS record on your VPN. +Use `cloudconnexa_dns_record` to create a DNS record on your VPN. @@ -33,5 +33,5 @@ Use `openvpncloud_dns_record` to create a DNS record on your VPN. A connector can be imported using the DNS record ID, which can be fetched directly from the API. ``` -terraform import openvpncloud_dns_record.record +terraform import cloudconnexa_dns_record.record ``` \ No newline at end of file diff --git a/templates/resources/host.md b/templates/resources/host.md index c37afbd..fb51956 100644 --- a/templates/resources/host.md +++ b/templates/resources/host.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_host Resource - terraform-provider-openvpncloud" +page_title: "cloudconnexa_host Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use openvpncloud_host to create an OpenVPN Cloud host. + Use cloudconnexa_host to create an OpenVPN Cloud host. --- -# openvpncloud_host (Resource) +# cloudconnexa_host (Resource) -Use `openvpncloud_host` to create an OpenVPN Cloud host. +Use `cloudconnexa_host` to create an OpenVPN Cloud host. @@ -51,5 +51,5 @@ Read-Only: A host can be imported using the DNS record ID, which can be fetched directly from the API. ``` -terraform import openvpncloud_host.host +terraform import cloudconnexa_host.host ``` \ No newline at end of file diff --git a/templates/resources/network.md b/templates/resources/network.md index 3cc141b..6ded36e 100644 --- a/templates/resources/network.md +++ b/templates/resources/network.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_network Resource - terraform-provider-openvpncloud" +page_title: "cloudconnexa_network Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use openvpncloud_network to create an OpenVPN Cloud Network. + Use cloudconnexa_network to create an OpenVPN Cloud Network. --- -# openvpncloud_network (Resource) +# cloudconnexa_network (Resource) -Use `openvpncloud_network` to create an OpenVPN Cloud Network. +Use `cloudconnexa_network` to create an OpenVPN Cloud Network. @@ -67,7 +67,7 @@ Read-Only: A network can be imported using the network ID, which can be fetched directly from the API. ``` -terraform import openvpncloud_network.network +terraform import cloudconnexa_network.network ``` ~> NOTE: This will only import the network itslef, but it'll create a new connector and a new route as its defaults. There is currently no way to import an existing connector and route along with a network. The existing connector(s)/route(s) will continue to work, but you'll need to set a `default_connector` and a `default_route` that don't collide with your existing resources. \ No newline at end of file diff --git a/templates/resources/route.md b/templates/resources/route.md index 66e31ae..019cadd 100644 --- a/templates/resources/route.md +++ b/templates/resources/route.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_route Resource - terraform-provider-openvpncloud" +page_title: "cloudconnexa_route Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use openvpncloud_route to create a route on an OpenVPN Cloud network. + Use cloudconnexa_route to create a route on an OpenVPN Cloud network. --- -# openvpncloud_route (Resource) +# cloudconnexa_route (Resource) -Use `openvpncloud_route` to create a route on an OpenVPN Cloud network. +Use `cloudconnexa_route` to create a route on an OpenVPN Cloud network. @@ -30,5 +30,5 @@ Use `openvpncloud_route` to create a route on an OpenVPN Cloud network. A route can be imported using the route ID, which can be fetched directly from the API. ``` -terraform import openvpncloud_route.route +terraform import cloudconnexa_route.route ``` \ No newline at end of file diff --git a/templates/resources/user.md b/templates/resources/user.md index 990d12f..af8a8ba 100644 --- a/templates/resources/user.md +++ b/templates/resources/user.md @@ -1,14 +1,14 @@ --- # generated by https://github.com/hashicorp/terraform-plugin-docs -page_title: "openvpncloud_user Resource - terraform-provider-openvpncloud" +page_title: "cloudconnexa_user Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use openvpncloud_user to create an OpenVPN Cloud user. + Use cloudconnexa_user to create an OpenVPN Cloud user. --- -# openvpncloud_user (Resource) +# cloudconnexa_user (Resource) -Use `openvpncloud_user` to create an OpenVPN Cloud user. +Use `cloudconnexa_user` to create an OpenVPN Cloud user. @@ -49,5 +49,5 @@ Optional: A user can be imported using the user ID using the format below. ``` -terraform import openvpncloud_user.user username@accountname +terraform import cloudconnexa_user.user username@accountname ``` From c76b219c8eb0e2879652cbb2abfd4e3d0e4d03d0 Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Fri, 12 Jan 2024 15:54:19 +0000 Subject: [PATCH 14/16] Update structure for new go-client --- cloudconnexa/data_source_connector.go | 67 ++++ cloudconnexa/data_source_host.go | 97 +++++ cloudconnexa/data_source_ip_service.go | 65 ++++ cloudconnexa/data_source_network.go | 167 +++++++++ cloudconnexa/data_source_network_routes.go | 82 ++++ cloudconnexa/data_source_user.go | 139 +++++++ cloudconnexa/data_source_user_group.go | 77 ++++ cloudconnexa/data_source_vpn_region.go | 65 ++++ cloudconnexa/provider.go | 86 +++++ cloudconnexa/provider_test.go | 59 +++ cloudconnexa/resource_connector.go | 137 +++++++ cloudconnexa/resource_connector_test.go | 87 +++++ cloudconnexa/resource_dns_record.go | 146 ++++++++ cloudconnexa/resource_dns_record_test.go | 71 ++++ cloudconnexa/resource_host.go | 268 +++++++++++++ cloudconnexa/resource_network.go | 352 ++++++++++++++++++ cloudconnexa/resource_route.go | 128 +++++++ cloudconnexa/resource_route_test.go | 132 +++++++ cloudconnexa/resource_service.go | 282 ++++++++++++++ cloudconnexa/resource_service_test.go | 113 ++++++ cloudconnexa/resource_user.go | 226 +++++++++++ cloudconnexa/resource_user_group.go | 168 +++++++++ cloudconnexa/resource_user_group_test.go | 107 ++++++ cloudconnexa/resource_user_test.go | 118 ++++++ .../cloudconnexaclient/cloudconnexaclient.go | 29 ++ main.go | 1 - 26 files changed, 3268 insertions(+), 1 deletion(-) create mode 100644 cloudconnexa/data_source_connector.go create mode 100644 cloudconnexa/data_source_host.go create mode 100644 cloudconnexa/data_source_ip_service.go create mode 100644 cloudconnexa/data_source_network.go create mode 100644 cloudconnexa/data_source_network_routes.go create mode 100644 cloudconnexa/data_source_user.go create mode 100644 cloudconnexa/data_source_user_group.go create mode 100644 cloudconnexa/data_source_vpn_region.go create mode 100644 cloudconnexa/provider.go create mode 100644 cloudconnexa/provider_test.go create mode 100644 cloudconnexa/resource_connector.go create mode 100644 cloudconnexa/resource_connector_test.go create mode 100644 cloudconnexa/resource_dns_record.go create mode 100644 cloudconnexa/resource_dns_record_test.go create mode 100644 cloudconnexa/resource_host.go create mode 100644 cloudconnexa/resource_network.go create mode 100644 cloudconnexa/resource_route.go create mode 100644 cloudconnexa/resource_route_test.go create mode 100644 cloudconnexa/resource_service.go create mode 100644 cloudconnexa/resource_service_test.go create mode 100644 cloudconnexa/resource_user.go create mode 100644 cloudconnexa/resource_user_group.go create mode 100644 cloudconnexa/resource_user_group_test.go create mode 100644 cloudconnexa/resource_user_test.go create mode 100644 internal/cloudconnexaclient/cloudconnexaclient.go diff --git a/cloudconnexa/data_source_connector.go b/cloudconnexa/data_source_connector.go new file mode 100644 index 0000000..e4cab9a --- /dev/null +++ b/cloudconnexa/data_source_connector.go @@ -0,0 +1,67 @@ +package cloudconnexa + +import ( + "context" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" +) + +func dataSourceConnector() *schema.Resource { + return &schema.Resource{ + Description: "Use an `cloudconnexa_connector` data source to read an existing Cloud Connexa connector.", + ReadContext: dataSourceConnectorRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the connector.", + }, + "network_item_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the network or host with which the connector is associated.", + }, + "network_item_type": { + Type: schema.TypeString, + Computed: true, + Description: "The network object type of the connector. This typically will be set to either `NETWORK` or `HOST`.", + }, + "vpn_region_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the region where the connector is deployed.", + }, + "ip_v4_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IPV4 address of the connector.", + }, + "ip_v6_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IPV6 address of the connector.", + }, + }, + } +} + +func dataSourceConnectorRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + connector, err := c.Connectors.GetByName(d.Get("name").(string)) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + d.Set("name", connector.Name) + d.Set("network_item_id", connector.NetworkItemId) + d.Set("network_item_type", connector.NetworkItemType) + d.Set("vpn_region_id", connector.VpnRegionId) + d.Set("ip_v4_address", connector.IPv4Address) + d.Set("ip_v6_address", connector.IPv6Address) + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + return diags +} diff --git a/cloudconnexa/data_source_host.go b/cloudconnexa/data_source_host.go new file mode 100644 index 0000000..47c07f2 --- /dev/null +++ b/cloudconnexa/data_source_host.go @@ -0,0 +1,97 @@ +package cloudconnexa + +import ( + "context" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" +) + +func dataSourceHost() *schema.Resource { + return &schema.Resource{ + Description: "Use an `cloudconnexa_host` data source to read an existing Cloud Connexa connector.", + ReadContext: dataSourceHostRead, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "The name of the host.", + }, + "internet_access": { + Type: schema.TypeString, + Computed: true, + Description: "The type of internet access provided.", + }, + "system_subnets": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "The IPV4 and IPV6 subnets automatically assigned to this host.", + }, + "connectors": { + Type: schema.TypeList, + Computed: true, + Description: "The list of connectors to be associated with this host.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The connector id.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The connector name.", + }, + "network_item_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the host with which the connector is associated.", + }, + "network_item_type": { + Type: schema.TypeString, + Computed: true, + Description: "The network object type of the connector. This typically will be set to `HOST`.", + }, + "vpn_region_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the region where the connector is deployed.", + }, + "ip_v4_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IPV4 address of the connector.", + }, + "ip_v6_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IPV6 address of the connector.", + }, + }, + }, + }, + }, + } +} + +func dataSourceHostRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + host, err := c.Hosts.GetByName(d.Get("name").(string)) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + d.Set("name", host.Name) + d.Set("internet_access", host.InternetAccess) + d.Set("system_subnets", host.SystemSubnets) + d.Set("connectors", getConnectorsSlice(&host.Connectors)) + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + return diags +} diff --git a/cloudconnexa/data_source_ip_service.go b/cloudconnexa/data_source_ip_service.go new file mode 100644 index 0000000..61f87e5 --- /dev/null +++ b/cloudconnexa/data_source_ip_service.go @@ -0,0 +1,65 @@ +package cloudconnexa + +import ( + "context" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" +) + +func dataSourceIPService() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceIPServiceRead, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Required: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + "routes": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "config": { + Type: schema.TypeList, + Computed: true, + Elem: resourceServiceConfig(), + }, + "network_item_type": { + Type: schema.TypeString, + Computed: true, + }, + "network_item_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceIPServiceRead(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { + c := i.(*cloudconnexa.Client) + service, err := c.IPServices.Get( + data.Id(), + ) + + if err != nil { + return diag.FromErr(err) + } + setResourceData(data, service) + return nil +} diff --git a/cloudconnexa/data_source_network.go b/cloudconnexa/data_source_network.go new file mode 100644 index 0000000..6315f67 --- /dev/null +++ b/cloudconnexa/data_source_network.go @@ -0,0 +1,167 @@ +package cloudconnexa + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceNetwork() *schema.Resource { + return &schema.Resource{ + Description: "Use a `cloudconnexa_network` data source to read an OpenVPN Cloud network.", + ReadContext: dataSourceNetworkRead, + Schema: map[string]*schema.Schema{ + "network_id": { + Type: schema.TypeString, + Computed: true, + Description: "The network ID.", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "The network name.", + }, + "egress": { + Type: schema.TypeBool, + Computed: true, + Description: "Boolean to indicate whether this network provides an egress or not.", + }, + "internet_access": { + Type: schema.TypeString, + Computed: true, + Description: "The type of internet access provided. Valid values are `BLOCKED`, `GLOBAL_INTERNET`, or `LOCAL`. Defaults to `LOCAL`.", + }, + "system_subnets": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "The IPV4 and IPV6 subnets automatically assigned to this network.", + }, + "routes": { + Type: schema.TypeList, + Computed: true, + Description: "The routes associated with this network.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The route id.", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of route. Valid values are `IP_V4`, `IP_V6`, and `DOMAIN`.", + }, + "subnet": { + Type: schema.TypeString, + Computed: true, + Description: "The value of the route, either an IPV4 address, an IPV6 address, or a DNS hostname.", + }, + }, + }, + }, + "connectors": { + Type: schema.TypeList, + Computed: true, + Description: "The list of connectors associated with this network.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The connector id.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The connector name.", + }, + "network_item_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the network with which the connector is associated.", + }, + "network_item_type": { + Type: schema.TypeString, + Computed: true, + Description: "The network object type of the connector. This typically will be set to `NETWORK`.", + }, + "vpn_region_id": { + Type: schema.TypeString, + Computed: true, + Description: "The id of the region where the connector is deployed.", + }, + "ip_v4_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IPV4 address of the connector.", + }, + "ip_v6_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IPV6 address of the connector.", + }, + }, + }, + }, + }, + } +} + +func dataSourceNetworkRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + networkName := d.Get("name").(string) + network, err := c.Networks.GetByName(networkName) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if network == nil { + return append(diags, diag.Errorf("Network with name %s was not found", networkName)...) + } + d.Set("network_id", network.Id) + d.Set("name", network.Name) + d.Set("description", network.Description) + d.Set("egress", network.Egress) + d.Set("internet_access", network.InternetAccess) + d.Set("system_subnets", network.SystemSubnets) + d.Set("routes", getRoutesSlice(&network.Routes)) + //d.Set("connectors", getConnectorsSlice(&network.Connectors)) + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + return diags +} + +func getRoutesSlice(networkRoutes *[]cloudconnexa.Route) []interface{} { + routes := make([]interface{}, len(*networkRoutes)) + for i, r := range *networkRoutes { + route := make(map[string]interface{}) + route["id"] = r.Id + route["subnet"] = r.Subnet + route["type"] = r.Type + routes[i] = route + } + return routes +} + +func getConnectorsSlice(connectors *[]cloudconnexa.Connector) []interface{} { + conns := make([]interface{}, len(*connectors)) + for i, c := range *connectors { + connector := make(map[string]interface{}) + connector["id"] = c.Id + connector["name"] = c.Name + connector["network_item_id"] = c.NetworkItemId + connector["network_item_type"] = c.NetworkItemType + connector["vpn_region_id"] = c.VpnRegionId + connector["ip_v4_address"] = c.IPv4Address + connector["ip_v6_address"] = c.IPv6Address + conns[i] = connector + } + return conns +} diff --git a/cloudconnexa/data_source_network_routes.go b/cloudconnexa/data_source_network_routes.go new file mode 100644 index 0000000..296b72f --- /dev/null +++ b/cloudconnexa/data_source_network_routes.go @@ -0,0 +1,82 @@ +package cloudconnexa + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceNetworkRoutes() *schema.Resource { + return &schema.Resource{ + Description: "Use an `cloudconnexa_network_routes` data source to read all the routes associated with an Cloud Connexa network.", + ReadContext: dataSourceNetworkRoutesRead, + Schema: map[string]*schema.Schema{ + "network_item_id": { + Type: schema.TypeString, + Required: true, + Description: "The id of the Cloud Connexa network of the routes to be discovered.", + }, + "routes": { + Type: schema.TypeList, + Computed: true, + Description: "The list of routes.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The unique identifier of the route.", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of route. Valid values are `IP_V4`, `IP_V6`, and others.", + }, + "subnet": { + Type: schema.TypeString, + Computed: true, + Description: "The subnet of the route.", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "A description of the route.", + }, + }, + }, + }, + }, + } +} + +func dataSourceNetworkRoutesRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + + networkId := d.Get("network_item_id").(string) + routes, err := c.Routes.List(networkId) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + + configRoutes := make([]map[string]interface{}, len(routes)) + for i, r := range routes { + route := make(map[string]interface{}) + route["id"] = r.Id + route["type"] = r.Type + route["subnet"] = r.Subnet + route["description"] = r.Description + configRoutes[i] = route + } + + if err := d.Set("routes", configRoutes); err != nil { + return append(diags, diag.FromErr(err)...) + } + + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + return diags +} diff --git a/cloudconnexa/data_source_user.go b/cloudconnexa/data_source_user.go new file mode 100644 index 0000000..cf41e3a --- /dev/null +++ b/cloudconnexa/data_source_user.go @@ -0,0 +1,139 @@ +package cloudconnexa + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceUser() *schema.Resource { + return &schema.Resource{ + Description: "Use a `cloudconnexa_user` data source to read a specific OpenVPN Cloud user.", + ReadContext: dataSourceUserRead, + Schema: map[string]*schema.Schema{ + "user_id": { + Type: schema.TypeString, + Computed: true, + Description: "The ID of this resource.", + }, + "username": { + Type: schema.TypeString, + Required: true, + Description: "The username of the user.", + }, + "role": { + Type: schema.TypeString, + Required: true, + Description: "The type of user role. Valid values are `ADMIN`, `MEMBER`, or `OWNER`.", + }, + "email": { + Type: schema.TypeString, + Computed: true, + Description: "The email address of the user.", + }, + "auth_type": { + Type: schema.TypeString, + Computed: true, + Description: "The authentication type of the user.", + }, + "first_name": { + Type: schema.TypeString, + Computed: true, + Description: "The user's first name.", + }, + "last_name": { + Type: schema.TypeString, + Computed: true, + Description: "The user's last name.", + }, + "group_id": { + Type: schema.TypeString, + Computed: true, + Description: "The user's group id.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The user's status.", + }, + "devices": { + Type: schema.TypeList, + Computed: true, + Description: "The list of user devices.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The device's id.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The device's name.", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The device's description.", + }, + "ip_v4_address": { + Type: schema.TypeString, + Computed: true, + Description: "The device's IPV4 address.", + }, + "ip_v6_address": { + Type: schema.TypeString, + Computed: true, + Description: "The device's IPV6 address.", + }, + }, + }, + }, + }, + } +} + +func dataSourceUserRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + userName := d.Get("username").(string) + user, err := c.Users.Get(userName) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if user == nil { + return append(diags, diag.Errorf("User with name %s was not found", userName)...) + } + + d.Set("user_id", user.Id) + d.Set("username", user.Username) + d.Set("role", user.Role) + d.Set("email", user.Email) + d.Set("auth_type", user.AuthType) + d.Set("first_name", user.FirstName) + d.Set("last_name", user.LastName) + d.Set("group_id", user.GroupId) + d.Set("status", user.Status) + d.Set("devices", getUserDevicesSlice(&user.Devices)) + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + return diags +} + +func getUserDevicesSlice(userDevices *[]cloudconnexa.Device) []interface{} { + devices := make([]interface{}, len(*userDevices)) + for i, d := range *userDevices { + device := make(map[string]interface{}) + device["id"] = d.Id + device["name"] = d.Name + device["description"] = d.Description + device["ip_v4_address"] = d.IPv4Address + device["ip_v6_address"] = d.IPv6Address + devices[i] = device + } + return devices +} diff --git a/cloudconnexa/data_source_user_group.go b/cloudconnexa/data_source_user_group.go new file mode 100644 index 0000000..bc58d4b --- /dev/null +++ b/cloudconnexa/data_source_user_group.go @@ -0,0 +1,77 @@ +package cloudconnexa + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceUserGroup() *schema.Resource { + return &schema.Resource{ + Description: "Use an `cloudconnexa_user_group` data source to read an OpenVPN Cloud user group.", + ReadContext: dataSourceUserGroupRead, + Schema: map[string]*schema.Schema{ + "user_group_id": { + Type: schema.TypeString, + Computed: true, + Description: "The user group ID.", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "The user group name.", + }, + "vpn_region_ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "The list of VPN region IDs this user group is associated with.", + }, + "internet_access": { + Type: schema.TypeString, + Computed: true, + Description: "The type of internet access provided. Valid values are `BLOCKED`, `GLOBAL_INTERNET`, or `LOCAL`. Defaults to `LOCAL`.", + }, + "max_device": { + Type: schema.TypeInt, + Computed: true, + Description: "The maximum number of devices per user.", + }, + "system_subnets": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "The IPV4 and IPV6 addresses of the subnets associated with this user group.", + }, + }, + } +} + +func dataSourceUserGroupRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + userGroupName := d.Get("name").(string) + userGroup, err := c.UserGroups.GetByName(userGroupName) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if userGroup == nil { + return append(diags, diag.Errorf("User group with name %s was not found", userGroupName)...) + } + d.Set("user_group_id", userGroup.ID) + d.Set("name", userGroup.Name) + d.Set("vpn_region_ids", userGroup.VpnRegionIds) + d.Set("internet_access", userGroup.InternetAccess) + d.Set("max_device", userGroup.MaxDevice) + d.Set("system_subnets", userGroup.SystemSubnets) + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + return diags +} diff --git a/cloudconnexa/data_source_vpn_region.go b/cloudconnexa/data_source_vpn_region.go new file mode 100644 index 0000000..f28430d --- /dev/null +++ b/cloudconnexa/data_source_vpn_region.go @@ -0,0 +1,65 @@ +package cloudconnexa + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + "strconv" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceVpnRegion() *schema.Resource { + return &schema.Resource{ + Description: "Use a `cloudconnexa_vpn_region` data source to read an OpenVPN Cloud VPN region.", + ReadContext: dataSourceVpnRegionRead, + Schema: map[string]*schema.Schema{ + "region_id": { + Type: schema.TypeString, + Required: true, + Description: "The id of the region.", + }, + "continent": { + Type: schema.TypeString, + Computed: true, + Description: "The continent of the region.", + }, + "country": { + Type: schema.TypeString, + Computed: true, + Description: "The country of the region.", + }, + "country_iso": { + Type: schema.TypeString, + Computed: true, + Description: "The ISO code of the country of the region.", + }, + "region_name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the region.", + }, + }, + } +} + +func dataSourceVpnRegionRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + vpnRegionId := d.Get("region_id").(string) + vpnRegion, err := c.VPNRegions.GetVpnRegion(vpnRegionId) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if vpnRegion == nil { + return append(diags, diag.Errorf("VPN region with id %s was not found", vpnRegionId)...) + } + d.Set("region_id", vpnRegion.Id) + d.Set("continent", vpnRegion.Continent) + d.Set("country", vpnRegion.Country) + d.Set("country_iso", vpnRegion.CountryISO) + d.Set("region_name", vpnRegion.RegionName) + d.SetId(strconv.FormatInt(time.Now().Unix(), 10)) + return diags +} diff --git a/cloudconnexa/provider.go b/cloudconnexa/provider.go new file mode 100644 index 0000000..da3d91e --- /dev/null +++ b/cloudconnexa/provider.go @@ -0,0 +1,86 @@ +package cloudconnexa + +import ( + "context" + "fmt" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +const ( + clientIDEnvVar = "OPENVPN_CLOUD_CLIENT_ID" + clientSecretEnvVar = "OPENVPN_CLOUD_CLIENT_SECRET" +) + +type Token struct { + AccessToken string `json:"access_token"` +} + +func Provider() *schema.Provider { + return &schema.Provider{ + Schema: map[string]*schema.Schema{ + "client_id": { + Description: "The authentication client_id used to connect to Cloud Connexa API. The value can be sourced from " + + "the `CLOUDCONNEXA_CLIENT_ID` environment variable.", + Type: schema.TypeString, + Optional: true, + Sensitive: true, + DefaultFunc: schema.EnvDefaultFunc(clientIDEnvVar, nil), + }, + "client_secret": { + Description: "The authentication client_secret used to connect to Cloud Connexa API. The value can be sourced from " + + "the `CLOUDCONNEXA_CLIENT_SECRET` environment variable.", + Type: schema.TypeString, + Optional: true, + Sensitive: true, + DefaultFunc: schema.EnvDefaultFunc(clientSecretEnvVar, nil), + }, + "base_url": { + Description: "The target Cloud Connexa Base API URL in the format `https://[companyName].api.openvpn.com`", + Type: schema.TypeString, + Required: true, + }, + }, + ResourcesMap: map[string]*schema.Resource{ + "cloudconnexa_network": resourceNetwork(), + "cloudconnexa_connector": resourceConnector(), + "cloudconnexa_route": resourceRoute(), + "cloudconnexa_dns_record": resourceDnsRecord(), + "cloudconnexa_user": resourceUser(), + "cloudconnexa_host": resourceHost(), + "cloudconnexa_user_group": resourceUserGroup(), + "cloudconnexa_ip_service": resourceIPService(), + }, + + DataSourcesMap: map[string]*schema.Resource{ + "cloudconnexa_network": dataSourceNetwork(), + "cloudconnexa_connector": dataSourceConnector(), + "cloudconnexa_user": dataSourceUser(), + "cloudconnexa_user_group": dataSourceUserGroup(), + "cloudconnexa_vpn_region": dataSourceVpnRegion(), + "cloudconnexa_network_routes": dataSourceNetworkRoutes(), + "cloudconnexa_host": dataSourceHost(), + "cloudconnexa_ip_service": dataSourceIPService(), + }, + ConfigureContextFunc: providerConfigure, + } +} + +func providerConfigure(ctx context.Context, d *schema.ResourceData) (interface{}, diag.Diagnostics) { + clientId := d.Get("client_id").(string) + clientSecret := d.Get("client_secret").(string) + baseUrl := d.Get("base_url").(string) + cloudConnexaClient, err := cloudconnexa.NewClient(baseUrl, clientId, clientSecret) + var diags diag.Diagnostics + if err != nil { + diags = append(diags, diag.Diagnostic{ + Severity: diag.Error, + Summary: "Unable to create client", + Detail: fmt.Sprintf("Error: %v", err), + }) + return nil, diags + } + return cloudConnexaClient, nil +} diff --git a/cloudconnexa/provider_test.go b/cloudconnexa/provider_test.go new file mode 100644 index 0000000..e82347c --- /dev/null +++ b/cloudconnexa/provider_test.go @@ -0,0 +1,59 @@ +package cloudconnexa + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + "os" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const alphabet = "abcdefghigklmnopqrstuvwxyz" + +var testCloudID = os.Getenv("CLOUDCONNEXA_TEST_ORGANIZATION") +var testAccProvider *schema.Provider +var testAccProviders map[string]*schema.Provider +var testAccProviderFactories map[string]func() (*schema.Provider, error) + +func init() { + testAccProvider = Provider() + testAccProviders = map[string]*schema.Provider{ + "cloudconnexa": testAccProvider, + } + testAccProviderFactories = map[string]func() (*schema.Provider, error){ + "cloudconnexa": func() (*schema.Provider, error) { + return testAccProvider, nil + }, + } +} + +func TestProvider(t *testing.T) { + err := Provider().InternalValidate() + require.NoError(t, err) + + // must have the required error when the credentials are not set + t.Setenv(clientIDEnvVar, "") + t.Setenv(clientSecretEnvVar, "") + rc := terraform.ResourceConfig{} + diags := Provider().Configure(context.Background(), &rc) + assert.True(t, diags.HasError()) + + for _, d := range diags { + assert.Truef(t, strings.Contains(d.Detail, cloudconnexa.ErrCredentialsRequired.Error()), + "error message does not contain the expected error: %s", d.Detail) + } +} + +func testAccPreCheck(t *testing.T) { + if v := os.Getenv(clientIDEnvVar); v == "" { + t.Fatalf("%s must be set for acceptance tests", clientIDEnvVar) + } + if v := os.Getenv(clientSecretEnvVar); v == "" { + t.Fatalf("%s must be set for acceptance tests", clientSecretEnvVar) + } +} diff --git a/cloudconnexa/resource_connector.go b/cloudconnexa/resource_connector.go new file mode 100644 index 0000000..9769ea0 --- /dev/null +++ b/cloudconnexa/resource_connector.go @@ -0,0 +1,137 @@ +package cloudconnexa + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceConnector() *schema.Resource { + return &schema.Resource{ + Description: "Use `cloudconnexa_connector` to create an OpenVPN Cloud connector.\n\n~> NOTE: This only creates the OpenVPN Cloud connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", + CreateContext: resourceConnectorCreate, + ReadContext: resourceConnectorRead, + DeleteContext: resourceConnectorDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The connector display name.", + }, + "vpn_region_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of the region where the connector will be deployed.", + }, + "network_item_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"HOST", "NETWORK"}, false), + Description: "The type of network item of the connector. Supported values are `HOST` and `NETWORK`.", + }, + "network_item_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of the network with which this connector is associated.", + }, + "ip_v4_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IPV4 address of the connector.", + }, + "ip_v6_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IPV6 address of the connector.", + }, + }, + } +} + +func resourceConnectorCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + name := d.Get("name").(string) + networkItemId := d.Get("network_item_id").(string) + networkItemType := d.Get("network_item_type").(string) + vpnRegionId := d.Get("vpn_region_id").(string) + connector := cloudconnexa.Connector{ + Name: name, + NetworkItemId: networkItemId, + NetworkItemType: networkItemType, + VpnRegionId: vpnRegionId, + } + conn, err := c.Connectors.Create(connector, networkItemId) + if err != nil { + return diag.FromErr(err) + } + d.SetId(conn.Id) + return append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Connector needs to be set up manually", + Detail: "Terraform only creates the OpenVPN Cloud connector object, but additional manual steps are required to associate a host in your infrastructure with this connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", + }) +} + +func resourceConnectorRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + connector, err := c.Connectors.GetByID(d.Id()) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if connector == nil { + d.SetId("") + } else { + d.SetId(connector.Id) + d.Set("name", connector.Name) + d.Set("vpn_region_id", connector.VpnRegionId) + d.Set("network_item_type", connector.NetworkItemType) + d.Set("network_item_id", connector.NetworkItemId) + d.Set("ip_v4_address", connector.IPv4Address) + d.Set("ip_v6_address", connector.IPv6Address) + } + return diags +} + +func resourceConnectorDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + err := c.Connectors.Delete(d.Id(), d.Get("network_item_id").(string), d.Get("network_item_type").(string)) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + return diags +} + +func getConnectorSlice(connectors []cloudconnexa.Connector, networkItemId string, connectorName string) []interface{} { + if len(connectors) == 0 { + return nil + } + connectorsList := make([]interface{}, 1) + for _, c := range connectors { + if c.NetworkItemId == networkItemId && c.Name == connectorName { + connector := make(map[string]interface{}) + connector["id"] = c.Id + connector["name"] = c.Name + connector["network_item_id"] = c.NetworkItemId + connector["network_item_type"] = c.NetworkItemType + connector["vpn_region_id"] = c.VpnRegionId + connector["ip_v4_address"] = c.IPv4Address + connector["ip_v6_address"] = c.IPv6Address + connectorsList[0] = connector + break + } + } + return connectorsList +} diff --git a/cloudconnexa/resource_connector_test.go b/cloudconnexa/resource_connector_test.go new file mode 100644 index 0000000..4df8102 --- /dev/null +++ b/cloudconnexa/resource_connector_test.go @@ -0,0 +1,87 @@ +package cloudconnexa + +import ( + "fmt" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccCloudConnexaConnector_basic(t *testing.T) { + rName := acctest.RandomWithPrefix("test-connector") + resourceName := "cloudconnexa_connector.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckCloudConnexaConnectorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudConnexaConnectorConfigBasic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckCloudConnexaConnectorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrSet(resourceName, "vpn_region_id"), + resource.TestCheckResourceAttrSet(resourceName, "network_item_type"), + resource.TestCheckResourceAttrSet(resourceName, "network_item_id"), + resource.TestCheckResourceAttrSet(resourceName, "ip_v4_address"), + resource.TestCheckResourceAttrSet(resourceName, "ip_v6_address"), + ), + }, + }, + }) +} + +func testAccCheckCloudConnexaConnectorExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("No connector ID is set") + } + return nil + } +} + +func testAccCheckCloudConnexaConnectorDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*cloudconnexa.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "cloudconnexa_connector" { + continue + } + + connectorId := rs.Primary.ID + connector, err := client.Connectors.GetByID(connectorId) + + if err != nil { + return err + } + + if connector != nil { + return fmt.Errorf("connector with ID '%s' still exists", connectorId) + } + } + + return nil +} + +func testAccCloudConnexaConnectorConfigBasic(rName string) string { + return fmt.Sprintf(` +provider "cloudconnexa" { + base_url = "https://%[1]s.api.openvpn.com" +} + +resource "cloudconnexa_connector" "test" { + name = "%s" + vpn_region_id = "us-west-1" + network_item_type = "HOST" + network_item_id = "example_network_item_id" +} +`, testCloudID, rName) +} diff --git a/cloudconnexa/resource_dns_record.go b/cloudconnexa/resource_dns_record.go new file mode 100644 index 0000000..61dd85c --- /dev/null +++ b/cloudconnexa/resource_dns_record.go @@ -0,0 +1,146 @@ +package cloudconnexa + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceDnsRecord() *schema.Resource { + return &schema.Resource{ + Description: "Use `cloudconnexa_dns_record` to create a DNS record on your VPN.", + CreateContext: resourceDnsRecordCreate, + ReadContext: resourceDnsRecordRead, + DeleteContext: resourceDnsRecordDelete, + UpdateContext: resourceDnsRecordUpdate, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "domain": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The DNS record name.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Default: "Managed by Terraform", + ValidateFunc: validation.StringLenBetween(1, 120), + Description: "The description for the UI. Defaults to `Managed by Terraform`.", + }, + "ip_v4_addresses": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.IsIPv4Address, + }, + Description: "The list of IPV4 addresses to which this record will resolve.", + }, + "ip_v6_addresses": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.IsIPv6Address, + }, + Description: "The list of IPV6 addresses to which this record will resolve.", + }, + }, + } +} + +func resourceDnsRecordCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + domain := d.Get("domain").(string) + description := d.Get("description").(string) + ipV4Addresses := d.Get("ip_v4_addresses").([]interface{}) + ipV4AddressesSlice := make([]string, 0) + for _, a := range ipV4Addresses { + ipV4AddressesSlice = append(ipV4AddressesSlice, a.(string)) + } + ipV6Addresses := d.Get("ip_v6_addresses").([]interface{}) + ipV6AddressesSlice := make([]string, 0) + for _, a := range ipV6Addresses { + ipV6AddressesSlice = append(ipV6AddressesSlice, a.(string)) + } + dr := cloudconnexa.DnsRecord{ + Domain: domain, + Description: description, + IPV4Addresses: ipV4AddressesSlice, + IPV6Addresses: ipV6AddressesSlice, + } + dnsRecord, err := c.DnsRecords.Create(dr) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + d.SetId(dnsRecord.Id) + return diags +} + +func resourceDnsRecordRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + recordId := d.Id() + r, err := c.DnsRecords.GetDnsRecord(recordId) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if r == nil { + d.SetId("") + } else { + d.Set("domain", r.Domain) + d.Set("description", r.Description) + d.Set("ip_v4_addresses", r.IPV4Addresses) + d.Set("ip_v6_addresses", r.IPV6Addresses) + } + return diags +} + +func resourceDnsRecordUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + _, domain := d.GetChange("domain") + _, description := d.GetChange("description") + _, ipV4Addresses := d.GetChange("ip_v4_addresses") + ipV4AddressesSlice := getAddressesSlice(ipV4Addresses.([]interface{})) + _, ipV6Addresses := d.GetChange("ip_v6_addresses") + ipV6AddressesSlice := getAddressesSlice(ipV6Addresses.([]interface{})) + dr := cloudconnexa.DnsRecord{ + Id: d.Id(), + Domain: domain.(string), + Description: description.(string), + IPV4Addresses: ipV4AddressesSlice, + IPV6Addresses: ipV6AddressesSlice, + } + err := c.DnsRecords.Update(dr) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + return diags +} + +func resourceDnsRecordDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + routeId := d.Id() + err := c.DnsRecords.Delete(routeId) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + return diags +} + +func getAddressesSlice(addresses []interface{}) []string { + addressesSlice := make([]string, 0) + for _, a := range addresses { + addressesSlice = append(addressesSlice, a.(string)) + } + return addressesSlice +} diff --git a/cloudconnexa/resource_dns_record_test.go b/cloudconnexa/resource_dns_record_test.go new file mode 100644 index 0000000..26252f9 --- /dev/null +++ b/cloudconnexa/resource_dns_record_test.go @@ -0,0 +1,71 @@ +package cloudconnexa + +import ( + "fmt" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccCloudConnexaDnsRecord_basic(t *testing.T) { + resourceName := "cloudconnexa_dns_record.test" + domainName := "test.cloudconnexa.com" + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckCloudConnexaDnsRecordDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudConnexaDnsRecordConfig(domainName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "domain", domainName), + resource.TestCheckResourceAttr(resourceName, "description", "test description"), + resource.TestCheckResourceAttr(resourceName, "ip_v4_addresses.0", "192.168.1.1"), + resource.TestCheckResourceAttr(resourceName, "ip_v4_addresses.1", "192.168.1.2"), + resource.TestCheckResourceAttr(resourceName, "ip_v6_addresses.0", "2001:db8:85a3:0:0:8a2e:370:7334"), + resource.TestCheckResourceAttr(resourceName, "ip_v6_addresses.1", "2001:db8:85a3:0:0:8a2e:370:7335"), + ), + }, + }, + }) +} + +func testAccCheckCloudConnexaDnsRecordDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*cloudconnexa.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "cloudconnexa_dns_record" { + continue + } + + recordId := rs.Primary.ID + r, err := client.DnsRecords.GetDnsRecord(recordId) + + if err != nil { + return err + } + + if r != nil { + return fmt.Errorf("DNS record with ID '%s' still exists", recordId) + } + } + + return nil +} + +func testAccCloudConnexaDnsRecordConfig(domainName string) string { + return fmt.Sprintf(` +provider "cloudconnexa" { + base_url = "https://%[1]s.api.openvpn.com" +} + +resource "cloudconnexa_dns_record" "test" { + domain = "%[2]s" + description = "test description" + ip_v4_addresses = ["192.168.1.1", "192.168.1.2"] + ip_v6_addresses = ["2001:db8:85a3:0:0:8a2e:370:7334", "2001:db8:85a3:0:0:8a2e:370:7335"] +} +`, testCloudID, domainName) +} diff --git a/cloudconnexa/resource_host.go b/cloudconnexa/resource_host.go new file mode 100644 index 0000000..43d218e --- /dev/null +++ b/cloudconnexa/resource_host.go @@ -0,0 +1,268 @@ +package cloudconnexa + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + "hash/fnv" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceHost() *schema.Resource { + return &schema.Resource{ + Description: "Use `cloudconnexa_host` to create an OpenVPN Cloud host.", + CreateContext: resourceHostCreate, + ReadContext: resourceHostRead, + UpdateContext: resourceHostUpdate, + DeleteContext: resourceHostDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "The display name of the host.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Default: "Managed by Terraform", + ValidateFunc: validation.StringLenBetween(1, 120), + Description: "The description for the UI. Defaults to `Managed by Terraform`.", + }, + "internet_access": { + Type: schema.TypeString, + Optional: true, + Default: "LOCAL", + ValidateFunc: validation.StringInSlice([]string{"BLOCKED", "GLOBAL_INTERNET", "LOCAL"}, false), + Description: "The type of internet access provided. Valid values are `BLOCKED`, `GLOBAL_INTERNET`, or `LOCAL`. Defaults to `LOCAL`.", + }, + "system_subnets": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "The IPV4 and IPV6 subnets automatically assigned to this host.", + }, + "connector": { + Type: schema.TypeSet, + Required: true, + Set: func(i interface{}) int { + n := i.(map[string]interface{})["name"] + h := fnv.New32a() + h.Write([]byte(n.(string))) + return int(h.Sum32()) + }, + Description: "The set of connectors to be associated with this host. Can be defined more than once.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the connector associated with this host.", + }, + "vpn_region_id": { + Type: schema.TypeString, + Required: true, + Description: "The id of the region where the connector will be deployed.", + }, + "network_item_type": { + Type: schema.TypeString, + Computed: true, + Description: "The network object type. This typically will be set to `HOST`.", + }, + "network_item_id": { + Type: schema.TypeString, + Computed: true, + Description: "The host id.", + }, + "ip_v4_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IPV4 address of the connector.", + }, + "ip_v6_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IPV6 address of the connector.", + }, + "profile": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func resourceHostCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + var connectors []cloudconnexa.Connector + configConnectors := d.Get("connector").(*schema.Set) + for _, c := range configConnectors.List() { + connectors = append(connectors, cloudconnexa.Connector{ + Name: c.(map[string]interface{})["name"].(string), + VpnRegionId: c.(map[string]interface{})["vpn_region_id"].(string), + }) + } + h := cloudconnexa.Host{ + Name: d.Get("name").(string), + Description: d.Get("description").(string), + InternetAccess: d.Get("internet_access").(string), + Connectors: connectors, + } + host, err := c.Hosts.Create(h) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + d.SetId(host.Id) + diagnostics := setConnectorsList(d, c, host.Connectors) + if diagnostics != nil { + return diagnostics + } + + return append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "The connector for this host needs to be set up manually", + Detail: "Terraform only creates the OpenVPN Cloud connector object for this host, but additional manual steps are required to associate a host in your infrastructure with this connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", + }) +} + +func resourceHostRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + host, err := c.Hosts.Get(d.Id()) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if host == nil { + d.SetId("") + return diags + } + d.Set("name", host.Name) + d.Set("description", host.Description) + d.Set("internet_access", host.InternetAccess) + d.Set("system_subnets", host.SystemSubnets) + + diagnostics := setConnectorsList(d, c, host.Connectors) + if diagnostics != nil { + return diagnostics + } + return diags +} + +func resourceHostUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + if d.HasChange("connector") { + old, new := d.GetChange("connector") + oldSet := old.(*schema.Set) + newSet := new.(*schema.Set) + if oldSet.Len() == 0 && newSet.Len() > 0 { + // This happens when importing the resource + newConnector := cloudconnexa.Connector{ + Name: newSet.List()[0].(map[string]interface{})["name"].(string), + VpnRegionId: newSet.List()[0].(map[string]interface{})["vpn_region_id"].(string), + NetworkItemType: "HOST", + } + _, err := c.Connectors.Create(newConnector, d.Id()) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + } else { + for _, o := range oldSet.List() { + if !newSet.Contains(o) { + err := c.Connectors.Delete(o.(map[string]interface{})["id"].(string), d.Id(), "HOST") + if err != nil { + diags = append(diags, diag.FromErr(err)...) + } + } + } + for _, n := range newSet.List() { + if !oldSet.Contains(n) { + newConnector := cloudconnexa.Connector{ + Name: n.(map[string]interface{})["name"].(string), + VpnRegionId: n.(map[string]interface{})["vpn_region_id"].(string), + NetworkItemType: "HOST", + } + _, err := c.Connectors.Create(newConnector, d.Id()) + if err != nil { + diags = append(diags, diag.FromErr(err)...) + } + } + } + } + } + if d.HasChanges("name", "description", "internet_access") { + _, newName := d.GetChange("name") + _, newDescription := d.GetChange("description") + _, newAccess := d.GetChange("internet_access") + err := c.Hosts.Update(cloudconnexa.Host{ + Id: d.Id(), + Name: newName.(string), + Description: newDescription.(string), + InternetAccess: newAccess.(string), + }) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + } + return append(diags, resourceHostRead(ctx, d, m)...) +} + +func resourceHostDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + hostId := d.Id() + err := c.Hosts.Delete(hostId) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + return diags +} + +func setConnectorsList(data *schema.ResourceData, c *cloudconnexa.Client, connectors []cloudconnexa.Connector) diag.Diagnostics { + connectorsList := make([]interface{}, len(connectors)) + for i, connector := range connectors { + connectorsData, err := getConnectorsListItem(c, connector) + if err != nil { + return diag.FromErr(err) + } + connectorsList[i] = connectorsData + } + err := data.Set("connector", connectorsList) + if err != nil { + return diag.FromErr(err) + } + return nil +} + +func getConnectorsListItem(c *cloudconnexa.Client, connector cloudconnexa.Connector) (map[string]interface{}, error) { + connectorsData := map[string]interface{}{ + "id": connector.Id, + "name": connector.Name, + "vpn_region_id": connector.VpnRegionId, + "ip_v4_address": connector.IPv4Address, + "ip_v6_address": connector.IPv6Address, + "network_item_id": connector.NetworkItemId, + "network_item_type": connector.NetworkItemType, + } + + connectorProfile, err := c.Connectors.GetProfile(connector.Id) + if err != nil { + return nil, err + } + connectorsData["profile"] = connectorProfile + return connectorsData, nil +} diff --git a/cloudconnexa/resource_network.go b/cloudconnexa/resource_network.go new file mode 100644 index 0000000..978fb16 --- /dev/null +++ b/cloudconnexa/resource_network.go @@ -0,0 +1,352 @@ +package cloudconnexa + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceNetwork() *schema.Resource { + return &schema.Resource{ + Description: "Use `cloudconnexa_network` to create an OpenVPN Cloud Network.", + CreateContext: resourceNetworkCreate, + ReadContext: resourceNetworkRead, + UpdateContext: resourceNetworkUpdate, + DeleteContext: resourceNetworkDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + Description: "The display name of the network.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Default: "Managed by Terraform", + ValidateFunc: validation.StringLenBetween(1, 120), + Description: "The display description for this resource. Defaults to `Managed by Terraform`.", + }, + "egress": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Boolean to control whether this network provides an egress or not.", + }, + "internet_access": { + Type: schema.TypeString, + Optional: true, + Default: "LOCAL", + ValidateFunc: validation.StringInSlice([]string{"BLOCKED", "GLOBAL_INTERNET", "LOCAL"}, false), + Description: "The type of internet access provided. Valid values are `BLOCKED`, `GLOBAL_INTERNET`, or `LOCAL`. Defaults to `LOCAL`.", + }, + "system_subnets": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "The IPV4 and IPV6 subnets automatically assigned to this network.", + }, + "default_route": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Description: "The default route of this network.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Optional: true, + Default: "IP_V4", + ValidateFunc: validation.StringInSlice([]string{"IP_V4", "IP_V6"}, false), + Description: "The type of route. Valid values are `IP_V4`, `IP_V6`, and `DOMAIN`.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Default: "Managed by Terraform.", + Description: "The default route description.", + }, + "value": { + Type: schema.TypeString, + Required: true, + Description: "The target value of the default route.", + }, + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The ID of this resource.", + }, + }, + }, + }, + "default_connector": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Description: "The default connector of this network.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The ID of this resource.", + }, + "name": { + Type: schema.TypeString, + Required: true, + Description: "Name of the connector automatically created and attached to this network.", + }, + "vpn_region_id": { + Type: schema.TypeString, + Required: true, + Description: "The id of the region where the default connector will be deployed.", + }, + "network_item_type": { + Type: schema.TypeString, + Computed: true, + Description: "The network object type. This typically will be set to `NETWORK`.", + }, + "network_item_id": { + Type: schema.TypeString, + Computed: true, + Description: "The parent network id.", + }, + "ip_v4_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IPV4 address of the default connector.", + }, + "ip_v6_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IPV6 address of the default connector.", + }, + }, + }, + }, + }, + } +} + +func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + configConnector := d.Get("default_connector").([]interface{})[0].(map[string]interface{}) + connectors := []cloudconnexa.NetworkConnector{ + { + Name: configConnector["name"].(string), + VpnRegionId: configConnector["vpn_region_id"].(string), + }, + } + n := cloudconnexa.Network{ + Name: d.Get("name").(string), + Description: d.Get("description").(string), + Egress: d.Get("egress").(bool), + InternetAccess: d.Get("internet_access").(string), + Connectors: connectors, + } + network, err := c.Networks.Create(n) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + d.SetId(network.Id) + configRoute := d.Get("default_route").([]interface{})[0].(map[string]interface{}) + defaultRoute, err := c.Routes.Create(network.Id, cloudconnexa.Route{ + Type: configRoute["type"].(string), + Description: configRoute["description"].(string), + Subnet: configRoute["subnet"].(string), + }) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + defaultRouteWithIdSlice := make([]map[string]interface{}, 1) + defaultRouteWithIdSlice[0] = map[string]interface{}{ + "id": defaultRoute.Id, + "description": defaultRoute.Description, + "type": defaultRoute.Type, + "subnet": defaultRoute.Subnet, + } + d.Set("default_route", defaultRouteWithIdSlice) + return append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "The default connector for this network needs to be set up manually", + Detail: "Terraform only creates the OpenVPN Cloud default connector object for this network, but additional manual steps are required to associate a host in your infrastructure with this connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", + }) +} + +func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + network, err := c.Networks.Get(d.Id()) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if network == nil { + d.SetId("") + return diags + } + d.Set("name", network.Name) + d.Set("description", network.Description) + d.Set("egress", network.Egress) + d.Set("internet_access", network.InternetAccess) + d.Set("system_subnets", network.SystemSubnets) + if len(d.Get("default_connector").([]interface{})) > 0 { + configConnector := d.Get("default_connector").([]interface{})[0].(map[string]interface{}) + connectorName := configConnector["name"].(string) + networkConnectors, err := c.Connectors.GetByNetworkID(network.Id) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + err = d.Set("default_connector", getConnectorSlice(networkConnectors, network.Id, connectorName)) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + } + if len(d.Get("default_route").([]interface{})) > 0 { + configRoute := d.Get("default_route").([]interface{})[0].(map[string]interface{}) + route, err := c.Routes.GetNetworkRoute(d.Id(), configRoute["id"].(string)) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if route == nil { + d.Set("default_route", []map[string]interface{}{}) + } else { + defaultRoute := []map[string]interface{}{ + { + "id": configRoute["id"].(string), + "type": route.Type, + "description": route.Description, + }, + } + if route.Type == "IP_V4" || route.Type == "IP_V6" { + defaultRoute[0]["value"] = route.Subnet + } + d.Set("default_route", defaultRoute) + } + } + return diags +} + +func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + if d.HasChange("default_connector") { + old, new := d.GetChange("default_connector") + oldSlice := old.([]interface{}) + newSlice := new.([]interface{}) + if len(oldSlice) == 0 && len(newSlice) == 1 { + // This happens when importing the resource + newConnector := cloudconnexa.Connector{ + Name: newSlice[0].(map[string]interface{})["name"].(string), + VpnRegionId: newSlice[0].(map[string]interface{})["vpn_region_id"].(string), + NetworkItemType: "NETWORK", + } + _, err := c.Connectors.Create(newConnector, d.Id()) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + } else { + oldMap := oldSlice[0].(map[string]interface{}) + newMap := newSlice[0].(map[string]interface{}) + if oldMap["name"].(string) != newMap["name"].(string) || oldMap["vpn_region_id"].(string) != newMap["vpn_region_id"].(string) { + newConnector := cloudconnexa.Connector{ + Name: newMap["name"].(string), + VpnRegionId: newMap["vpn_region_id"].(string), + NetworkItemType: "NETWORK", + } + _, err := c.Connectors.Create(newConnector, d.Id()) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if len(oldMap["id"].(string)) > 0 { + // This can sometimes happen when importing the resource + err = c.Connectors.Delete(oldMap["id"].(string), d.Id(), oldMap["network_item_type"].(string)) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + } + } + } + } + if d.HasChange("default_route") { + old, new := d.GetChange("default_route") + oldSlice := old.([]interface{}) + newSlice := new.([]interface{}) + if len(oldSlice) == 0 && len(newSlice) == 1 { + // This happens when importing the resource + newMap := newSlice[0].(map[string]interface{}) + routeType := newMap["type"] + routeDesc := newMap["description"] + routeSubnet := newMap["subnet"] + route := cloudconnexa.Route{ + Type: routeType.(string), + Description: routeDesc.(string), + Subnet: routeSubnet.(string), + } + defaultRoute, err := c.Routes.Create(d.Id(), route) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + defaultRouteWithIdSlice := make([]map[string]interface{}, 1) + defaultRouteWithIdSlice[0] = map[string]interface{}{ + "id": defaultRoute.Id, + "description": defaultRoute.Description, + } + err = d.Set("default_route", defaultRouteWithIdSlice) + if err != nil { + diags = append(diags, diag.FromErr(err)...) + } + } else { + newMap := newSlice[0].(map[string]interface{}) + routeId := newMap["id"] + routeType := newMap["type"] + routeDesc := newMap["description"] + routeSubnet := newMap["subnet"] + route := cloudconnexa.Route{ + Id: routeId.(string), + Type: routeType.(string), + Description: routeDesc.(string), + Subnet: routeSubnet.(string), + } + err := c.Routes.Update(d.Id(), route) + if err != nil { + diags = append(diags, diag.FromErr(err)...) + } + } + } + if d.HasChanges("name", "description", "internet_access", "egress") { + _, newName := d.GetChange("name") + _, newDescription := d.GetChange("description") + _, newEgress := d.GetChange("egress") + _, newAccess := d.GetChange("internet_access") + err := c.Networks.Update(cloudconnexa.Network{ + Id: d.Id(), + Name: newName.(string), + Description: newDescription.(string), + Egress: newEgress.(bool), + InternetAccess: newAccess.(string), + }) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + } + return append(diags, resourceNetworkRead(ctx, d, m)...) +} + +func resourceNetworkDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + networkId := d.Id() + err := c.Networks.Delete(networkId) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + return diags +} diff --git a/cloudconnexa/resource_route.go b/cloudconnexa/resource_route.go new file mode 100644 index 0000000..5a1d393 --- /dev/null +++ b/cloudconnexa/resource_route.go @@ -0,0 +1,128 @@ +package cloudconnexa + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func resourceRoute() *schema.Resource { + return &schema.Resource{ + Description: "Use `cloudconnexa_route` to create a route on an OpenVPN Cloud network.", + CreateContext: resourceRouteCreate, + UpdateContext: resourceRouteUpdate, + ReadContext: resourceRouteRead, + DeleteContext: resourceRouteDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"IP_V4", "IP_V6"}, false), + Description: "The type of route. Valid values are `IP_V4`, `IP_V6`, and `DOMAIN`.", + }, + "value": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The target value of the default route.", + }, + "network_item_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The id of the network on which to create the route.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Default: "Managed by Terraform", + }, + }, + } +} + +func resourceRouteCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + networkItemId := d.Get("network_item_id").(string) + routeType := d.Get("type").(string) + routeSubnet := d.Get("subnet").(string) + descriptionValue := d.Get("description").(string) + r := cloudconnexa.Route{ + Type: routeType, + Subnet: routeSubnet, + Description: descriptionValue, + } + route, err := c.Routes.Create(networkItemId, r) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + d.SetId(route.Id) + if routeType == "IP_V4" || routeType == "IP_V6" { + d.Set("value", route.Subnet) + } + return diags +} + +func resourceRouteRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + routeId := d.Id() + r, err := c.Routes.Get(routeId) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if r == nil { + d.SetId("") + } else { + d.Set("type", r.Type) + if r.Type == "IP_V4" || r.Type == "IP_V6" { + d.Set("value", r.Subnet) + } + d.Set("description", r.Description) + d.Set("network_item_id", r.NetworkItemId) + } + return diags +} + +func resourceRouteUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + if !d.HasChanges("description", "value") { + return diags + } + + networkItemId := d.Get("network_item_id").(string) + _, description := d.GetChange("description") + _, subnet := d.GetChange("subnet") + r := cloudconnexa.Route{ + Id: d.Id(), + Description: description.(string), + Subnet: subnet.(string), + } + + err := c.Routes.Update(networkItemId, r) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + return diags +} + +func resourceRouteDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + routeId := d.Id() + networkItemId := d.Get("network_item_id").(string) + err := c.Routes.Delete(networkItemId, routeId) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + return diags +} diff --git a/cloudconnexa/resource_route_test.go b/cloudconnexa/resource_route_test.go new file mode 100644 index 0000000..1b17892 --- /dev/null +++ b/cloudconnexa/resource_route_test.go @@ -0,0 +1,132 @@ +package cloudconnexa + +import ( + "errors" + "fmt" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/stretchr/testify/require" +) + +func TestAccCloudConnexaRoute_basic(t *testing.T) { + rn := "cloudconnexa_route.test" + ip, err := acctest.RandIpAddress("10.0.0.0/8") + require.NoError(t, err) + route := cloudconnexa.Route{ + Description: "test" + acctest.RandString(10), + Type: "IP_V4", + Subnet: ip + "/32", + } + routeChanged := route + routeChanged.Description = acctest.RandStringFromCharSet(10, alphabet) + networkRandString := "test" + acctest.RandString(10) + var routeId string + + check := func(r cloudconnexa.Route) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + testAccCheckCloudConnexaRouteExists(rn, &routeId), + resource.TestCheckResourceAttr(rn, "description", r.Description), + resource.TestCheckResourceAttr(rn, "type", r.Type), + resource.TestCheckResourceAttr(rn, "value", r.Subnet), + ) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckCloudConnexaRouteDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudConnexaRouteConfig(route, networkRandString), + Check: check(route), + }, + { + Config: testAccCloudConnexaRouteConfig(routeChanged, networkRandString), + Check: check(routeChanged), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateIdFunc: testAccCloudConnexaRouteImportStateIdFunc(rn), + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckCloudConnexaRouteDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*cloudconnexa.Client) + for _, rs := range s.RootModule().Resources { + if rs.Type != "cloudconnexa_route" { + continue + } + routeId := rs.Primary.ID + r, err := client.Routes.Get(routeId) + if err == nil { + return err + } + if r != nil { + return errors.New("route still exists") + } + } + return nil +} + +func testAccCheckCloudConnexaRouteExists(n string, routeID *string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("not found: %s", n) + } + + if rs.Primary.ID == "" { + return errors.New("no ID is set") + } + + client := testAccProvider.Meta().(*cloudconnexa.Client) + _, err := client.Routes.Get(rs.Primary.ID) + if err != nil { + return err + } + return nil + } +} + +func testAccCloudConnexaRouteImportStateIdFunc(n string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[n] + if !ok { + return "", fmt.Errorf("not found: %s", n) + } + return rs.Primary.ID, nil + } +} + +func testAccCloudConnexaRouteConfig(r cloudconnexa.Route, networkRandStr string) string { + return fmt.Sprintf(` +provider "cloudconnexa" { + base_url = "https://%[1]s.api.openvpn.com" +} +resource "cloudconnexa_network" "test" { + name = "%[5]s" + default_connector { + name = "%[5]s" + vpn_region_id = "fi-hel" + } + default_route { + value = "10.1.2.0/24" + type = "IP_V4" + } +} +resource "cloudconnexa_route" "test" { + network_item_id = cloudconnexa_network.test.id + description = "%[2]s" + value = "%[3]s" + type = "%[4]s" +} +`, testCloudID, r.Description, r.Subnet, r.Type, networkRandStr) +} diff --git a/cloudconnexa/resource_service.go b/cloudconnexa/resource_service.go new file mode 100644 index 0000000..b17fae9 --- /dev/null +++ b/cloudconnexa/resource_service.go @@ -0,0 +1,282 @@ +package cloudconnexa + +import ( + "context" + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" +) + +var ( + validValues = []string{"ANY", "BGP", "CUSTOM", "DHCP", "DNS", "FTP", "HTTP", "HTTPS", "IMAP", "IMAPS", "NTP", "POP3", "POP3S", "SMTP", "SMTPS", "SNMP", "SSH", "TELNET", "TFTP"} +) + +func resourceIPService() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceServiceCreate, + ReadContext: resourceServiceRead, + DeleteContext: resourceServiceDelete, + UpdateContext: resourceServiceUpdate, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "description": { + Type: schema.TypeString, + Default: "Created by Terraform OpenVPN Cloud Provider", + ValidateFunc: validation.StringLenBetween(1, 255), + Optional: true, + }, + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"IP_SOURCE", "SERVICE_DESTINATION"}, false), + }, + "routes": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "config": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: resourceServiceConfig(), + }, + "network_item_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"NETWORK", "HOST"}, false), + }, + "network_item_id": { + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func resourceServiceUpdate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { + c := i.(*cloudconnexa.Client) + networkItemId := data.Get("network_item_id").(string) + networkItemType := data.Get("network_item_type").(string) + + s, err := c.UpdateIPService(data.Id(), networkItemType, networkItemId, resourceDataToService(data)) + if err != nil { + return diag.FromErr(err) + } + + setResourceData(data, s) + return nil +} + +func resourceServiceConfig() *schema.Resource { + return &schema.Resource{ + Schema: map[string]*schema.Schema{ + "custom_service_types": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "icmp_type": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "lower_value": { + Type: schema.TypeInt, + Required: true, + }, + "upper_value": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + }, + }, + }, + "service_types": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: func(i interface{}, path cty.Path) diag.Diagnostics { + + val := i.(string) + for _, validValue := range validValues { + if val == validValue { + return nil + } + } + return diag.Errorf("service type must be one of %s", validValues) + }, + }, + }, + }, + } +} + +func resourceServiceRead(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { + c := i.(*cloudconnexa.Client) + service, err := c.GetServiceIPByID( + data.Id(), + ) + + if err != nil { + return diag.FromErr(err) + } + setResourceData(data, service) + return nil +} + +func setResourceData(data *schema.ResourceData, service *client.IPService) { + data.SetId(service.Id) + _ = data.Set("name", service.Name) + _ = data.Set("description", service.Description) + _ = data.Set("type", service.Type) + _ = data.Set("routes", flattenRoutes(service.Routes)) + _ = data.Set("config", flattenServiceConfig(service.Config)) + _ = data.Set("network_item_type", service.NetworkItemType) + _ = data.Set("network_item_id", service.NetworkItemId) +} + +func resourceServiceDelete(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { + c := i.(*cloudconnexa.Client) + err := c.DeleteIPService( + data.Id(), + ) + if err != nil { + return diag.FromErr(err) + } + return nil +} + +func flattenServiceConfig(config *client.ServiceConfig) interface{} { + var data = map[string]interface{}{ + "custom_service_types": flattenCustomServiceTypes(config.CustomServiceTypes), + "service_types": config.ServiceTypes, + } + return []interface{}{data} +} + +func flattenCustomServiceTypes(types []*client.CustomIPServiceType) interface{} { + var data []interface{} + for _, t := range types { + data = append( + data, + map[string]interface{}{ + "icmp_type": flattenIcmpType(t.IcmpType), + }, + ) + } + return data +} + +func flattenIcmpType(icmpType []client.Range) interface{} { + var data []interface{} + for _, t := range icmpType { + data = append( + data, + map[string]interface{}{ + "lower_value": t.LowerValue, + "upper_value": t.UpperValue, + }, + ) + } + return data +} + +func flattenRoutes(routes []*client.Route) []string { + var data []string + for _, route := range routes { + data = append( + data, + route.Domain, + ) + } + return data +} + +func resourceServiceCreate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { + c := i.(*cloudconnexa.Client) + var diags diag.Diagnostics + + service, err := c.CreateService(resourceDataToService(data)) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + + data.SetId(service.Id) + setResourceData(data, service) + return diags +} + +func resourceDataToService(data *schema.ResourceData) *client.Service { + routes := data.Get("routes").([]interface{}) + var configRoutes []*client.Route + for _, r := range routes { + configRoutes = append( + configRoutes, + &client.Route{ + Value: r.(string), + }, + ) + } + + config := client.ServiceConfig{} + configList := data.Get("config").([]interface{}) + if len(configList) > 0 && configList[0] != nil { + + config.CustomServiceTypes = []*client.CustomIPServiceType{} + config.ServiceTypes = []string{} + + mainConfig := configList[0].(map[string]interface{}) + for _, r := range mainConfig["custom_service_types"].([]interface{}) { + cst := r.(map[string]interface{}) + var icmpTypes []client.Range + for _, r := range cst["icmp_type"].([]interface{}) { + icmpType := r.(map[string]interface{}) + icmpTypes = append( + icmpTypes, + client.Range{ + LowerValue: icmpType["lower_value"].(int), + UpperValue: icmpType["upper_value"].(int), + }, + ) + } + config.CustomServiceTypes = append( + config.CustomServiceTypes, + &client.CustomIPServiceType{ + IcmpType: icmpTypes, + }, + ) + } + + for _, r := range mainConfig["service_types"].([]interface{}) { + config.ServiceTypes = append(config.ServiceTypes, r.(string)) + } + } + + s := &client.Service{ + Name: data.Get("name").(string), + Description: data.Get("description").(string), + NetworkItemId: data.Get("network_item_id").(string), + NetworkItemType: data.Get("network_item_type").(string), + Type: data.Get("type").(string), + Routes: configRoutes, + Config: &config, + } + return s +} diff --git a/cloudconnexa/resource_service_test.go b/cloudconnexa/resource_service_test.go new file mode 100644 index 0000000..619d3b9 --- /dev/null +++ b/cloudconnexa/resource_service_test.go @@ -0,0 +1,113 @@ +package cloudconnexa + +import ( + "errors" + "fmt" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "testing" + + "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccCloudConnexaService_basic(t *testing.T) { + rn := "cloudconnexa_service.test" + networkName := acctest.RandStringFromCharSet(10, alphabet) + service := client.Service{ + Name: acctest.RandStringFromCharSet(10, alphabet), + } + serviceChanged := service + serviceChanged.Name = fmt.Sprintf("changed-%s", acctest.RandStringFromCharSet(10, alphabet)) + + check := func(service client.Service) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + testAccCheckCloudConnexaServiceExists(rn, networkName), + resource.TestCheckResourceAttr(rn, "name", service.Name), + ) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckCloudConnexaServiceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudConnexaServiceConfig(service, networkName), + Check: check(service), + }, + { + Config: testAccCloudConnexaServiceConfig(serviceChanged, networkName), + Check: check(serviceChanged), + }, + }, + }) +} + +func testAccCheckCloudConnexaServiceExists(rn, networkId string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[rn] + if !ok { + return fmt.Errorf("not found: %s", rn) + } + + if rs.Primary.ID == "" { + return errors.New("no ID is set") + } + + c := testAccProvider.Meta().(*client.Client) + _, err := c.GetService(rs.Primary.ID, rs.Primary.Attributes["network_item_type"], rs.Primary.Attributes["network_item_id"]) + if err != nil { + return err + } + return nil + } +} + +func testAccCheckCloudConnexaServiceDestroy(state *terraform.State) error { + c := testAccProvider.Meta().(*client.Client) + for _, rs := range state.RootModule().Resources { + if rs.Type != "cloudconnexa_service" { + continue + } + id := rs.Primary.Attributes["id"] + s, err := c.GetService(id, "c63acae0-b569-4116-9b39-921c1dee62d2", "NETWORK") + if err == nil || s != nil { + return fmt.Errorf("service still exists") + } + } + return nil +} + +func testAccCloudConnexaServiceConfig(service client.Service, networkName string) string { + return fmt.Sprintf(` +provider "cloudconnexa" { + base_url = "https://%s.api.openvpn.com" +} + +resource "cloudconnexa_network" "test" { + name = "%s" + + default_connector { + name = "%s" + vpn_region_id = "fi-hel" + } + default_route { + value = "10.1.2.0/24" + type = "IP_V4" + } +} + +resource "cloudconnexa_service" "test" { + name = "%s" + type = "SERVICE_DESTINATION" + description = "test" + network_item_type = "NETWORK" + network_item_id = cloudconnexa_network.test.id + routes = ["test.ua" ] + config { + service_types = ["ANY"] + } +} +`, testCloudID, networkName, fmt.Sprintf("connector_%s", networkName), service.Name) +} diff --git a/cloudconnexa/resource_user.go b/cloudconnexa/resource_user.go new file mode 100644 index 0000000..68e758b --- /dev/null +++ b/cloudconnexa/resource_user.go @@ -0,0 +1,226 @@ +package cloudconnexa + +import ( + "context" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" +) + +func resourceUser() *schema.Resource { + return &schema.Resource{ + Description: "Use `cloudconnexa_user` to create an OpenVPN Cloud user.", + CreateContext: resourceUserCreate, + ReadContext: resourceUserRead, + UpdateContext: resourceUserUpdate, + DeleteContext: resourceUserDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "username": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 120), + Description: "A username for the user.", + }, + "email": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 120), + Description: "An invitation to OpenVPN cloud account will be sent to this email. It will include an initial password and a VPN setup guide.", + }, + "first_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 20), + Description: "User's first name.", + }, + "last_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 20), + Description: "User's last name.", + }, + "group_id": { + Type: schema.TypeString, + Optional: true, + Description: "The UUID of a user's group.", + }, + "role": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "MEMBER", + Description: "The type of user role. Valid values are `ADMIN`, `MEMBER`, or `OWNER`.", + }, + "devices": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Description: "When a user signs in, the device that they use will be added to their account. You can read more at [OpenVPN Cloud Device](https://openvpn.net/cloud-docs/device/).", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 32), + Description: "A device name.", + }, + "description": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 120), + Description: "A device description.", + }, + "ipv4_address": { + Type: schema.TypeString, + Optional: true, + Description: "An IPv4 address of the device.", + }, + "ipv6_address": { + Type: schema.TypeString, + Optional: true, + Description: "An IPv6 address of the device.", + }, + }, + }, + }, + }, + } +} + +func resourceUserCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + username := d.Get("username").(string) + email := d.Get("email").(string) + firstName := d.Get("first_name").(string) + lastName := d.Get("last_name").(string) + groupId := d.Get("group_id").(string) + role := d.Get("role").(string) + configDevices := d.Get("devices").([]interface{}) + var devices []cloudconnexa.Device + for _, d := range configDevices { + device := d.(map[string]interface{}) + devices = append( + devices, + cloudconnexa.Device{ + Name: device["name"].(string), + Description: device["description"].(string), + IPv4Address: device["ipv4_address"].(string), + IPv6Address: device["ipv6_address"].(string), + }, + ) + + } + u := cloudconnexa.User{ + Username: username, + Email: email, + FirstName: firstName, + LastName: lastName, + GroupId: groupId, + Devices: devices, + Role: role, + } + user, err := c.Users.Create(u) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + d.SetId(user.Id) + return append(diags, diag.Diagnostic{ + Severity: diag.Warning, + Summary: "The user's role cannot be changed using the code.", + Detail: "There is a bug in OpenVPN Cloud API that prevents setting the user's role during the creation. All users are created as Members by default. Once it's fixed, the provider will be updated accordingly.", + }) +} + +func resourceUserRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + userId := d.Id() + u, err := c.Users.Get(userId) + + // If group_id is not set, OpenVPN cloud sets it to the default group. + var groupId string + if d.Get("group_id") == "" { + // The group has not been explicitly set. + // Set it to an empty string to keep the default group. + groupId = "" + } else { + groupId = u.GroupId + } + + if err != nil { + return append(diags, diag.FromErr(err)...) + } + if u == nil { + d.SetId("") + } else { + d.Set("username", u.Username) + d.Set("email", u.Email) + d.Set("first_name", u.FirstName) + d.Set("last_name", u.LastName) + d.Set("group_id", groupId) + d.Set("devices", u.Devices) + d.Set("role", u.Role) + } + return diags +} + +func resourceUserUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + if !d.HasChanges("first_name", "last_name", "group_id", "email") { + return diags + } + + u, err := c.Users.Get(d.Id()) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + + _, email := d.GetChange("email") + _, firstName := d.GetChange("first_name") + _, lastName := d.GetChange("last_name") + _, role := d.GetChange("role") + status := u.Status + oldGroupId, newGroupId := d.GetChange("group_id") + + groupId := newGroupId.(string) + // If both are empty strings, then the group has not been set explicitly. + // The update endpoint requires group_id to be set, so we should set it to the default group. + if oldGroupId.(string) == "" && groupId == "" { + g, err := c.UserGroups.GetByName("Default") + if err != nil { + return append(diags, diag.FromErr(err)...) + } + groupId = g.ID + } + + err = c.Users.Update(cloudconnexa.User{ + Id: d.Id(), + Email: email.(string), + FirstName: firstName.(string), + LastName: lastName.(string), + GroupId: groupId, + Role: role.(string), + Status: status, + }) + + return append(diags, diag.FromErr(err)...) +} + +func resourceUserDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + userId := d.Id() + err := c.Users.Delete(userId) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + return diags +} diff --git a/cloudconnexa/resource_user_group.go b/cloudconnexa/resource_user_group.go new file mode 100644 index 0000000..da8e400 --- /dev/null +++ b/cloudconnexa/resource_user_group.go @@ -0,0 +1,168 @@ +package cloudconnexa + +import ( + "context" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" +) + +func resourceUserGroup() *schema.Resource { + return &schema.Resource{ + Description: "Use `cloudconnexa_user_group` to create an OpenVPN Cloud user group.", + CreateContext: resourceUserGroupCreate, + ReadContext: resourceUserGroupRead, + UpdateContext: resourceUserGroupUpdate, + DeleteContext: resourceUserGroupDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The ID of the user group.", + }, + "connect_auth": { + Type: schema.TypeString, + Optional: true, + Default: "AUTO", + ValidateFunc: validation.StringInSlice([]string{"AUTH", "AUTO", "STRICT_AUTH"}, false), + }, + "internet_access": { + Type: schema.TypeString, + Optional: true, + Default: "LOCAL", + ValidateFunc: validation.StringInSlice([]string{"LOCAL", "BLOCKED", "GLOBAL_INTERNET"}, false), + }, + "max_device": { + Type: schema.TypeInt, + Optional: true, + Default: 3, + Description: "The maximum number of devices that can be connected to the user group.", + }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 40), + Description: "The name of the user group.", + }, + "system_subnets": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Default: nil, + Description: "A list of subnets that are accessible to the user group.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "vpn_region_ids": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + Description: "A list of VPN regions that are accessible to the user group.", + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func resourceUserGroupUpdate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { + c := i.(*cloudconnexa.Client) + var diags diag.Diagnostics + ug := resourceDataToUserGroup(data) + + userGroup, err := c.UserGroups.Update(data.Id(), ug) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + + if userGroup == nil { + data.SetId("") + } else { + updateUserGroupData(data, userGroup) + } + + return diags +} + +func resourceDataToUserGroup(data *schema.ResourceData) *cloudconnexa.UserGroup { + name := data.Get("name").(string) + connectAuth := data.Get("connect_auth").(string) + maxDevice := data.Get("max_device").(int) + internetAccess := data.Get("internet_access").(string) + configSystemSubnets := data.Get("system_subnets").([]interface{}) + var systemSubnets []string + for _, s := range configSystemSubnets { + systemSubnets = append(systemSubnets, s.(string)) + } + configVpnRegionIds := data.Get("vpn_region_ids").([]interface{}) + var vpnRegionIds []string + for _, r := range configVpnRegionIds { + vpnRegionIds = append(vpnRegionIds, r.(string)) + } + + ug := &cloudconnexa.UserGroup{ + Name: name, + ConnectAuth: connectAuth, + MaxDevice: maxDevice, + SystemSubnets: systemSubnets, + VpnRegionIds: vpnRegionIds, + InternetAccess: internetAccess, + } + return ug +} + +func updateUserGroupData(data *schema.ResourceData, userGroup *cloudconnexa.UserGroup) { + data.SetId(userGroup.ID) + _ = data.Set("connect_auth", userGroup.ConnectAuth) + _ = data.Set("max_device", userGroup.MaxDevice) + _ = data.Set("name", userGroup.Name) + _ = data.Set("system_subnets", userGroup.SystemSubnets) + _ = data.Set("vpn_region_ids", userGroup.VpnRegionIds) + _ = data.Set("internet_access", userGroup.InternetAccess) +} + +func resourceUserGroupDelete(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { + c := i.(*cloudconnexa.Client) + var diags diag.Diagnostics + err := c.UserGroups.Delete(data.Id()) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + data.SetId("") + return diags +} + +func resourceUserGroupRead(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { + c := i.(*cloudconnexa.Client) + var diags diag.Diagnostics + userGroup, err := c.UserGroups.Get(data.Id()) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + + if userGroup == nil { + data.SetId("") + } else { + updateUserGroupData(data, userGroup) + } + return diags +} + +func resourceUserGroupCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*cloudconnexa.Client) + var diags diag.Diagnostics + ug := resourceDataToUserGroup(d) + + userGroup, err := c.UserGroups.Create(ug) + if err != nil { + return append(diags, diag.FromErr(err)...) + } + updateUserGroupData(d, userGroup) + return diags +} diff --git a/cloudconnexa/resource_user_group_test.go b/cloudconnexa/resource_user_group_test.go new file mode 100644 index 0000000..abc19b6 --- /dev/null +++ b/cloudconnexa/resource_user_group_test.go @@ -0,0 +1,107 @@ +package cloudconnexa + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccCloudConnexaUserGroup_basic(t *testing.T) { + rn := "cloudconnexa_user_group.test" + userGroup := cloudconnexa.UserGroup{ + Name: acctest.RandStringFromCharSet(10, alphabet), + VpnRegionIds: []string{ + "us-east-1", + }, + } + userGroupChanged := userGroup + userGroupChanged.Name = fmt.Sprintf("changed-%s", acctest.RandStringFromCharSet(10, alphabet)) + + check := func(userGroup cloudconnexa.UserGroup) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + testAccCheckCloudConnexaUserGroupExists(rn), + resource.TestCheckResourceAttr(rn, "name", userGroup.Name), + resource.TestCheckResourceAttr(rn, "vpn_region_ids.0", userGroup.VpnRegionIds[0]), + ) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckCloudConnexaUserGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudConnexaUserGroupConfig(userGroup), + Check: check(userGroup), + }, + { + Config: testAccCloudConnexaUserGroupConfig(userGroupChanged), + Check: check(userGroupChanged), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateIdFunc: testAccCloudConnexaUserImportStateIdFunc(rn), + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckCloudConnexaUserGroupDestroy(s *terraform.State) error { + c := testAccProvider.Meta().(*cloudconnexa.Client) + for _, rs := range s.RootModule().Resources { + if rs.Type != "cloudconnexa_user_group" { + continue + } + username := rs.Primary.Attributes["username"] + u, err := c.UserGroups.GetByName(username) + if err == nil { + if u != nil { + return errors.New("user still exists") + } + } + } + return nil +} + +func testAccCheckCloudConnexaUserGroupExists(rn string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[rn] + if !ok { + return fmt.Errorf("not found: %s", rn) + } + + if rs.Primary.ID == "" { + return errors.New("no ID is set") + } + + c := testAccProvider.Meta().(*cloudconnexa.Client) + _, err := c.UserGroups.Get(rs.Primary.ID) + if err != nil { + return err + } + return nil + } +} + +func testAccCloudConnexaUserGroupConfig(userGroup cloudconnexa.UserGroup) string { + idsStr, _ := json.Marshal(userGroup.VpnRegionIds) + + return fmt.Sprintf(` +provider "cloudconnexa" { + base_url = "https://%s.api.openvpn.com" +} +resource "cloudconnexa_user_group" "test" { + name = "%s" + vpn_region_ids = %s + +} +`, testCloudID, userGroup.Name, idsStr) +} diff --git a/cloudconnexa/resource_user_test.go b/cloudconnexa/resource_user_test.go new file mode 100644 index 0000000..7095b73 --- /dev/null +++ b/cloudconnexa/resource_user_test.go @@ -0,0 +1,118 @@ +package cloudconnexa + +import ( + "errors" + "fmt" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccCloudConnexaUser_basic(t *testing.T) { + rn := "cloudconnexa_user.test" + user := cloudconnexa.User{ + Username: acctest.RandStringFromCharSet(10, alphabet), + FirstName: acctest.RandStringFromCharSet(10, alphabet), + LastName: acctest.RandStringFromCharSet(10, alphabet), + Email: fmt.Sprintf("terraform-tests+%s@devopenvpn.in", acctest.RandString(10)), + } + userChanged := user + userChanged.Email = fmt.Sprintf("terraform-tests+changed%s@devopenvpn.in", acctest.RandString(10)) + var userID string + + check := func(user cloudconnexa.User) resource.TestCheckFunc { + return resource.ComposeTestCheckFunc( + testAccCheckCloudConnexaUserExists(rn, &userID), + resource.TestCheckResourceAttr(rn, "username", user.Username), + resource.TestCheckResourceAttr(rn, "email", user.Email), + resource.TestCheckResourceAttr(rn, "first_name", user.FirstName), + resource.TestCheckResourceAttr(rn, "last_name", user.LastName), + ) + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckCloudConnexaUserDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCloudConnexaUserConfig(user), + Check: check(user), + }, + { + Config: testAccCloudConnexaUserConfig(userChanged), + Check: check(userChanged), + }, + { + ResourceName: rn, + ImportState: true, + ImportStateIdFunc: testAccCloudConnexaUserImportStateIdFunc(rn), + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckCloudConnexaUserDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*cloudconnexa.Client) + for _, rs := range s.RootModule().Resources { + if rs.Type != "cloudconnexa_user" { + continue + } + username := rs.Primary.Attributes["username"] + u, err := client.Users.Get(username) + if err == nil { + if u != nil { + return errors.New("user still exists") + } + } + } + return nil +} + +func testAccCheckCloudConnexaUserExists(n string, teamID *string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("not found: %s", n) + } + + if rs.Primary.ID == "" { + return errors.New("no ID is set") + } + + client := testAccProvider.Meta().(*cloudconnexa.Client) + _, err := client.Users.Get(rs.Primary.ID) + if err != nil { + return err + } + return nil + } +} + +func testAccCloudConnexaUserImportStateIdFunc(n string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[n] + if !ok { + return "", fmt.Errorf("not found: %s", n) + } + return rs.Primary.ID, nil + } +} + +func testAccCloudConnexaUserConfig(user cloudconnexa.User) string { + return fmt.Sprintf(` +provider "cloudconnexa" { + base_url = "https://%s.api.openvpn.com" +} +resource "cloudconnexa_user" "test" { + username = "%s" + email = "%s" + first_name = "%s" + last_name = "%s" +} +`, testCloudID, user.Username, user.Email, user.FirstName, user.LastName) +} diff --git a/internal/cloudconnexaclient/cloudconnexaclient.go b/internal/cloudconnexaclient/cloudconnexaclient.go new file mode 100644 index 0000000..2ceacbf --- /dev/null +++ b/internal/cloudconnexaclient/cloudconnexaclient.go @@ -0,0 +1,29 @@ +package cloudconnexaclient + +import ( + "context" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" +) + +type Config struct { + UserAgent string + BaseURL string + ClientID string + ClientSecret string +} + +func (c *Config) Client(ctx context.Context) (*cloudconnexa.Client, error) { + var cl *cloudconnexa.Client + var err error + if err != nil { + return nil, err + } + cl, err = cloudconnexa.NewClient("", "", "") + if err != nil { + return nil, err + } + + cl.UserAgent = c.UserAgent + + return cl, nil +} diff --git a/main.go b/main.go index 80e7bc3..3146076 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,6 @@ package main import ( "github.com/OpenVPN/terraform-provider-openvpn-cloud/cloudconnexa" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/plugin" ) From aff6e518720d6a6520f53f87e70e664ac99caef0 Mon Sep 17 00:00:00 2001 From: Denis Arslanbekov Date: Sun, 14 Jan 2024 18:00:34 +0000 Subject: [PATCH 15/16] Use new name (cloudconnexa instead openvpncloud) --- README.md | 10 +++---- cloudconnexa/data_source_network.go | 2 +- cloudconnexa/data_source_user.go | 2 +- cloudconnexa/data_source_user_group.go | 2 +- cloudconnexa/data_source_vpn_region.go | 2 +- cloudconnexa/resource_connector.go | 4 +-- cloudconnexa/resource_host.go | 4 +-- cloudconnexa/resource_network.go | 4 +-- cloudconnexa/resource_route.go | 2 +- cloudconnexa/resource_service.go | 28 +++++++++++------- cloudconnexa/resource_service_test.go | 5 ++-- cloudconnexa/resource_user.go | 10 +++---- cloudconnexa/resource_user_group.go | 2 +- docs/data-sources/connector.md | 4 +-- docs/data-sources/host.md | 4 +-- docs/data-sources/network.md | 4 +-- docs/data-sources/network_routes.md | 6 ++-- docs/data-sources/user.md | 4 +-- docs/data-sources/user_group.md | 4 +-- docs/data-sources/vpn_region.md | 4 +-- docs/index.md | 2 +- docs/resources/connector.md | 8 ++--- docs/resources/host.md | 4 +-- docs/resources/network.md | 4 +-- docs/resources/route.md | 4 +-- docs/resources/user.md | 8 ++--- e2e/integration_test.go | 2 +- .../cloudconnexaclient/cloudconnexaclient.go | 29 ------------------- templates/data-sources/host.md | 4 +-- templates/data-sources/network.md | 4 +-- templates/data-sources/network_routes.md | 6 ++-- templates/data-sources/user.md | 4 +-- templates/index.md.tmpl | 6 ++-- templates/resources/connector.md | 8 ++--- templates/resources/host.md | 4 +-- templates/resources/network.md | 4 +-- templates/resources/route.md | 4 +-- templates/resources/user.md | 8 ++--- 38 files changed, 99 insertions(+), 121 deletions(-) delete mode 100644 internal/cloudconnexaclient/cloudconnexaclient.go diff --git a/README.md b/README.md index 45fd151..e13399b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Terraform Provider OpenVPN Cloud +# Terraform Provider Cloud Connexa Terraform @@ -12,18 +12,18 @@ OpenVPN -- [Website OpenVPN Cloud](https://openvpn.net/cloud-vpn/?utm_source=terraform&utm_medium=docs) +- [Website Cloud Connexa](https://openvpn.net/cloud-vpn/?utm_source=terraform&utm_medium=docs) - [Terraform Registry](https://registry.terraform.io/providers/OpenVPN/openvpn-cloud/latest) ## Description -The Terraform provider for [OpenVPN Cloud](https://openvpn.net/cloud-vpn/?utm_source=terraform&utm_medium=docs) allows teams to configure and update OpenVPN Cloud project parameters via their command line. +The Terraform provider for [Cloud Connexa](https://openvpn.net/cloud-vpn/?utm_source=terraform&utm_medium=docs) allows teams to configure and update Cloud Connexa project parameters via their command line. ## Maintainers This provider plugin is maintained by: -- OpenVPN team at [OpenVPN Cloud](https://openvpn.net/cloud-vpn/?utm_source=terraform&utm_medium=docs) +- OpenVPN team at [Cloud Connexa](https://openvpn.net/cloud-vpn/?utm_source=terraform&utm_medium=docs) - SRE Team at [ANNA Money](https://anna.money/?utm_source=terraform&utm_medium=referral&utm_campaign=docs) / [GitHub ANNA Money](http://github.com/anna-money/) - [@patoarvizu](https://github.com/patoarvizu) @@ -75,4 +75,4 @@ _Note:_ Acceptance tests create real resources, and often cost money to run. make testacc ``` -_**Please note:** This provider, like OpenVPN Cloud API, is in beta status. Report any problems via issue in this repo._ +_**Please note:** This provider, like Cloud Connexa API, is in beta status. Report any problems via issue in this repo._ diff --git a/cloudconnexa/data_source_network.go b/cloudconnexa/data_source_network.go index 6315f67..ad5e685 100644 --- a/cloudconnexa/data_source_network.go +++ b/cloudconnexa/data_source_network.go @@ -12,7 +12,7 @@ import ( func dataSourceNetwork() *schema.Resource { return &schema.Resource{ - Description: "Use a `cloudconnexa_network` data source to read an OpenVPN Cloud network.", + Description: "Use a `cloudconnexa_network` data source to read an Cloud Connexa network.", ReadContext: dataSourceNetworkRead, Schema: map[string]*schema.Schema{ "network_id": { diff --git a/cloudconnexa/data_source_user.go b/cloudconnexa/data_source_user.go index cf41e3a..fde20d9 100644 --- a/cloudconnexa/data_source_user.go +++ b/cloudconnexa/data_source_user.go @@ -12,7 +12,7 @@ import ( func dataSourceUser() *schema.Resource { return &schema.Resource{ - Description: "Use a `cloudconnexa_user` data source to read a specific OpenVPN Cloud user.", + Description: "Use a `cloudconnexa_user` data source to read a specific Cloud Connexa user.", ReadContext: dataSourceUserRead, Schema: map[string]*schema.Schema{ "user_id": { diff --git a/cloudconnexa/data_source_user_group.go b/cloudconnexa/data_source_user_group.go index bc58d4b..f1ced3a 100644 --- a/cloudconnexa/data_source_user_group.go +++ b/cloudconnexa/data_source_user_group.go @@ -12,7 +12,7 @@ import ( func dataSourceUserGroup() *schema.Resource { return &schema.Resource{ - Description: "Use an `cloudconnexa_user_group` data source to read an OpenVPN Cloud user group.", + Description: "Use an `cloudconnexa_user_group` data source to read an Cloud Connexa user group.", ReadContext: dataSourceUserGroupRead, Schema: map[string]*schema.Schema{ "user_group_id": { diff --git a/cloudconnexa/data_source_vpn_region.go b/cloudconnexa/data_source_vpn_region.go index f28430d..e8d02ad 100644 --- a/cloudconnexa/data_source_vpn_region.go +++ b/cloudconnexa/data_source_vpn_region.go @@ -12,7 +12,7 @@ import ( func dataSourceVpnRegion() *schema.Resource { return &schema.Resource{ - Description: "Use a `cloudconnexa_vpn_region` data source to read an OpenVPN Cloud VPN region.", + Description: "Use a `cloudconnexa_vpn_region` data source to read an Cloud Connexa VPN region.", ReadContext: dataSourceVpnRegionRead, Schema: map[string]*schema.Schema{ "region_id": { diff --git a/cloudconnexa/resource_connector.go b/cloudconnexa/resource_connector.go index 9769ea0..60534fa 100644 --- a/cloudconnexa/resource_connector.go +++ b/cloudconnexa/resource_connector.go @@ -11,7 +11,7 @@ import ( func resourceConnector() *schema.Resource { return &schema.Resource{ - Description: "Use `cloudconnexa_connector` to create an OpenVPN Cloud connector.\n\n~> NOTE: This only creates the OpenVPN Cloud connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", + Description: "Use `cloudconnexa_connector` to create an Cloud Connexa connector.\n\n~> NOTE: This only creates the Cloud Connexa connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", CreateContext: resourceConnectorCreate, ReadContext: resourceConnectorRead, DeleteContext: resourceConnectorDelete, @@ -79,7 +79,7 @@ func resourceConnectorCreate(ctx context.Context, d *schema.ResourceData, m inte return append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: "Connector needs to be set up manually", - Detail: "Terraform only creates the OpenVPN Cloud connector object, but additional manual steps are required to associate a host in your infrastructure with this connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", + Detail: "Terraform only creates the Cloud Connexa connector object, but additional manual steps are required to associate a host in your infrastructure with this connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", }) } diff --git a/cloudconnexa/resource_host.go b/cloudconnexa/resource_host.go index 43d218e..398f264 100644 --- a/cloudconnexa/resource_host.go +++ b/cloudconnexa/resource_host.go @@ -12,7 +12,7 @@ import ( func resourceHost() *schema.Resource { return &schema.Resource{ - Description: "Use `cloudconnexa_host` to create an OpenVPN Cloud host.", + Description: "Use `cloudconnexa_host` to create an Cloud Connexa host.", CreateContext: resourceHostCreate, ReadContext: resourceHostRead, UpdateContext: resourceHostUpdate, @@ -135,7 +135,7 @@ func resourceHostCreate(ctx context.Context, d *schema.ResourceData, m interface return append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: "The connector for this host needs to be set up manually", - Detail: "Terraform only creates the OpenVPN Cloud connector object for this host, but additional manual steps are required to associate a host in your infrastructure with this connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", + Detail: "Terraform only creates the Cloud Connexa connector object for this host, but additional manual steps are required to associate a host in your infrastructure with this connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", }) } diff --git a/cloudconnexa/resource_network.go b/cloudconnexa/resource_network.go index 978fb16..909ed50 100644 --- a/cloudconnexa/resource_network.go +++ b/cloudconnexa/resource_network.go @@ -11,7 +11,7 @@ import ( func resourceNetwork() *schema.Resource { return &schema.Resource{ - Description: "Use `cloudconnexa_network` to create an OpenVPN Cloud Network.", + Description: "Use `cloudconnexa_network` to create an Cloud Connexa Network.", CreateContext: resourceNetworkCreate, ReadContext: resourceNetworkRead, UpdateContext: resourceNetworkUpdate, @@ -177,7 +177,7 @@ func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, m interf return append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: "The default connector for this network needs to be set up manually", - Detail: "Terraform only creates the OpenVPN Cloud default connector object for this network, but additional manual steps are required to associate a host in your infrastructure with this connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", + Detail: "Terraform only creates the Cloud Connexa default connector object for this network, but additional manual steps are required to associate a host in your infrastructure with this connector. Go to https://openvpn.net/cloud-docs/connector/ for more information.", }) } diff --git a/cloudconnexa/resource_route.go b/cloudconnexa/resource_route.go index 5a1d393..708feff 100644 --- a/cloudconnexa/resource_route.go +++ b/cloudconnexa/resource_route.go @@ -11,7 +11,7 @@ import ( func resourceRoute() *schema.Resource { return &schema.Resource{ - Description: "Use `cloudconnexa_route` to create a route on an OpenVPN Cloud network.", + Description: "Use `cloudconnexa_route` to create a route on an Cloud Connexa network.", CreateContext: resourceRouteCreate, UpdateContext: resourceRouteUpdate, ReadContext: resourceRouteRead, diff --git a/cloudconnexa/resource_service.go b/cloudconnexa/resource_service.go index b17fae9..1825a4b 100644 --- a/cloudconnexa/resource_service.go +++ b/cloudconnexa/resource_service.go @@ -15,7 +15,7 @@ var ( func resourceIPService() *schema.Resource { return &schema.Resource{ - CreateContext: resourceServiceCreate, + CreateContext: resourceIPServiceCreate, ReadContext: resourceServiceRead, DeleteContext: resourceServiceDelete, UpdateContext: resourceServiceUpdate, @@ -30,7 +30,7 @@ func resourceIPService() *schema.Resource { }, "description": { Type: schema.TypeString, - Default: "Created by Terraform OpenVPN Cloud Provider", + Default: "Created by Terraform Cloud Connexa Provider", ValidateFunc: validation.StringLenBetween(1, 255), Optional: true, }, @@ -209,20 +209,26 @@ func flattenRoutes(routes []*client.Route) []string { return data } -func resourceServiceCreate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { - c := i.(*cloudconnexa.Client) - var diags diag.Diagnostics +func resourceIPServiceCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + client := m.(*cloudconnexa.Client) + + service := &cloudconnexa.IPService{ + // Заполните поля структуры IPService данными из d + Name: d.Get("name").(string), + Description: d.Get("description").(string), + NetworkItemType: d.Get("network_item_type").(string), + NetworkItemId: d.Get("network_item_id").(string), + // ... другие поля ... + } - service, err := c.CreateService(resourceDataToService(data)) + createdService, err := client.IPServices.Create(service) if err != nil { - return append(diags, diag.FromErr(err)...) + return diag.FromErr(err) } - data.SetId(service.Id) - setResourceData(data, service) - return diags + d.SetId(createdService.Id) + return resourceIPServiceRead(ctx, d, m) } - func resourceDataToService(data *schema.ResourceData) *client.Service { routes := data.Get("routes").([]interface{}) var configRoutes []*client.Route diff --git a/cloudconnexa/resource_service_test.go b/cloudconnexa/resource_service_test.go index 619d3b9..7896036 100644 --- a/cloudconnexa/resource_service_test.go +++ b/cloudconnexa/resource_service_test.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" "testing" "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" @@ -55,8 +56,8 @@ func testAccCheckCloudConnexaServiceExists(rn, networkId string) resource.TestCh return errors.New("no ID is set") } - c := testAccProvider.Meta().(*client.Client) - _, err := c.GetService(rs.Primary.ID, rs.Primary.Attributes["network_item_type"], rs.Primary.Attributes["network_item_id"]) + c := testAccProvider.Meta().(*cloudconnexa.Client) + _, err := c.IPServices.Get(rs.Primary.ID, rs.Primary.Attributes["network_item_type"], rs.Primary.Attributes["network_item_id"]) if err != nil { return err } diff --git a/cloudconnexa/resource_user.go b/cloudconnexa/resource_user.go index 68e758b..dc63c19 100644 --- a/cloudconnexa/resource_user.go +++ b/cloudconnexa/resource_user.go @@ -10,7 +10,7 @@ import ( func resourceUser() *schema.Resource { return &schema.Resource{ - Description: "Use `cloudconnexa_user` to create an OpenVPN Cloud user.", + Description: "Use `cloudconnexa_user` to create an Cloud Connexa user.", CreateContext: resourceUserCreate, ReadContext: resourceUserRead, UpdateContext: resourceUserUpdate, @@ -30,7 +30,7 @@ func resourceUser() *schema.Resource { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringLenBetween(1, 120), - Description: "An invitation to OpenVPN cloud account will be sent to this email. It will include an initial password and a VPN setup guide.", + Description: "An invitation to Cloud Connexa account will be sent to this email. It will include an initial password and a VPN setup guide.", }, "first_name": { Type: schema.TypeString, @@ -61,7 +61,7 @@ func resourceUser() *schema.Resource { Optional: true, ForceNew: true, MaxItems: 1, - Description: "When a user signs in, the device that they use will be added to their account. You can read more at [OpenVPN Cloud Device](https://openvpn.net/cloud-docs/device/).", + Description: "When a user signs in, the device that they use will be added to their account. You can read more at [Cloud Connexa Device](https://openvpn.net/cloud-docs/device/).", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "name": { @@ -134,7 +134,7 @@ func resourceUserCreate(ctx context.Context, d *schema.ResourceData, m interface return append(diags, diag.Diagnostic{ Severity: diag.Warning, Summary: "The user's role cannot be changed using the code.", - Detail: "There is a bug in OpenVPN Cloud API that prevents setting the user's role during the creation. All users are created as Members by default. Once it's fixed, the provider will be updated accordingly.", + Detail: "There is a bug in Cloud Connexa API that prevents setting the user's role during the creation. All users are created as Members by default. Once it's fixed, the provider will be updated accordingly.", }) } @@ -144,7 +144,7 @@ func resourceUserRead(ctx context.Context, d *schema.ResourceData, m interface{} userId := d.Id() u, err := c.Users.Get(userId) - // If group_id is not set, OpenVPN cloud sets it to the default group. + // If group_id is not set, Cloud Connexa sets it to the default group. var groupId string if d.Get("group_id") == "" { // The group has not been explicitly set. diff --git a/cloudconnexa/resource_user_group.go b/cloudconnexa/resource_user_group.go index da8e400..0addd05 100644 --- a/cloudconnexa/resource_user_group.go +++ b/cloudconnexa/resource_user_group.go @@ -10,7 +10,7 @@ import ( func resourceUserGroup() *schema.Resource { return &schema.Resource{ - Description: "Use `cloudconnexa_user_group` to create an OpenVPN Cloud user group.", + Description: "Use `cloudconnexa_user_group` to create an Cloud Connexa user group.", CreateContext: resourceUserGroupCreate, ReadContext: resourceUserGroupRead, UpdateContext: resourceUserGroupUpdate, diff --git a/docs/data-sources/connector.md b/docs/data-sources/connector.md index bfb4e34..7f08612 100644 --- a/docs/data-sources/connector.md +++ b/docs/data-sources/connector.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_connector Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use an cloudconnexa_connector data source to read an existing OpenVPN Cloud connector. + Use an cloudconnexa_connector data source to read an existing Cloud Connexa connector. --- # cloudconnexa_connector (Data Source) -Use an `cloudconnexa_connector` data source to read an existing OpenVPN Cloud connector. +Use an `cloudconnexa_connector` data source to read an existing Cloud Connexa connector. diff --git a/docs/data-sources/host.md b/docs/data-sources/host.md index 08f92ff..b628d72 100644 --- a/docs/data-sources/host.md +++ b/docs/data-sources/host.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_host Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use an cloudconnexa_host data source to read an existing OpenVPN Cloud connector. + Use an cloudconnexa_host data source to read an existing Cloud Connexa connector. --- # cloudconnexa_host (Data Source) -Use an `cloudconnexa_host` data source to read an existing OpenVPN Cloud connector. +Use an `cloudconnexa_host` data source to read an existing Cloud Connexa connector. diff --git a/docs/data-sources/network.md b/docs/data-sources/network.md index bb8c3f0..1726893 100644 --- a/docs/data-sources/network.md +++ b/docs/data-sources/network.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_network Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use a cloudconnexa_network data source to read an OpenVPN Cloud network. + Use a cloudconnexa_network data source to read an Cloud Connexa network. --- # cloudconnexa_network (Data Source) -Use a `cloudconnexa_network` data source to read an OpenVPN Cloud network. +Use a `cloudconnexa_network` data source to read an Cloud Connexa network. diff --git a/docs/data-sources/network_routes.md b/docs/data-sources/network_routes.md index 3149367..1859bd6 100644 --- a/docs/data-sources/network_routes.md +++ b/docs/data-sources/network_routes.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_network_routes Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use an cloudconnexa_network_routes data source to read all the routes associated with an OpenVPN Cloud network. + Use an cloudconnexa_network_routes data source to read all the routes associated with an Cloud Connexa network. --- # cloudconnexa_network_routes (Data Source) -Use an `cloudconnexa_network_routes` data source to read all the routes associated with an OpenVPN Cloud network. +Use an `cloudconnexa_network_routes` data source to read all the routes associated with an Cloud Connexa network. @@ -17,7 +17,7 @@ Use an `cloudconnexa_network_routes` data source to read all the routes associat ### Required -- `network_item_id` (String) The id of the OpenVPN Cloud network of the routes to be discovered. +- `network_item_id` (String) The id of the Cloud Connexa network of the routes to be discovered. ### Read-Only diff --git a/docs/data-sources/user.md b/docs/data-sources/user.md index 784f834..c778a17 100644 --- a/docs/data-sources/user.md +++ b/docs/data-sources/user.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_user Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use a cloudconnexa_user data source to read a specific OpenVPN Cloud user. + Use a cloudconnexa_user data source to read a specific Cloud Connexa user. --- # cloudconnexa_user (Data Source) -Use a `cloudconnexa_user` data source to read a specific OpenVPN Cloud user. +Use a `cloudconnexa_user` data source to read a specific Cloud Connexa user. diff --git a/docs/data-sources/user_group.md b/docs/data-sources/user_group.md index 8d5d120..f084ea5 100644 --- a/docs/data-sources/user_group.md +++ b/docs/data-sources/user_group.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_user_group Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use an cloudconnexa_user_group data source to read an OpenVPN Cloud user group. + Use an cloudconnexa_user_group data source to read an Cloud Connexa user group. --- # cloudconnexa_user_group (Data Source) -Use an `cloudconnexa_user_group` data source to read an OpenVPN Cloud user group. +Use an `cloudconnexa_user_group` data source to read an Cloud Connexa user group. diff --git a/docs/data-sources/vpn_region.md b/docs/data-sources/vpn_region.md index 41e5fca..8afe0a4 100644 --- a/docs/data-sources/vpn_region.md +++ b/docs/data-sources/vpn_region.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_vpn_region Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use a cloudconnexa_vpn_region data source to read an OpenVPN Cloud VPN region. + Use a cloudconnexa_vpn_region data source to read an Cloud Connexa VPN region. --- # cloudconnexa_vpn_region (Data Source) -Use a `cloudconnexa_vpn_region` data source to read an OpenVPN Cloud VPN region. +Use a `cloudconnexa_vpn_region` data source to read an Cloud Connexa VPN region. diff --git a/docs/index.md b/docs/index.md index 4ba6dbf..c74fbad 100644 --- a/docs/index.md +++ b/docs/index.md @@ -8,7 +8,7 @@ description: |- # CloudConnexa Provider -!> **WARNING:** This provider is experimental and support for it is on a best-effort basis. Additionally, the underlying API for OpenVPN Cloud is on Beta, which means that future versions of the provider may introduce breaking changes. Should that happen, migration documentation and support will also be provided on a best-effort basis. +!> **WARNING:** This provider is experimental and support for it is on a best-effort basis. Additionally, the underlying API for Cloud Connexa is on Beta, which means that future versions of the provider may introduce breaking changes. Should that happen, migration documentation and support will also be provided on a best-effort basis. Use this provider to interact with the [Cloud Connexa API](https://openvpn.net/cloud-docs/developer/index.html). diff --git a/docs/resources/connector.md b/docs/resources/connector.md index a3ea7c7..845eb1a 100644 --- a/docs/resources/connector.md +++ b/docs/resources/connector.md @@ -3,15 +3,15 @@ page_title: "cloudconnexa_connector Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use cloudconnexa_connector to create an OpenVPN Cloud connector. - ~> NOTE: This only creates the OpenVPN Cloud connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information. + Use cloudconnexa_connector to create an Cloud Connexa connector. + ~> NOTE: This only creates the Cloud Connexa connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information. --- # cloudconnexa_connector (Resource) -Use `cloudconnexa_connector` to create an OpenVPN Cloud connector. +Use `cloudconnexa_connector` to create an Cloud Connexa connector. -~> NOTE: This only creates the OpenVPN Cloud connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information. +~> NOTE: This only creates the Cloud Connexa connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information. diff --git a/docs/resources/host.md b/docs/resources/host.md index fb51956..62fc611 100644 --- a/docs/resources/host.md +++ b/docs/resources/host.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_host Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use cloudconnexa_host to create an OpenVPN Cloud host. + Use cloudconnexa_host to create an Cloud Connexa host. --- # cloudconnexa_host (Resource) -Use `cloudconnexa_host` to create an OpenVPN Cloud host. +Use `cloudconnexa_host` to create an Cloud Connexa host. diff --git a/docs/resources/network.md b/docs/resources/network.md index 6ded36e..b7784b1 100644 --- a/docs/resources/network.md +++ b/docs/resources/network.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_network Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use cloudconnexa_network to create an OpenVPN Cloud Network. + Use cloudconnexa_network to create an Cloud Connexa Network. --- # cloudconnexa_network (Resource) -Use `cloudconnexa_network` to create an OpenVPN Cloud Network. +Use `cloudconnexa_network` to create an Cloud Connexa Network. diff --git a/docs/resources/route.md b/docs/resources/route.md index 019cadd..30e96e4 100644 --- a/docs/resources/route.md +++ b/docs/resources/route.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_route Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use cloudconnexa_route to create a route on an OpenVPN Cloud network. + Use cloudconnexa_route to create a route on an Cloud Connexa network. --- # cloudconnexa_route (Resource) -Use `cloudconnexa_route` to create a route on an OpenVPN Cloud network. +Use `cloudconnexa_route` to create a route on an Cloud Connexa network. diff --git a/docs/resources/user.md b/docs/resources/user.md index af8a8ba..5180af9 100644 --- a/docs/resources/user.md +++ b/docs/resources/user.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_user Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use cloudconnexa_user to create an OpenVPN Cloud user. + Use cloudconnexa_user to create an Cloud Connexa user. --- # cloudconnexa_user (Resource) -Use `cloudconnexa_user` to create an OpenVPN Cloud user. +Use `cloudconnexa_user` to create an Cloud Connexa user. @@ -17,14 +17,14 @@ Use `cloudconnexa_user` to create an OpenVPN Cloud user. ### Required -- `email` (String) An invitation to OpenVPN cloud account will be sent to this email. It will include an initial password and a VPN setup guide. +- `email` (String) An invitation to Cloud Connexa account will be sent to this email. It will include an initial password and a VPN setup guide. - `first_name` (String) User's first name. - `last_name` (String) User's last name. - `username` (String) A username for the user. ### Optional -- `devices` (Block List, Max: 1) When a user signs in, the device that they use will be added to their account. You can read more at [OpenVPN Cloud Device](https://openvpn.net/cloud-docs/device/). (see [below for nested schema](#nestedblock--devices)) +- `devices` (Block List, Max: 1) When a user signs in, the device that they use will be added to their account. You can read more at [Cloud Connexa Device](https://openvpn.net/cloud-docs/device/). (see [below for nested schema](#nestedblock--devices)) - `group_id` (String) The UUID of a user's group. ### Read-Only diff --git a/e2e/integration_test.go b/e2e/integration_test.go index a8528c5..1e3ea2e 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -2,8 +2,8 @@ package e2e import ( "fmt" - api "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" "github.com/gruntwork-io/terratest/modules/terraform" + api "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "os" diff --git a/internal/cloudconnexaclient/cloudconnexaclient.go b/internal/cloudconnexaclient/cloudconnexaclient.go deleted file mode 100644 index 2ceacbf..0000000 --- a/internal/cloudconnexaclient/cloudconnexaclient.go +++ /dev/null @@ -1,29 +0,0 @@ -package cloudconnexaclient - -import ( - "context" - "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" -) - -type Config struct { - UserAgent string - BaseURL string - ClientID string - ClientSecret string -} - -func (c *Config) Client(ctx context.Context) (*cloudconnexa.Client, error) { - var cl *cloudconnexa.Client - var err error - if err != nil { - return nil, err - } - cl, err = cloudconnexa.NewClient("", "", "") - if err != nil { - return nil, err - } - - cl.UserAgent = c.UserAgent - - return cl, nil -} diff --git a/templates/data-sources/host.md b/templates/data-sources/host.md index 08f92ff..b628d72 100644 --- a/templates/data-sources/host.md +++ b/templates/data-sources/host.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_host Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use an cloudconnexa_host data source to read an existing OpenVPN Cloud connector. + Use an cloudconnexa_host data source to read an existing Cloud Connexa connector. --- # cloudconnexa_host (Data Source) -Use an `cloudconnexa_host` data source to read an existing OpenVPN Cloud connector. +Use an `cloudconnexa_host` data source to read an existing Cloud Connexa connector. diff --git a/templates/data-sources/network.md b/templates/data-sources/network.md index bb8c3f0..1726893 100644 --- a/templates/data-sources/network.md +++ b/templates/data-sources/network.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_network Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use a cloudconnexa_network data source to read an OpenVPN Cloud network. + Use a cloudconnexa_network data source to read an Cloud Connexa network. --- # cloudconnexa_network (Data Source) -Use a `cloudconnexa_network` data source to read an OpenVPN Cloud network. +Use a `cloudconnexa_network` data source to read an Cloud Connexa network. diff --git a/templates/data-sources/network_routes.md b/templates/data-sources/network_routes.md index 3149367..1859bd6 100644 --- a/templates/data-sources/network_routes.md +++ b/templates/data-sources/network_routes.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_network_routes Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use an cloudconnexa_network_routes data source to read all the routes associated with an OpenVPN Cloud network. + Use an cloudconnexa_network_routes data source to read all the routes associated with an Cloud Connexa network. --- # cloudconnexa_network_routes (Data Source) -Use an `cloudconnexa_network_routes` data source to read all the routes associated with an OpenVPN Cloud network. +Use an `cloudconnexa_network_routes` data source to read all the routes associated with an Cloud Connexa network. @@ -17,7 +17,7 @@ Use an `cloudconnexa_network_routes` data source to read all the routes associat ### Required -- `network_item_id` (String) The id of the OpenVPN Cloud network of the routes to be discovered. +- `network_item_id` (String) The id of the Cloud Connexa network of the routes to be discovered. ### Read-Only diff --git a/templates/data-sources/user.md b/templates/data-sources/user.md index 784f834..c778a17 100644 --- a/templates/data-sources/user.md +++ b/templates/data-sources/user.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_user Data Source - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use a cloudconnexa_user data source to read a specific OpenVPN Cloud user. + Use a cloudconnexa_user data source to read a specific Cloud Connexa user. --- # cloudconnexa_user (Data Source) -Use a `cloudconnexa_user` data source to read a specific OpenVPN Cloud user. +Use a `cloudconnexa_user` data source to read a specific Cloud Connexa user. diff --git a/templates/index.md.tmpl b/templates/index.md.tmpl index b0d7cea..a04d270 100644 --- a/templates/index.md.tmpl +++ b/templates/index.md.tmpl @@ -8,16 +8,16 @@ description: |- # {{ .ProviderShortName }} Provider -!> **WARNING:** This provider is experimental and support for it is on a best-effort basis. Additionally, the underlying API for OpenVPN Cloud is on Beta, which means that future versions of the provider may introduce breaking changes. Should that happen, migration documentation and support will also be provided on a best-effort basis. +!> **WARNING:** This provider is experimental and support for it is on a best-effort basis. Additionally, the underlying API for Cloud Connexa is on Beta, which means that future versions of the provider may introduce breaking changes. Should that happen, migration documentation and support will also be provided on a best-effort basis. -Use this provider to interact with the [OpenVPN Cloud API](https://openvpn.net/cloud-docs/api-guide/). +Use this provider to interact with the [Cloud Connexa API](https://openvpn.net/cloud-docs/api-guide/). ## Schema ### Required -- **base_url** (String) The base url of your OpenVPN Cloud accout. +- **base_url** (String) The base url of your Cloud Connexa accout. ### Optional diff --git a/templates/resources/connector.md b/templates/resources/connector.md index a3ea7c7..845eb1a 100644 --- a/templates/resources/connector.md +++ b/templates/resources/connector.md @@ -3,15 +3,15 @@ page_title: "cloudconnexa_connector Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use cloudconnexa_connector to create an OpenVPN Cloud connector. - ~> NOTE: This only creates the OpenVPN Cloud connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information. + Use cloudconnexa_connector to create an Cloud Connexa connector. + ~> NOTE: This only creates the Cloud Connexa connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information. --- # cloudconnexa_connector (Resource) -Use `cloudconnexa_connector` to create an OpenVPN Cloud connector. +Use `cloudconnexa_connector` to create an Cloud Connexa connector. -~> NOTE: This only creates the OpenVPN Cloud connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information. +~> NOTE: This only creates the Cloud Connexa connector object. Additional manual steps are required to associate a host in your infrastructure with the connector. Go to https://openvpn.net/cloud-docs/connector/ for more information. diff --git a/templates/resources/host.md b/templates/resources/host.md index fb51956..62fc611 100644 --- a/templates/resources/host.md +++ b/templates/resources/host.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_host Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use cloudconnexa_host to create an OpenVPN Cloud host. + Use cloudconnexa_host to create an Cloud Connexa host. --- # cloudconnexa_host (Resource) -Use `cloudconnexa_host` to create an OpenVPN Cloud host. +Use `cloudconnexa_host` to create an Cloud Connexa host. diff --git a/templates/resources/network.md b/templates/resources/network.md index 6ded36e..b7784b1 100644 --- a/templates/resources/network.md +++ b/templates/resources/network.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_network Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use cloudconnexa_network to create an OpenVPN Cloud Network. + Use cloudconnexa_network to create an Cloud Connexa Network. --- # cloudconnexa_network (Resource) -Use `cloudconnexa_network` to create an OpenVPN Cloud Network. +Use `cloudconnexa_network` to create an Cloud Connexa Network. diff --git a/templates/resources/route.md b/templates/resources/route.md index 019cadd..30e96e4 100644 --- a/templates/resources/route.md +++ b/templates/resources/route.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_route Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use cloudconnexa_route to create a route on an OpenVPN Cloud network. + Use cloudconnexa_route to create a route on an Cloud Connexa network. --- # cloudconnexa_route (Resource) -Use `cloudconnexa_route` to create a route on an OpenVPN Cloud network. +Use `cloudconnexa_route` to create a route on an Cloud Connexa network. diff --git a/templates/resources/user.md b/templates/resources/user.md index af8a8ba..5180af9 100644 --- a/templates/resources/user.md +++ b/templates/resources/user.md @@ -3,12 +3,12 @@ page_title: "cloudconnexa_user Resource - terraform-provider-cloudconnexa" subcategory: "" description: |- - Use cloudconnexa_user to create an OpenVPN Cloud user. + Use cloudconnexa_user to create an Cloud Connexa user. --- # cloudconnexa_user (Resource) -Use `cloudconnexa_user` to create an OpenVPN Cloud user. +Use `cloudconnexa_user` to create an Cloud Connexa user. @@ -17,14 +17,14 @@ Use `cloudconnexa_user` to create an OpenVPN Cloud user. ### Required -- `email` (String) An invitation to OpenVPN cloud account will be sent to this email. It will include an initial password and a VPN setup guide. +- `email` (String) An invitation to Cloud Connexa account will be sent to this email. It will include an initial password and a VPN setup guide. - `first_name` (String) User's first name. - `last_name` (String) User's last name. - `username` (String) A username for the user. ### Optional -- `devices` (Block List, Max: 1) When a user signs in, the device that they use will be added to their account. You can read more at [OpenVPN Cloud Device](https://openvpn.net/cloud-docs/device/). (see [below for nested schema](#nestedblock--devices)) +- `devices` (Block List, Max: 1) When a user signs in, the device that they use will be added to their account. You can read more at [Cloud Connexa Device](https://openvpn.net/cloud-docs/device/). (see [below for nested schema](#nestedblock--devices)) - `group_id` (String) The UUID of a user's group. ### Read-Only From 35c58672e59efc2adff37fbbc0941b9279461a32 Mon Sep 17 00:00:00 2001 From: michael Date: Wed, 7 Feb 2024 10:41:44 +0200 Subject: [PATCH 16/16] ip-service implementation --- cloudconnexa/provider.go | 8 +- cloudconnexa/provider_test.go | 18 +-- cloudconnexa/resource_network.go | 7 + cloudconnexa/resource_service.go | 74 +++++----- cloudconnexa/resource_service_test.go | 17 ++- e2e/integration_test.go | 17 ++- go.mod | 91 ++++++------ go.sum | 200 ++++++++++++++------------ 8 files changed, 227 insertions(+), 205 deletions(-) diff --git a/cloudconnexa/provider.go b/cloudconnexa/provider.go index da3d91e..ff9796e 100644 --- a/cloudconnexa/provider.go +++ b/cloudconnexa/provider.go @@ -10,8 +10,8 @@ import ( ) const ( - clientIDEnvVar = "OPENVPN_CLOUD_CLIENT_ID" - clientSecretEnvVar = "OPENVPN_CLOUD_CLIENT_SECRET" + ClientIDEnvVar = "CLOUDCONNEXA_CLIENT_ID" + ClientSecretEnvVar = "CLOUDCONNEXA_CLIENT_SECRET" ) type Token struct { @@ -27,7 +27,7 @@ func Provider() *schema.Provider { Type: schema.TypeString, Optional: true, Sensitive: true, - DefaultFunc: schema.EnvDefaultFunc(clientIDEnvVar, nil), + DefaultFunc: schema.EnvDefaultFunc(ClientIDEnvVar, nil), }, "client_secret": { Description: "The authentication client_secret used to connect to Cloud Connexa API. The value can be sourced from " + @@ -35,7 +35,7 @@ func Provider() *schema.Provider { Type: schema.TypeString, Optional: true, Sensitive: true, - DefaultFunc: schema.EnvDefaultFunc(clientSecretEnvVar, nil), + DefaultFunc: schema.EnvDefaultFunc(ClientSecretEnvVar, nil), }, "base_url": { Description: "The target Cloud Connexa Base API URL in the format `https://[companyName].api.openvpn.com`", diff --git a/cloudconnexa/provider_test.go b/cloudconnexa/provider_test.go index e82347c..7f7b6d9 100644 --- a/cloudconnexa/provider_test.go +++ b/cloudconnexa/provider_test.go @@ -2,12 +2,12 @@ package cloudconnexa import ( "context" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" "os" "strings" "testing" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,14 +17,10 @@ const alphabet = "abcdefghigklmnopqrstuvwxyz" var testCloudID = os.Getenv("CLOUDCONNEXA_TEST_ORGANIZATION") var testAccProvider *schema.Provider -var testAccProviders map[string]*schema.Provider var testAccProviderFactories map[string]func() (*schema.Provider, error) func init() { testAccProvider = Provider() - testAccProviders = map[string]*schema.Provider{ - "cloudconnexa": testAccProvider, - } testAccProviderFactories = map[string]func() (*schema.Provider, error){ "cloudconnexa": func() (*schema.Provider, error) { return testAccProvider, nil @@ -37,8 +33,8 @@ func TestProvider(t *testing.T) { require.NoError(t, err) // must have the required error when the credentials are not set - t.Setenv(clientIDEnvVar, "") - t.Setenv(clientSecretEnvVar, "") + t.Setenv(ClientIDEnvVar, "") + t.Setenv(ClientSecretEnvVar, "") rc := terraform.ResourceConfig{} diags := Provider().Configure(context.Background(), &rc) assert.True(t, diags.HasError()) @@ -50,10 +46,10 @@ func TestProvider(t *testing.T) { } func testAccPreCheck(t *testing.T) { - if v := os.Getenv(clientIDEnvVar); v == "" { - t.Fatalf("%s must be set for acceptance tests", clientIDEnvVar) + if v := os.Getenv(ClientIDEnvVar); v == "" { + t.Fatalf("%s must be set for acceptance tests", ClientIDEnvVar) } - if v := os.Getenv(clientSecretEnvVar); v == "" { - t.Fatalf("%s must be set for acceptance tests", clientSecretEnvVar) + if v := os.Getenv(ClientSecretEnvVar); v == "" { + t.Fatalf("%s must be set for acceptance tests", ClientSecretEnvVar) } } diff --git a/cloudconnexa/resource_network.go b/cloudconnexa/resource_network.go index 909ed50..6da7f2f 100644 --- a/cloudconnexa/resource_network.go +++ b/cloudconnexa/resource_network.go @@ -98,6 +98,12 @@ func resourceNetwork() *schema.Resource { Computed: true, Description: "The ID of this resource.", }, + "description": { + Type: schema.TypeString, + Optional: true, + Default: "Managed by Terraform.", + Description: "The default connection description.", + }, "name": { Type: schema.TypeString, Required: true, @@ -143,6 +149,7 @@ func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, m interf { Name: configConnector["name"].(string), VpnRegionId: configConnector["vpn_region_id"].(string), + Description: configConnector["description"].(string), }, } n := cloudconnexa.Network{ diff --git a/cloudconnexa/resource_service.go b/cloudconnexa/resource_service.go index 1825a4b..b1cfc75 100644 --- a/cloudconnexa/resource_service.go +++ b/cloudconnexa/resource_service.go @@ -68,10 +68,8 @@ func resourceIPService() *schema.Resource { func resourceServiceUpdate(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { c := i.(*cloudconnexa.Client) - networkItemId := data.Get("network_item_id").(string) - networkItemType := data.Get("network_item_type").(string) - s, err := c.UpdateIPService(data.Id(), networkItemType, networkItemId, resourceDataToService(data)) + s, err := c.IPServices.Update(data.Id(), resourceDataToService(data)) if err != nil { return diag.FromErr(err) } @@ -130,18 +128,21 @@ func resourceServiceConfig() *schema.Resource { func resourceServiceRead(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { c := i.(*cloudconnexa.Client) - service, err := c.GetServiceIPByID( - data.Id(), - ) - + var diags diag.Diagnostics + service, err := c.IPServices.Get(data.Id()) if err != nil { - return diag.FromErr(err) + return append(diags, diag.FromErr(err)...) + } + if service == nil { + data.SetId("") + return diags } + setResourceData(data, service) - return nil + return diags } -func setResourceData(data *schema.ResourceData, service *client.IPService) { +func setResourceData(data *schema.ResourceData, service *cloudconnexa.IPService) { data.SetId(service.Id) _ = data.Set("name", service.Name) _ = data.Set("description", service.Description) @@ -154,16 +155,15 @@ func setResourceData(data *schema.ResourceData, service *client.IPService) { func resourceServiceDelete(ctx context.Context, data *schema.ResourceData, i interface{}) diag.Diagnostics { c := i.(*cloudconnexa.Client) - err := c.DeleteIPService( - data.Id(), - ) + var diags diag.Diagnostics + err := c.IPServices.Delete(data.Id()) if err != nil { - return diag.FromErr(err) + return append(diags, diag.FromErr(err)...) } - return nil + return diags } -func flattenServiceConfig(config *client.ServiceConfig) interface{} { +func flattenServiceConfig(config *cloudconnexa.IPServiceConfig) interface{} { var data = map[string]interface{}{ "custom_service_types": flattenCustomServiceTypes(config.CustomServiceTypes), "service_types": config.ServiceTypes, @@ -171,7 +171,7 @@ func flattenServiceConfig(config *client.ServiceConfig) interface{} { return []interface{}{data} } -func flattenCustomServiceTypes(types []*client.CustomIPServiceType) interface{} { +func flattenCustomServiceTypes(types []*cloudconnexa.CustomIPServiceType) interface{} { var data []interface{} for _, t := range types { data = append( @@ -184,7 +184,7 @@ func flattenCustomServiceTypes(types []*client.CustomIPServiceType) interface{} return data } -func flattenIcmpType(icmpType []client.Range) interface{} { +func flattenIcmpType(icmpType []cloudconnexa.Range) interface{} { var data []interface{} for _, t := range icmpType { data = append( @@ -198,65 +198,57 @@ func flattenIcmpType(icmpType []client.Range) interface{} { return data } -func flattenRoutes(routes []*client.Route) []string { +func flattenRoutes(routes []*cloudconnexa.IPServiceRoute) []string { var data []string for _, route := range routes { data = append( data, - route.Domain, + route.Value, ) } return data } -func resourceIPServiceCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { +func resourceIPServiceCreate(ctx context.Context, data *schema.ResourceData, m interface{}) diag.Diagnostics { client := m.(*cloudconnexa.Client) - service := &cloudconnexa.IPService{ - // Заполните поля структуры IPService данными из d - Name: d.Get("name").(string), - Description: d.Get("description").(string), - NetworkItemType: d.Get("network_item_type").(string), - NetworkItemId: d.Get("network_item_id").(string), - // ... другие поля ... - } - + service := resourceDataToService(data) createdService, err := client.IPServices.Create(service) if err != nil { return diag.FromErr(err) } - d.SetId(createdService.Id) - return resourceIPServiceRead(ctx, d, m) + setResourceData(data, createdService) + return nil } -func resourceDataToService(data *schema.ResourceData) *client.Service { +func resourceDataToService(data *schema.ResourceData) *cloudconnexa.IPService { routes := data.Get("routes").([]interface{}) - var configRoutes []*client.Route + var configRoutes []*cloudconnexa.IPServiceRoute for _, r := range routes { configRoutes = append( configRoutes, - &client.Route{ + &cloudconnexa.IPServiceRoute{ Value: r.(string), }, ) } - config := client.ServiceConfig{} + config := cloudconnexa.IPServiceConfig{} configList := data.Get("config").([]interface{}) if len(configList) > 0 && configList[0] != nil { - config.CustomServiceTypes = []*client.CustomIPServiceType{} + config.CustomServiceTypes = []*cloudconnexa.CustomIPServiceType{} config.ServiceTypes = []string{} mainConfig := configList[0].(map[string]interface{}) for _, r := range mainConfig["custom_service_types"].([]interface{}) { cst := r.(map[string]interface{}) - var icmpTypes []client.Range + var icmpTypes []cloudconnexa.Range for _, r := range cst["icmp_type"].([]interface{}) { icmpType := r.(map[string]interface{}) icmpTypes = append( icmpTypes, - client.Range{ + cloudconnexa.Range{ LowerValue: icmpType["lower_value"].(int), UpperValue: icmpType["upper_value"].(int), }, @@ -264,7 +256,7 @@ func resourceDataToService(data *schema.ResourceData) *client.Service { } config.CustomServiceTypes = append( config.CustomServiceTypes, - &client.CustomIPServiceType{ + &cloudconnexa.CustomIPServiceType{ IcmpType: icmpTypes, }, ) @@ -275,7 +267,7 @@ func resourceDataToService(data *schema.ResourceData) *client.Service { } } - s := &client.Service{ + s := &cloudconnexa.IPService{ Name: data.Get("name").(string), Description: data.Get("description").(string), NetworkItemId: data.Get("network_item_id").(string), diff --git a/cloudconnexa/resource_service_test.go b/cloudconnexa/resource_service_test.go index 7896036..412c9ae 100644 --- a/cloudconnexa/resource_service_test.go +++ b/cloudconnexa/resource_service_test.go @@ -7,7 +7,6 @@ import ( "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" "testing" - "github.com/OpenVPN/terraform-provider-openvpn-cloud/client" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) @@ -15,13 +14,13 @@ import ( func TestAccCloudConnexaService_basic(t *testing.T) { rn := "cloudconnexa_service.test" networkName := acctest.RandStringFromCharSet(10, alphabet) - service := client.Service{ + service := cloudconnexa.IPService{ Name: acctest.RandStringFromCharSet(10, alphabet), } serviceChanged := service serviceChanged.Name = fmt.Sprintf("changed-%s", acctest.RandStringFromCharSet(10, alphabet)) - check := func(service client.Service) resource.TestCheckFunc { + check := func(service cloudconnexa.IPService) resource.TestCheckFunc { return resource.ComposeTestCheckFunc( testAccCheckCloudConnexaServiceExists(rn, networkName), resource.TestCheckResourceAttr(rn, "name", service.Name), @@ -57,7 +56,7 @@ func testAccCheckCloudConnexaServiceExists(rn, networkId string) resource.TestCh } c := testAccProvider.Meta().(*cloudconnexa.Client) - _, err := c.IPServices.Get(rs.Primary.ID, rs.Primary.Attributes["network_item_type"], rs.Primary.Attributes["network_item_id"]) + _, err := c.IPServices.Get(rs.Primary.ID) if err != nil { return err } @@ -66,21 +65,20 @@ func testAccCheckCloudConnexaServiceExists(rn, networkId string) resource.TestCh } func testAccCheckCloudConnexaServiceDestroy(state *terraform.State) error { - c := testAccProvider.Meta().(*client.Client) + c := testAccProvider.Meta().(*cloudconnexa.Client) for _, rs := range state.RootModule().Resources { if rs.Type != "cloudconnexa_service" { continue } id := rs.Primary.Attributes["id"] - s, err := c.GetService(id, "c63acae0-b569-4116-9b39-921c1dee62d2", "NETWORK") + s, err := c.IPServices.Get(id) if err == nil || s != nil { return fmt.Errorf("service still exists") } } return nil } - -func testAccCloudConnexaServiceConfig(service client.Service, networkName string) string { +func testAccCloudConnexaServiceConfig(service cloudconnexa.IPService, networkName string) string { return fmt.Sprintf(` provider "cloudconnexa" { base_url = "https://%s.api.openvpn.com" @@ -88,6 +86,7 @@ provider "cloudconnexa" { resource "cloudconnexa_network" "test" { name = "%s" + description = "test" default_connector { name = "%s" @@ -99,7 +98,7 @@ resource "cloudconnexa_network" "test" { } } -resource "cloudconnexa_service" "test" { +resource "cloudconnexa_ip_service" "test" { name = "%s" type = "SERVICE_DESTINATION" description = "test" diff --git a/e2e/integration_test.go b/e2e/integration_test.go index 1e3ea2e..95767c7 100644 --- a/e2e/integration_test.go +++ b/e2e/integration_test.go @@ -2,6 +2,7 @@ package e2e import ( "fmt" + "github.com/OpenVPN/terraform-provider-openvpn-cloud/cloudconnexa" "github.com/gruntwork-io/terratest/modules/terraform" api "github.com/openvpn/cloudconnexa-go-client/v2/cloudconnexa" "github.com/stretchr/testify/assert" @@ -12,9 +13,7 @@ import ( ) const ( - CloudConnexaHostKey = "OVPN_HOST" - CloudConnexaClientIdKey = "CLOUDCONNEXA_CLIENT_ID" - CloudConnexaClientSecretKey = "CLOUDCONNEXA_CLIENT_SECRET" + CloudConnexaHostKey = "OVPN_HOST" ) func TestCreationDeletion(t *testing.T) { @@ -48,8 +47,8 @@ func TestCreationDeletion(t *testing.T) { client, err := api.NewClient( os.Getenv(CloudConnexaHostKey), - os.Getenv(CloudConnexaClientIdKey), - os.Getenv(CloudConnexaClientSecretKey), + os.Getenv(cloudconnexa.ClientIDEnvVar), + os.Getenv(cloudconnexa.ClientSecretEnvVar), ) require.NoError(t, err) @@ -60,9 +59,9 @@ func TestCreationDeletion(t *testing.T) { connectorWasOnline := false for i := 0; i < totalAttempts; i++ { t.Logf("Waiting for connector to be online (%d/%d)", i+1, totalAttempts) - connector, err := client.GetConnectorById(connectorID) + connector, err := client.Connectors.GetByID(connectorID) require.NoError(t, err, "Invalid connector ID in output") - if connector.ConnectionStatus == api.ConnectionStatusOnline { + if connector.ConnectionStatus == "online" { connectorWasOnline = true break } @@ -73,8 +72,8 @@ func TestCreationDeletion(t *testing.T) { func validateEnvVars(t *testing.T) { validateEnvVar(t, CloudConnexaHostKey) - validateEnvVar(t, CloudConnexaClientIdKey) - validateEnvVar(t, CloudConnexaClientSecretKey) + validateEnvVar(t, cloudconnexa.ClientIDEnvVar) + validateEnvVar(t, cloudconnexa.ClientSecretEnvVar) } func validateEnvVar(t *testing.T, envVar string) { diff --git a/go.mod b/go.mod index 530ec43..ffbf255 100644 --- a/go.mod +++ b/go.mod @@ -7,58 +7,60 @@ toolchain go1.21.3 require ( github.com/gruntwork-io/terratest v0.46.1 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 - github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0 - github.com/openvpn/cloudconnexa-go-client/v2 v2.0.0-20240111183827-7c0db1bee7e1 + github.com/hashicorp/terraform-plugin-sdk/v2 v2.32.0 + github.com/openvpn/cloudconnexa-go-client/v2 v2.29.0 github.com/stretchr/testify v1.8.4 - golang.org/x/oauth2 v0.7.0 golang.org/x/time v0.5.0 ) require ( - cloud.google.com/go v0.110.0 // indirect - cloud.google.com/go/compute v1.19.1 // indirect + cloud.google.com/go v0.111.0 // indirect + cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v0.13.0 // indirect - cloud.google.com/go/storage v1.28.1 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect + cloud.google.com/go/iam v1.1.5 // indirect + cloud.google.com/go/storage v1.30.1 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/aws/aws-sdk-go v1.44.122 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect - github.com/cloudflare/circl v1.3.3 // indirect + github.com/cloudflare/circl v1.3.7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fatih/color v1.13.0 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect - github.com/googleapis/gax-go/v2 v2.7.1 // indirect - github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/s2a-go v0.1.7 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect + github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-checkpoint v0.5.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-getter v1.7.1 // indirect - github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-hclog v1.6.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-plugin v1.5.1 // indirect + github.com/hashicorp/go-plugin v1.6.0 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/hc-install v0.6.0 // indirect - github.com/hashicorp/hcl/v2 v2.18.0 // indirect + github.com/hashicorp/hc-install v0.6.2 // indirect + github.com/hashicorp/hcl/v2 v2.19.1 // indirect github.com/hashicorp/logutils v1.0.0 // indirect - github.com/hashicorp/terraform-exec v0.19.0 // indirect - github.com/hashicorp/terraform-json v0.17.1 // indirect - github.com/hashicorp/terraform-plugin-go v0.19.0 // indirect + github.com/hashicorp/terraform-exec v0.20.0 // indirect + github.com/hashicorp/terraform-json v0.21.0 // indirect + github.com/hashicorp/terraform-plugin-go v0.21.0 // indirect github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect - github.com/hashicorp/terraform-registry-address v0.2.2 // indirect + github.com/hashicorp/terraform-registry-address v0.2.3 // indirect github.com/hashicorp/terraform-svchost v0.1.1 // indirect - github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect + github.com/hashicorp/yamux v0.1.1 // indirect github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/klauspost/compress v1.15.11 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -66,28 +68,31 @@ require ( github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/oklog/run v1.0.0 // indirect + github.com/oklog/run v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/tmccombs/hcl2json v0.3.3 // indirect github.com/ulikunitz/xz v0.5.10 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect - github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect + github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/zclconf/go-cty v1.14.0 // indirect + github.com/zclconf/go-cty v1.14.2 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.114.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect - google.golang.org/grpc v1.57.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + go.opentelemetry.io/otel v1.19.0 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/oauth2 v0.14.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/api v0.149.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect + google.golang.org/grpc v1.61.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 2f4c433..9b9bc60 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9 cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go v0.111.0 h1:YHLKNupSD1KqjDbQ3+LVdQ81h/UJbJyZG203cEfnQgM= +cloud.google.com/go v0.111.0/go.mod h1:0mibmpKP1TyOOFYQY5izo0LnT+ecvOQ0Sg3OdmMiNRU= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= @@ -68,8 +68,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= @@ -109,14 +109,12 @@ cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y97 cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= +cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= @@ -173,8 +171,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1 h1:F5QDG5ChchaAVQhINh24U99OWHURqrW8OmQcGKXcbgI= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= +cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= @@ -194,10 +192,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ= -github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= @@ -223,8 +219,9 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -235,6 +232,8 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= 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= @@ -251,18 +250,24 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= -github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= -github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= -github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +github.com/go-git/go-git/v5 v5.10.1 h1:tu8/D8i+TWxgKpzQ3Vc43e+kkhXqtsZCKI/egajKnxk= +github.com/go-git/go-git/v5 v5.10.1/go.mod h1:uEuHjxkHap8kAl//V5F/nNWwqIYtP/402ddd05mp0wg= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.7 h1:/VSMRlnY/JSyqxQUzQLKVMAskpY/NZKFA5j2P+0pP2M= github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= @@ -318,8 +323,9 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -342,14 +348,17 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= +github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -359,14 +368,15 @@ github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99 github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= +github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/gruntwork-io/terratest v0.46.1 h1:dJ/y2/Li6yCDIc8KXY8PfydtrMRiXFb3UZm4LoPShPI= github.com/gruntwork-io/terratest v0.46.1/go.mod h1:gl//tb5cLnbpQs1FTSNwhsrbhsoG00goCJPfOnyliiU= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU= github.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -376,12 +386,12 @@ github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUK github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= github.com/hashicorp/go-getter v1.7.1 h1:SWiSWN/42qdpR0MdhaOc/bLR48PLuP1ZQtYLRlM69uY= github.com/hashicorp/go-getter v1.7.1/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.2 h1:NOtoftovWkDheyUM/8JW3QMiXyxJK3uHRK7wV04nD2I= +github.com/hashicorp/go-hclog v1.6.2/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-plugin v1.5.1 h1:oGm7cWBaYIp3lJpx1RUEfLWophprE2EV/KUeqBYo+6k= -github.com/hashicorp/go-plugin v1.5.1/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4= +github.com/hashicorp/go-plugin v1.6.0 h1:wgd4KxHJTVGGqWBq4QPB1i5BZNEx9BR8+OFmHDmTk8A= +github.com/hashicorp/go-plugin v1.6.0/go.mod h1:lBS5MtSSBZk0SHc66KACcjjlU6WzEVP/8pwz68aMkCI= github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= @@ -391,29 +401,29 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hc-install v0.6.0 h1:fDHnU7JNFNSQebVKYhHZ0va1bC6SrPQ8fpebsvNr2w4= -github.com/hashicorp/hc-install v0.6.0/go.mod h1:10I912u3nntx9Umo1VAeYPUUuehk0aRQJYpMwbX5wQA= +github.com/hashicorp/hc-install v0.6.2 h1:V1k+Vraqz4olgZ9UzKiAcbman9i9scg9GgSt/U3mw/M= +github.com/hashicorp/hc-install v0.6.2/go.mod h1:2JBpd+NCFKiHiu/yYCGaPyPHhZLxXTpz8oreHa/a3Ps= github.com/hashicorp/hcl/v2 v2.9.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= -github.com/hashicorp/hcl/v2 v2.18.0 h1:wYnG7Lt31t2zYkcquwgKo6MWXzRUDIeIVU5naZwHLl8= -github.com/hashicorp/hcl/v2 v2.18.0/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= +github.com/hashicorp/hcl/v2 v2.19.1 h1://i05Jqznmb2EXqa39Nsvyan2o5XyMowW5fnCKW5RPI= +github.com/hashicorp/hcl/v2 v2.19.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/terraform-exec v0.19.0 h1:FpqZ6n50Tk95mItTSS9BjeOVUb4eg81SpgVtZNNtFSM= -github.com/hashicorp/terraform-exec v0.19.0/go.mod h1:tbxUpe3JKruE9Cuf65mycSIT8KiNPZ0FkuTE3H4urQg= -github.com/hashicorp/terraform-json v0.17.1 h1:eMfvh/uWggKmY7Pmb3T85u86E2EQg6EQHgyRwf3RkyA= -github.com/hashicorp/terraform-json v0.17.1/go.mod h1:Huy6zt6euxaY9knPAFKjUITn8QxUFIe9VuSzb4zn/0o= -github.com/hashicorp/terraform-plugin-go v0.19.0 h1:BuZx/6Cp+lkmiG0cOBk6Zps0Cb2tmqQpDM3iAtnhDQU= -github.com/hashicorp/terraform-plugin-go v0.19.0/go.mod h1:EhRSkEPNoylLQntYsk5KrDHTZJh9HQoumZXbOGOXmec= +github.com/hashicorp/terraform-exec v0.20.0 h1:DIZnPsqzPGuUnq6cH8jWcPunBfY+C+M8JyYF3vpnuEo= +github.com/hashicorp/terraform-exec v0.20.0/go.mod h1:ckKGkJWbsNqFKV1itgMnE0hY9IYf1HoiekpuN0eWoDw= +github.com/hashicorp/terraform-json v0.21.0 h1:9NQxbLNqPbEMze+S6+YluEdXgJmhQykRyRNd+zTI05U= +github.com/hashicorp/terraform-json v0.21.0/go.mod h1:qdeBs11ovMzo5puhrRibdD6d2Dq6TyE/28JiU4tIQxk= +github.com/hashicorp/terraform-plugin-go v0.21.0 h1:VSjdVQYNDKR0l2pi3vsFK1PdMQrw6vGOshJXMNFeVc0= +github.com/hashicorp/terraform-plugin-go v0.21.0/go.mod h1:piJp8UmO1uupCvC9/H74l2C6IyKG0rW4FDedIpwW5RQ= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0 h1:wcOKYwPI9IorAJEBLzgclh3xVolO7ZorYd6U1vnok14= -github.com/hashicorp/terraform-plugin-sdk/v2 v2.29.0/go.mod h1:qH/34G25Ugdj5FcM95cSoXzUgIbgfhVLXCcEcYaMwq8= -github.com/hashicorp/terraform-registry-address v0.2.2 h1:lPQBg403El8PPicg/qONZJDC6YlgCVbWDtNmmZKtBno= -github.com/hashicorp/terraform-registry-address v0.2.2/go.mod h1:LtwNbCihUoUZ3RYriyS2wF/lGPB6gF9ICLRtuDk7hSo= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.32.0 h1:7xdO9aOXVmhvMxNAq8UloyyqW0EEzyAY37llSTHJgjo= +github.com/hashicorp/terraform-plugin-sdk/v2 v2.32.0/go.mod h1:LxQzs7AQl/5JE1IGFd6LX8E4A0InRJ/7s245gOmsejA= +github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= +github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= @@ -445,12 +455,15 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326 h1:ofNAzWCcyTALn2Zv40+8XitdzCgXY6e9qvXwN9W0YXg= github.com/mattn/go-zglob v0.0.2-0.20190814121620-e3c945676326/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo= @@ -467,10 +480,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/openvpn/cloudconnexa-go-client/v2 v2.0.0-20240111183827-7c0db1bee7e1 h1:92Qkf8eLZ0YXNoGQzMttkyCxUoQtCHBvEznh0swRJOY= -github.com/openvpn/cloudconnexa-go-client/v2 v2.0.0-20240111183827-7c0db1bee7e1/go.mod h1:zKYdNzbENBz0jI9EWBOjXCyjGuGcpnOwmoE8lwDXmNU= +github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= +github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -482,8 +493,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/skeema/knownhosts v1.2.0 h1:h9r9cf0+u7wSE+M183ZtMGgOJKiL96brpaz5ekfJCpM= -github.com/skeema/knownhosts v1.2.0/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo= +github.com/skeema/knownhosts v1.2.1 h1:SHWdIUa82uGZz+F+47k8SY4QhhI291cXCpopT1lK2AQ= +github.com/skeema/knownhosts v1.2.1/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -508,8 +519,8 @@ github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6Ac github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= -github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= -github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= +github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= @@ -524,8 +535,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zclconf/go-cty v1.8.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= -github.com/zclconf/go-cty v1.14.0 h1:/Xrd39K7DXbHzlisFP9c4pHao4yyf+/Ug9LEz+Y/yhc= -github.com/zclconf/go-cty v1.14.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= +github.com/zclconf/go-cty v1.14.2 h1:kTG7lqmBou0Zkx35r6HJHUQTvaRPr5bIAf3AoHS0izI= +github.com/zclconf/go-cty v1.14.2/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -536,6 +547,14 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -546,8 +565,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -585,8 +604,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -638,8 +657,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -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.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -665,8 +684,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.7.0 h1:qe6s0zUXlPX80/dITx3440hWZ7GwMwgDDyrSGTPJG/g= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0= +golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -681,8 +700,9 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/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.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -748,21 +768,22 @@ golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -772,11 +793,12 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -834,8 +856,9 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/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 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= 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= @@ -893,16 +916,17 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/api v0.149.0 h1:b2CqT6kG+zqJIVKRQ3ELJVLN1PwHZ6DJ3dW8yl82rgY= +google.golang.org/api v0.149.0/go.mod h1:Mwn1B7JTXrzXtnvmzQE2BD6bYZQ8DShKZDZbeN9I7qI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1004,12 +1028,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 h1:m8v1xLLLzMe1m5P+gCTF8nJB9epwZQUBERm20Oy1poQ= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= +google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM= +google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1045,8 +1069,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -1063,8 +1087,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=