package civogo import ( "bytes" "encoding/json" "errors" "fmt" "strings" ) // Network represents a private network for instances to connect to type Network struct { ID string `json:"id"` Name string `json:"name,omitempty"` Default bool `json:"default"` CIDR string `json:"cidr,omitempty"` CIDRV6 string `json:"cidr_v6,omitempty"` Label string `json:"label,omitempty"` Status string `json:"status,omitempty"` IPv4Enabled bool `json:"ipv4_enabled,omitempty"` IPv6Enabled bool `json:"ipv6_enabled,omitempty"` NameserversV4 []string `json:"nameservers_v4,omitempty"` NameserversV6 []string `json:"nameservers_v6,omitempty"` } // Subnet represents a subnet within a private network type Subnet struct { ID string `json:"id"` Name string `json:"name,omitempty"` NetworkID string `json:"network_id"` SubnetSize string `json:"subnet_size,omitempty"` Status string `json:"status,omitempty"` } // SubnetConfig contains incoming request parameters for the subnet object type SubnetConfig struct { Name string `json:"name" validate:"required" schema:"name"` } // Route represents a route within a subnet type Route struct { ID string `json:"id"` SubnetID string `json:"subnet_id"` NetworkID string `json:"network_id"` ResourceID string `json:"resource_id"` ResourceType string `json:"resource_type"` } // CreateRoute contains incoming request parameters for creating a route object type CreateRoute struct { ResourceID string `json:"resource_id"` ResourceType string `json:"resource_type"` } // NetworkConfig contains incoming request parameters for the network object type NetworkConfig struct { Label string `json:"label" validate:"required" schema:"label"` Default string `json:"default" schema:"default"` IPv4Enabled *bool `json:"ipv4_enabled"` NameserversV4 []string `json:"nameservers_v4"` CIDRv4 string `json:"cidr_v4"` IPv6Enabled *bool `json:"ipv6_enabled"` NameserversV6 []string `json:"nameservers_v6"` Region string `json:"region"` } // NetworkResult represents the result from a network create/update call type NetworkResult struct { ID string `json:"id"` Label string `json:"label"` Result string `json:"result"` } // GetDefaultNetwork finds the default private network for an account func (c *Client) GetDefaultNetwork() (*Network, error) { resp, err := c.SendGetRequest("/v2/networks") if err != nil { return nil, decodeError(err) } networks := make([]Network, 0) json.NewDecoder(bytes.NewReader(resp)).Decode(&networks) for _, network := range networks { if network.Default { return &network, nil } } return nil, errors.New("no default network found") } // GetNetwork gets a network with ID func (c *Client) GetNetwork(id string) (*Network, error) { resp, err := c.SendGetRequest("/v2/networks/" + id) if err != nil { return nil, decodeError(err) } network := Network{} err = json.NewDecoder(bytes.NewReader(resp)).Decode(&network) return &network, err } // NewNetwork creates a new private network func (c *Client) NewNetwork(label string) (*NetworkResult, error) { nc := NetworkConfig{Label: label, Region: c.Region} body, err := c.SendPostRequest("/v2/networks", nc) if err != nil { return nil, decodeError(err) } var result = &NetworkResult{} if err := json.NewDecoder(bytes.NewReader(body)).Decode(result); err != nil { return nil, err } return result, nil } // ListNetworks list all private networks func (c *Client) ListNetworks() ([]Network, error) { resp, err := c.SendGetRequest("/v2/networks") if err != nil { return nil, decodeError(err) } networks := make([]Network, 0) if err := json.NewDecoder(bytes.NewReader(resp)).Decode(&networks); err != nil { return nil, err } return networks, nil } // FindNetwork finds a network by either part of the ID or part of the name func (c *Client) FindNetwork(search string) (*Network, error) { networks, err := c.ListNetworks() if err != nil { return nil, decodeError(err) } exactMatch := false partialMatchesCount := 0 result := Network{} for _, value := range networks { if value.Name == search || value.ID == search || value.Label == search { exactMatch = true result = value } else if strings.Contains(value.Name, search) || strings.Contains(value.ID, search) || strings.Contains(value.Label, search) { if !exactMatch { result = value partialMatchesCount++ } } } if exactMatch || partialMatchesCount == 1 { return &result, nil } else if partialMatchesCount > 1 { err := fmt.Errorf("unable to find %s because there were multiple matches", search) return nil, MultipleMatchesError.wrap(err) } else { err := fmt.Errorf("unable to find %s, zero matches", search) return nil, ZeroMatchesError.wrap(err) } } // RenameNetwork renames an existing private network func (c *Client) RenameNetwork(label, id string) (*NetworkResult, error) { nc := NetworkConfig{Label: label, Region: c.Region} body, err := c.SendPutRequest("/v2/networks/"+id, nc) if err != nil { return nil, decodeError(err) } var result = &NetworkResult{} if err := json.NewDecoder(bytes.NewReader(body)).Decode(result); err != nil { return nil, err } return result, nil } // DeleteNetwork deletes a private network func (c *Client) DeleteNetwork(id string) (*SimpleResponse, error) { resp, err := c.SendDeleteRequest(fmt.Sprintf("/v2/networks/%s", id)) if err != nil { return nil, decodeError(err) } return c.DecodeSimpleResponse(resp) } // GetSubnet gets a subnet with ID func (c *Client) GetSubnet(networkID, subnetID string) (*Subnet, error) { resp, err := c.SendGetRequest(fmt.Sprintf("/v2/networks/%s/subnets/", networkID) + subnetID) if err != nil { return nil, decodeError(err) } subnet := Subnet{} err = json.NewDecoder(bytes.NewReader(resp)).Decode(&subnet) return &subnet, err } // ListSubnets list all subnets for a private network func (c *Client) ListSubnets(networkID string) ([]Subnet, error) { resp, err := c.SendGetRequest(fmt.Sprintf("/v2/networks/%s/subnets", networkID)) if err != nil { return nil, decodeError(err) } subnets := make([]Subnet, 0) if err := json.NewDecoder(bytes.NewReader(resp)).Decode(&subnets); err != nil { return nil, err } return subnets, nil } // CreateSubnet creates a new subnet for a private network func (c *Client) CreateSubnet(networkID string, subnet SubnetConfig) (*Subnet, error) { body, err := c.SendPostRequest(fmt.Sprintf("/v2/networks/%s/subnets", networkID), subnet) if err != nil { return nil, decodeError(err) } var result = &Subnet{} if err := json.NewDecoder(bytes.NewReader(body)).Decode(result); err != nil { return nil, err } return result, nil } // FindSubnet finds a subnet by either part of the ID or part of the name func (c *Client) FindSubnet(search, networkID string) (*Subnet, error) { subnets, err := c.ListSubnets(networkID) if err != nil { return nil, decodeError(err) } exactMatch := false partialMatchesCount := 0 result := Subnet{} for _, value := range subnets { if value.Name == search || value.ID == search { exactMatch = true result = value } else if strings.Contains(value.Name, search) || strings.Contains(value.ID, search) { if !exactMatch { result = value partialMatchesCount++ } } } if exactMatch || partialMatchesCount == 1 { return &result, nil } else if partialMatchesCount > 1 { err := fmt.Errorf("unable to find %s because there were multiple matches", search) return nil, MultipleMatchesError.wrap(err) } else { err := fmt.Errorf("unable to find %s, zero matches", search) return nil, ZeroMatchesError.wrap(err) } } // AttachSubnetToInstance attaches a subnet to an instance func (c *Client) AttachSubnetToInstance(networkID, subnetID string, route *CreateRoute) (*Route, error) { resp, err := c.SendPostRequest(fmt.Sprintf("/v2/networks/%s/subnets/%s/routes", networkID, subnetID), route) if err != nil { return nil, decodeError(err) } var result = &Route{} if err := json.NewDecoder(bytes.NewReader(resp)).Decode(result); err != nil { return nil, err } return result, nil } // DetachSubnetFromInstance detaches a subnet from an instance func (c *Client) DetachSubnetFromInstance(networkID, subnetID string) (*SimpleResponse, error) { resp, err := c.SendDeleteRequest(fmt.Sprintf("/v2/networks/%s/subnets/%s/routes", networkID, subnetID)) if err != nil { return nil, decodeError(err) } response, err := c.DecodeSimpleResponse(resp) return response, err } // DeleteSubnet deletes a subnet func (c *Client) DeleteSubnet(networkID, subnetID string) (*SimpleResponse, error) { resp, err := c.SendDeleteRequest(fmt.Sprintf("/v2/networks/%s/subnets/%s", networkID, subnetID)) if err != nil { return nil, decodeError(err) } return c.DecodeSimpleResponse(resp) } // CreateNetwork creates a new network func (c *Client) CreateNetwork(nc NetworkConfig) (*NetworkResult, error) { body, err := c.SendPostRequest("/v2/networks", nc) if err != nil { return nil, decodeError(err) } var result = &NetworkResult{} if err := json.NewDecoder(bytes.NewReader(body)).Decode(result); err != nil { return nil, err } return result, nil } // UpdateNetwork updates an existing network func (c *Client) UpdateNetwork(id string, nc NetworkConfig) (*NetworkResult, error) { body, err := c.SendPutRequest("/v2/networks/"+id, nc) if err != nil { return nil, decodeError(err) } var result = &NetworkResult{} if err := json.NewDecoder(bytes.NewReader(body)).Decode(result); err != nil { return nil, err } return result, nil }