diff --git a/cloud/linode/fake_linode_test.go b/cloud/linode/fake_linode_test.go index aeb069d8..f54e972b 100644 --- a/cloud/linode/fake_linode_test.go +++ b/cloud/linode/fake_linode_test.go @@ -19,12 +19,15 @@ import ( const apiVersion = "v4" type fakeAPI struct { - t *testing.T - nb map[string]*linodego.NodeBalancer - nbc map[string]*linodego.NodeBalancerConfig - nbn map[string]*linodego.NodeBalancerNode - fw map[int]*linodego.Firewall // map of firewallID -> firewall - fwd map[int]map[int]*linodego.FirewallDevice // map of firewallID -> firewallDeviceID:FirewallDevice + t *testing.T + nb map[string]*linodego.NodeBalancer + nbc map[string]*linodego.NodeBalancerConfig + nbn map[string]*linodego.NodeBalancerNode + fw map[int]*linodego.Firewall // map of firewallID -> firewall + fwd map[int]map[int]*linodego.FirewallDevice // map of firewallID -> firewallDeviceID:FirewallDevice + nbvpcc map[string]*linodego.NodeBalancerVPCConfig + vpc map[int]*linodego.VPC + subnet map[int]*linodego.VPCSubnet requests map[fakeRequest]struct{} mux *http.ServeMux @@ -44,6 +47,9 @@ func newFake(t *testing.T) *fakeAPI { nbn: make(map[string]*linodego.NodeBalancerNode), fw: make(map[int]*linodego.Firewall), fwd: make(map[int]map[int]*linodego.FirewallDevice), + nbvpcc: make(map[string]*linodego.NodeBalancerVPCConfig), + vpc: make(map[int]*linodego.VPC), + subnet: make(map[int]*linodego.VPCSubnet), requests: make(map[fakeRequest]struct{}), mux: http.NewServeMux(), } @@ -117,6 +123,54 @@ func (f *fakeAPI) setupRoutes() { _, _ = w.Write(rr) }) + f.mux.HandleFunc("GET /v4/vpcs", func(w http.ResponseWriter, r *http.Request) { + res := 0 + data := []linodego.VPC{} + filter := r.Header.Get("X-Filter") + if filter == "" { + for _, v := range f.vpc { + data = append(data, *v) + } + } else { + var fs map[string]string + err := json.Unmarshal([]byte(filter), &fs) + if err != nil { + f.t.Fatal(err) + } + for _, v := range f.vpc { + if v.Label != "" && fs["label"] != "" && v.Label == fs["label"] { + data = append(data, *v) + } + } + } + + resp := paginatedResponse[linodego.VPC]{ + Page: 1, + Pages: 1, + Results: res, + Data: data, + } + rr, _ := json.Marshal(resp) + _, _ = w.Write(rr) + }) + + f.mux.HandleFunc("GET /v4/vpcs/{vpcId}/subnets", func(w http.ResponseWriter, r *http.Request) { + res := 0 + vpcID, err := strconv.Atoi(r.PathValue("vpcId")) + if err != nil { + f.t.Fatal(err) + } + + resp := paginatedResponse[linodego.VPCSubnet]{ + Page: 1, + Pages: 1, + Results: res, + Data: f.vpc[vpcID].Subnets, + } + rr, _ := json.Marshal(resp) + _, _ = w.Write(rr) + }) + f.mux.HandleFunc("GET /v4/nodebalancers/{nodeBalancerId}", func(w http.ResponseWriter, r *http.Request) { nb, found := f.nb[r.PathValue("nodeBalancerId")] if !found { @@ -462,6 +516,53 @@ func (f *fakeAPI) setupRoutes() { _, _ = w.Write(resp) }) + f.mux.HandleFunc("POST /v4/vpcs", func(w http.ResponseWriter, r *http.Request) { + vco := linodego.VPCCreateOptions{} + if err := json.NewDecoder(r.Body).Decode(&vco); err != nil { + f.t.Fatal(err) + } + + subnets := []linodego.VPCSubnet{} + for _, s := range vco.Subnets { + subnet := linodego.VPCSubnet{ + ID: rand.Intn(9999), + IPv4: s.IPv4, + } + subnets = append(subnets, subnet) + f.subnet[subnet.ID] = &subnet + } + vpc := linodego.VPC{ + ID: rand.Intn(9999), + Label: vco.Label, + Description: vco.Description, + Region: vco.Region, + Subnets: subnets, + } + + f.vpc[vpc.ID] = &vpc + resp, err := json.Marshal(vpc) + if err != nil { + f.t.Fatal(err) + } + _, _ = w.Write(resp) + }) + + f.mux.HandleFunc("DELETE /v4/vpcs/{vpcId}", func(w http.ResponseWriter, r *http.Request) { + vpcid, err := strconv.Atoi(r.PathValue("vpcId")) + if err != nil { + f.t.Fatal(err) + } + + for k, v := range f.vpc { + if v.ID == vpcid { + for _, s := range v.Subnets { + delete(f.subnet, s.ID) + } + delete(f.vpc, k) + } + } + }) + f.mux.HandleFunc("POST /v4/networking/firewalls/{firewallId}/devices", func(w http.ResponseWriter, r *http.Request) { fdco := linodego.FirewallDeviceCreateOptions{} if err := json.NewDecoder(r.Body).Decode(&fdco); err != nil { diff --git a/cloud/linode/loadbalancers_test.go b/cloud/linode/loadbalancers_test.go index 71eb48a3..8964b345 100644 --- a/cloud/linode/loadbalancers_test.go +++ b/cloud/linode/loadbalancers_test.go @@ -150,6 +150,14 @@ func TestCCMLoadBalancers(t *testing.T) { name: "Create Load Balancer With Invalid Firewall ACL - NO Allow Or Deny", f: testCreateNodeBalanceWithNoAllowOrDenyList, }, + { + name: "Create Load Balancer With VPC Backend", + f: testCreateNodeBalancerWithVPCBackend, + }, + { + name: "Create Load Balancer With VPC Backend - Overwrite VPC Name and Subnet with Annotation", + f: testCreateNodeBalancerWithVPCAnnotationOverwrite, + }, { name: "Create Load Balancer With Global Tags set", f: testCreateNodeBalancerWithGlobalTags, @@ -479,6 +487,83 @@ func testCreateNodeBalancerWithGlobalTags(t *testing.T, client *linodego.Client, } } +func testCreateNodeBalancerWithVPCBackend(t *testing.T, client *linodego.Client, f *fakeAPI) { + // test when no VPCs are present + ann := map[string]string{ + annotations.NodeBalancerBackendIPv4Range: "10.100.0.0/30", + } + if err := testCreateNodeBalancer(t, client, f, ann, nil); err == nil { + t.Fatalf("expected nodebalancer creation to fail") + } + + f.ResetRequests() + + // provision vpc and test again + vpcNames := Options.VPCNames + defer func() { + Options.VPCNames = vpcNames + }() + Options.VPCNames = "test1" + _, _ = client.CreateVPC(context.TODO(), linodego.VPCCreateOptions{ + Label: "test1", + Description: "", + Region: "us-west", + Subnets: []linodego.VPCSubnetCreateOptions{ + { + Label: "default", + IPv4: "10.0.0.0/8", + }, + }, + }) + + err := testCreateNodeBalancer(t, client, f, ann, nil) + if err != nil { + t.Fatalf("expected a nil error, got %v", err) + } +} + +func testCreateNodeBalancerWithVPCAnnotationOverwrite(t *testing.T, client *linodego.Client, f *fakeAPI) { + // provision multiple vpcs + vpcNames := Options.VPCNames + defer func() { + Options.VPCNames = vpcNames + }() + Options.VPCNames = "test1" + + _, _ = client.CreateVPC(context.TODO(), linodego.VPCCreateOptions{ + Label: "test1", + Description: "", + Region: "us-west", + Subnets: []linodego.VPCSubnetCreateOptions{ + { + Label: "default", + IPv4: "10.0.0.0/8", + }, + }, + }) + + _, _ = client.CreateVPC(context.TODO(), linodego.VPCCreateOptions{ + Label: "test2", + Description: "", + Region: "us-west", + Subnets: []linodego.VPCSubnetCreateOptions{ + { + Label: "subnet1", + IPv4: "10.0.0.0/8", + }, + }, + }) + ann := map[string]string{ + annotations.NodeBalancerBackendIPv4Range: "10.100.0.0/30", + annotations.NodeBalancerBackendVPCName: "test2", + //annotations.NodeBalancerBackendSubnetName: "subnet1", + } + err := testCreateNodeBalancer(t, client, f, ann, nil) + if err != nil { + t.Fatalf("expected a nil error, got %v", err) + } +} + func testUpdateLoadBalancerAddNode(t *testing.T, client *linodego.Client, f *fakeAPI) { svc := &v1.Service{ ObjectMeta: metav1.ObjectMeta{