Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Project: Virtual Private Cloud #381

Merged
merged 21 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9e2ab66
Support Linode VPC (#362)
zliang-akamai Aug 29, 2023
4d3bb35
Fix instance create opts (#373)
zliang-akamai Sep 1, 2023
b587732
Merge branch 'main' into proj/vpc
zliang-akamai Sep 13, 2023
be510b5
Merge branch 'main' into proj/vpc
zliang-akamai Sep 26, 2023
9eee757
Change vpc to vpc_id in InstanceConfigInterface (#388)
zliang-akamai Sep 26, 2023
efeae87
Merge pull request #404 from linode/main
zliang-akamai Oct 11, 2023
7e34133
test: add test coverage for vpc (#405)
ykim-akamai Oct 16, 2023
8d36154
Merge pull request #409 from linode/main
zliang-akamai Oct 17, 2023
c889822
Add omitempty tag to `VPCIPv4` struct (#408)
zliang-akamai Oct 21, 2023
0a02d1f
Merge branch 'main' into proj/vpc
zliang-akamai Oct 21, 2023
a362978
Fix and improve VPC test (#412)
zliang-akamai Oct 24, 2023
9b51629
Update VPCSubnet linodes field (#414)
lgarber-akamai Nov 2, 2023
045b3cd
new: Add VPCNAT1To1 configuration to IPAddress struct (#415)
lgarber-akamai Nov 3, 2023
afa0dd8
Merge branch 'main' into proj/vpc
zliang-akamai Nov 6, 2023
c287fea
Fix test workflow
zliang-akamai Nov 6, 2023
b38fbd6
Add config interfaces fixtures
zliang-akamai Nov 6, 2023
7baa4f1
Fix test
zliang-akamai Nov 6, 2023
a9ac19b
Explicitly store fixture in `setupVPCWithSubnetWithInstance` (#421)
zliang-akamai Nov 6, 2023
8b4b603
Rename `ListVPC` to be `ListVPCs` (#420)
lgarber-akamai Nov 7, 2023
b97658d
Rename `ListVPCSubnet` to be `ListVPCSubnets` (#422)
zliang-akamai Nov 7, 2023
d0fbe0f
Merge branch 'main' into proj/vpc
zliang-akamai Nov 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
timestamp=$(date +'%Y%m%d%H%M')
report_filename="${timestamp}_linodego_test_report.log"

if ! make ARGS="2>&1" testacc > "$report_filename"; then
if ! make ARGS="2>&1" test > "$report_filename"; then
echo "EXIT_STATUS=1" >> $GITHUB_ENV
fi
cat "$report_filename"
Expand Down
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,5 @@ linters:
- exhaustive
- depguard
- tagalign
- inamedparam
fast: false
8 changes: 8 additions & 0 deletions account_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ const (
ActionVolumeUpdate EventAction = "volume_update"
ActionVolumeDetach EventAction = "volume_detach"
ActionVolumeResize EventAction = "volume_resize"
ActionVPCCreate EventAction = "vpc_create"
ActionVPCDelete EventAction = "vpc_delete"
ActionVPCUpdate EventAction = "vpc_update"
ActionVPCSubnetCreate EventAction = "subnet_create"
ActionVPCSubnetDelete EventAction = "subnet_delete"
ActionVPCSubnetUpdate EventAction = "subnet_update"

// deprecated due to incorrect spelling,
// to be removed in the next major version release.
Expand All @@ -200,6 +206,8 @@ const (
EntityDomain EntityType = "domain"
EntityFirewall EntityType = "firewall"
EntityNodebalancer EntityType = "nodebalancer"
EntityVPC EntityType = "vpc"
EntityVPCSubnet EntityType = "subnet"
)

// EventStatus constants start with Event and include Linode API Event Status values
Expand Down
7 changes: 2 additions & 5 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+j
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-resty/resty/v2 v2.9.1/go.mod h1:4/GYJVjh9nhkhGR6AUNW3XhpDYNUr+Uvy9gV/VGZIy4=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
Expand Down Expand Up @@ -157,6 +156,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
Expand All @@ -183,15 +183,12 @@ golang.org/x/net v0.7.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.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
Expand Down
228 changes: 228 additions & 0 deletions instance_config_interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
package linodego

import (
"context"
"encoding/json"
"fmt"
)

// InstanceConfigInterface contains information about a configuration's network interface
type InstanceConfigInterface struct {
ID int `json:"id"`
IPAMAddress string `json:"ipam_address"`
Label string `json:"label"`
Purpose ConfigInterfacePurpose `json:"purpose"`
Primary bool `json:"primary"`
Active bool `json:"active"`
VPCID *int `json:"vpc_id"`
SubnetID *int `json:"subnet_id"`
IPv4 VPCIPv4 `json:"ipv4"`
IPRanges []string `json:"ip_ranges"`
}

type VPCIPv4 struct {
VPC string `json:"vpc,omitempty"`
NAT1To1 string `json:"nat_1_1,omitempty"`
}

type InstanceConfigInterfaceCreateOptions struct {
IPAMAddress string `json:"ipam_address,omitempty"`
Label string `json:"label,omitempty"`
Purpose ConfigInterfacePurpose `json:"purpose,omitempty"`
Primary bool `json:"primary,omitempty"`
SubnetID *int `json:"subnet_id,omitempty"`
IPv4 *VPCIPv4 `json:"ipv4,omitempty"`
IPRanges []string `json:"ip_ranges,omitempty"`
}

type InstanceConfigInterfaceUpdateOptions struct {
Primary bool `json:"primary,omitempty"`
IPv4 *VPCIPv4 `json:"ipv4,omitempty"`
IPRanges []string `json:"ip_ranges,omitempty"`
}

type InstanceConfigInterfacesReorderOptions struct {
IDs []int `json:"ids"`
}

func getInstanceConfigInterfacesCreateOptionsList(
interfaces []InstanceConfigInterface,
) []InstanceConfigInterfaceCreateOptions {
interfaceOptsList := make([]InstanceConfigInterfaceCreateOptions, len(interfaces))
for index, configInterface := range interfaces {
interfaceOptsList[index] = configInterface.GetCreateOptions()
}
return interfaceOptsList
}

func (i InstanceConfigInterface) GetCreateOptions() InstanceConfigInterfaceCreateOptions {
opts := InstanceConfigInterfaceCreateOptions{
Label: i.Label,
Purpose: i.Purpose,
Primary: i.Primary,
SubnetID: i.SubnetID,
}

if len(i.IPRanges) > 0 {
opts.IPRanges = i.IPRanges
}

if i.Purpose == InterfacePurposeVPC &&
i.IPv4.NAT1To1 != "" && i.IPv4.VPC != "" {
opts.IPv4 = &VPCIPv4{
VPC: i.IPv4.VPC,
NAT1To1: i.IPv4.NAT1To1,
}
}

// workaround for API issue
if i.IPAMAddress == "222" {
opts.IPAMAddress = ""
} else {
opts.IPAMAddress = i.IPAMAddress
}

return opts
}

func (i InstanceConfigInterface) GetUpdateOptions() InstanceConfigInterfaceUpdateOptions {
opts := InstanceConfigInterfaceUpdateOptions{
Primary: i.Primary,
}

if i.Purpose == InterfacePurposeVPC {
opts.IPv4 = &VPCIPv4{
VPC: i.IPv4.VPC,
NAT1To1: i.IPv4.NAT1To1,
}
}

if len(i.IPRanges) > 0 {
opts.IPRanges = i.IPRanges
}

return opts
}

func (c *Client) AppendInstanceConfigInterface(
ctx context.Context,
linodeID int,
configID int,
opts InstanceConfigInterfaceCreateOptions,
) (*InstanceConfigInterface, error) {
body, err := json.Marshal(opts)
if err != nil {
return nil, err
}

req := c.R(ctx).SetResult(&InstanceConfigInterface{}).SetBody(string(body))
e := fmt.Sprintf("/linode/instances/%d/configs/%d/interfaces", linodeID, configID)
r, err := coupleAPIErrors(req.Post(e))
if err != nil {
return nil, err
}

return r.Result().(*InstanceConfigInterface), nil
}

func (c *Client) GetInstanceConfigInterface(
ctx context.Context,
linodeID int,
configID int,
interfaceID int,
) (*InstanceConfigInterface, error) {
e := fmt.Sprintf(
"linode/instances/%d/configs/%d/interfaces/%d",
linodeID,
configID,
interfaceID,
)
req := c.R(ctx).SetResult(&InstanceConfigInterface{})
r, err := coupleAPIErrors(req.Get(e))
if err != nil {
return nil, err
}
return r.Result().(*InstanceConfigInterface), nil
}

func (c *Client) ListInstanceConfigInterfaces(
ctx context.Context,
linodeID int,
configID int,
) ([]InstanceConfigInterface, error) {
e := fmt.Sprintf(
"linode/instances/%d/configs/%d/interfaces",
linodeID,
configID,
)
req := c.R(ctx).SetResult([]InstanceConfigInterface{})
r, err := coupleAPIErrors(req.Get(e))
if err != nil {
return nil, err
}
return *r.Result().(*[]InstanceConfigInterface), nil
}

func (c *Client) UpdateInstanceConfigInterface(
ctx context.Context,
linodeID int,
configID int,
interfaceID int,
opts InstanceConfigInterfaceUpdateOptions,
) (*InstanceConfigInterface, error) {
body, err := json.Marshal(opts)
if err != nil {
return nil, err
}

e := fmt.Sprintf(
"linode/instances/%d/configs/%d/interfaces/%d",
linodeID,
configID,
interfaceID,
)
req := c.R(ctx).SetResult(&InstanceConfigInterface{}).SetBody(string(body))
r, err := coupleAPIErrors(req.Put(e))
if err != nil {
return nil, err
}
return r.Result().(*InstanceConfigInterface), nil
}

func (c *Client) DeleteInstanceConfigInterface(
ctx context.Context,
linodeID int,
configID int,
interfaceID int,
) error {
e := fmt.Sprintf(
"linode/instances/%d/configs/%d/interfaces/%d",
linodeID,
configID,
interfaceID,
)
_, err := coupleAPIErrors(c.R(ctx).Delete(e))
return err
}

func (c *Client) ReorderInstanceConfigInterfaces(
ctx context.Context,
linodeID int,
configID int,
opts InstanceConfigInterfacesReorderOptions,
) error {
body, err := json.Marshal(opts)
if err != nil {
return err
}
e := fmt.Sprintf(
"linode/instances/%d/configs/%d/interfaces/order",
linodeID,
configID,
)

req := c.R(ctx).SetBody(string(body))
_, err = coupleAPIErrors(req.Post(e))

return err
}
44 changes: 19 additions & 25 deletions instance_configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,9 @@ type ConfigInterfacePurpose string
const (
InterfacePurposePublic ConfigInterfacePurpose = "public"
InterfacePurposeVLAN ConfigInterfacePurpose = "vlan"
InterfacePurposeVPC ConfigInterfacePurpose = "vpc"
)

// InstanceConfigInterface contains information about a configuration's network interface
type InstanceConfigInterface struct {
IPAMAddress string `json:"ipam_address"`
Label string `json:"label"`
Purpose ConfigInterfacePurpose `json:"purpose"`
}

// InstanceConfigsPagedResponse represents a paginated InstanceConfig API response
type InstanceConfigsPagedResponse struct {
*PageOptions
Expand All @@ -78,26 +72,26 @@ type InstanceConfigsPagedResponse struct {

// InstanceConfigCreateOptions are InstanceConfig settings that can be used at creation
type InstanceConfigCreateOptions struct {
Label string `json:"label,omitempty"`
Comments string `json:"comments,omitempty"`
Devices InstanceConfigDeviceMap `json:"devices"`
Helpers *InstanceConfigHelpers `json:"helpers,omitempty"`
Interfaces []InstanceConfigInterface `json:"interfaces"`
MemoryLimit int `json:"memory_limit,omitempty"`
Kernel string `json:"kernel,omitempty"`
InitRD int `json:"init_rd,omitempty"`
RootDevice *string `json:"root_device,omitempty"`
RunLevel string `json:"run_level,omitempty"`
VirtMode string `json:"virt_mode,omitempty"`
Label string `json:"label,omitempty"`
Comments string `json:"comments,omitempty"`
Devices InstanceConfigDeviceMap `json:"devices"`
Helpers *InstanceConfigHelpers `json:"helpers,omitempty"`
Interfaces []InstanceConfigInterfaceCreateOptions `json:"interfaces"`
MemoryLimit int `json:"memory_limit,omitempty"`
Kernel string `json:"kernel,omitempty"`
InitRD int `json:"init_rd,omitempty"`
RootDevice *string `json:"root_device,omitempty"`
RunLevel string `json:"run_level,omitempty"`
VirtMode string `json:"virt_mode,omitempty"`
}

// InstanceConfigUpdateOptions are InstanceConfig settings that can be used in updates
type InstanceConfigUpdateOptions struct {
Label string `json:"label,omitempty"`
Comments string `json:"comments"`
Devices *InstanceConfigDeviceMap `json:"devices,omitempty"`
Helpers *InstanceConfigHelpers `json:"helpers,omitempty"`
Interfaces []InstanceConfigInterface `json:"interfaces"`
Label string `json:"label,omitempty"`
Comments string `json:"comments"`
Devices *InstanceConfigDeviceMap `json:"devices,omitempty"`
Helpers *InstanceConfigHelpers `json:"helpers,omitempty"`
Interfaces []InstanceConfigInterfaceCreateOptions `json:"interfaces"`
// MemoryLimit 0 means unlimitted, this is not omitted
MemoryLimit int `json:"memory_limit"`
Kernel string `json:"kernel,omitempty"`
Expand Down Expand Up @@ -141,7 +135,7 @@ func (i InstanceConfig) GetCreateOptions() InstanceConfigCreateOptions {
Comments: i.Comments,
Devices: *i.Devices,
Helpers: i.Helpers,
Interfaces: i.Interfaces,
Interfaces: getInstanceConfigInterfacesCreateOptionsList(i.Interfaces),
MemoryLimit: i.MemoryLimit,
Kernel: i.Kernel,
InitRD: initrd,
Expand All @@ -158,7 +152,7 @@ func (i InstanceConfig) GetUpdateOptions() InstanceConfigUpdateOptions {
Comments: i.Comments,
Devices: i.Devices,
Helpers: i.Helpers,
Interfaces: i.Interfaces,
Interfaces: getInstanceConfigInterfacesCreateOptionsList(i.Interfaces),
MemoryLimit: i.MemoryLimit,
Kernel: i.Kernel,
InitRD: copyInt(i.InitRD),
Expand Down
Loading