pkg/cloud/rgraph/testing/ez/factories.go (475 lines of code) (raw):

/* Copyright 2023 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package ez import ( "strings" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/rgraph/rnode" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/rgraph/rnode/address" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/rgraph/rnode/backendservice" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/rgraph/rnode/fake" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/rgraph/rnode/forwardingrule" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/rgraph/rnode/healthcheck" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/rgraph/rnode/networkendpointgroup" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/rgraph/rnode/targethttpproxy" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/rgraph/rnode/tcproute" "github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/rgraph/rnode/urlmap" "google.golang.org/api/compute/v1" "google.golang.org/api/networkservices/v1" ) var ( allNodeFactories = []nodeFactory{ addressFactory{}, backendServiceFactory{}, fakeFactory{}, forwardingRuleFactory{}, healthCheckFactory{}, negFactory{}, targetHttpProxyFactory{}, urlMapFactory{}, tcpRouteFactory{}, } ) func getFactory(name string) nodeFactory { for _, nf := range allNodeFactories { if nf.match(name) { return nf } } panicf("getFactory: invalid name: %q", name) panic("not reached") } type nodeFactory interface { match(name string) bool id(g *Graph, n *Node) *cloud.ResourceID builder(g *Graph, n *Node) rnode.Builder } func getProject(g *Graph, n *Node) string { if n.Project != "" { return n.Project } if g.Project != "" { return g.Project } return "test-project" } func setCommonOptions(n *Node, b rnode.Builder) { b.SetOwnership(rnode.OwnershipManaged) switch { case n.Options&External != 0: b.SetOwnership(rnode.OwnershipExternal) case n.Options&Managed != 0: b.SetOwnership(rnode.OwnershipManaged) } b.SetState(rnode.NodeExists) switch { case n.Options&Exists != 0: case n.Options&DoesNotExist != 0: b.SetState(rnode.NodeDoesNotExist) } } type addressFactory struct{} func (addressFactory) match(name string) bool { return strings.HasPrefix(name, "addr") } func (addressFactory) id(g *Graph, n *Node) *cloud.ResourceID { switch { case n.Region == "" && n.Zone == "": return address.ID(getProject(g, n), meta.GlobalKey(n.Name)) case n.Region != "": return address.ID(getProject(g, n), meta.RegionalKey(n.Name, n.Region)) default: panicf("addressFactory: invalid scope: %+v", n) } panic("not reached") } func (f addressFactory) builder(g *Graph, n *Node) rnode.Builder { id := f.id(g, n) b := address.NewBuilder(id) setCommonOptions(n, b) if b.State() == rnode.NodeExists { ma := address.NewMutableAddress(id.ProjectID, id.Key) err := ma.Access(func(x *compute.Address) { if n.SetupFunc != nil { sf, ok := n.SetupFunc.(func(x *compute.Address)) if !ok { panicf("addressFactory: invalid type for SetupFunc: %T", n.SetupFunc) } sf(x) } }) if g.Options&PanicOnAccessErr != 0 && err != nil { panicf("addressFactory %s: Access: %v", id, err) } r, err := ma.Freeze() if err != nil { panicf("addressFactory: Freeze: %v", err) } err = b.SetResource(r) if err != nil { panicf("addressFactory: SetResource: %v", err) } } return b } type backendServiceFactory struct{} func (backendServiceFactory) match(name string) bool { return strings.HasPrefix(name, "bs") } func (backendServiceFactory) id(g *Graph, n *Node) *cloud.ResourceID { switch { case n.Region == "" && n.Zone == "": return backendservice.ID(getProject(g, n), meta.GlobalKey(n.Name)) case n.Region != "": return backendservice.ID(getProject(g, n), meta.RegionalKey(n.Name, n.Region)) default: panicf("backendServiceFactory: invalid scope: %+v", n) } panic("not reached") } func (f backendServiceFactory) builder(g *Graph, n *Node) rnode.Builder { id := f.id(g, n) b := backendservice.NewBuilder(id) setCommonOptions(n, b) if b.State() == rnode.NodeExists { ma := backendservice.NewMutableBackendService(id.ProjectID, id.Key) err := ma.Access(func(x *compute.BackendService) { for _, ref := range n.Refs { switch ref.Field { case "Backends.Group": backend := &compute.Backend{ Group: g.ids.selfLink(ref.To), } x.Backends = append(x.Backends, backend) case "Healthchecks": x.HealthChecks = append(x.HealthChecks, g.ids.selfLink(ref.To)) default: panicf("invalid Ref Field: %q (must be one of [Backends.Group, HealthChecks])", ref.Field) } } if n.SetupFunc != nil { sf, ok := n.SetupFunc.(func(x *compute.BackendService)) if !ok { panicf("invalid type for SetupFunc: %T", n.SetupFunc) } sf(x) } }) if g.Options&PanicOnAccessErr != 0 && err != nil { panicf("backendServiceFactory %s: Access: %v", id, err) } r, err := ma.Freeze() if err != nil { panic(err) } err = b.SetResource(r) if err != nil { panic(err) } } return b } type fakeFactory struct{} func (fakeFactory) match(name string) bool { return strings.HasPrefix(name, "fake") } func (fakeFactory) id(g *Graph, n *Node) *cloud.ResourceID { switch { case n.Region == "" && n.Zone == "": return fake.ID(getProject(g, n), meta.GlobalKey(n.Name)) case n.Region != "": return fake.ID(getProject(g, n), meta.RegionalKey(n.Name, n.Region)) case n.Zone != "": return fake.ID(getProject(g, n), meta.ZonalKey(n.Name, n.Zone)) default: panicf("invalid id: %+v", n) } panic("not reached") } func (f fakeFactory) builder(g *Graph, n *Node) rnode.Builder { return fake.NewBuilder(f.id(g, n)) } type forwardingRuleFactory struct{} func (forwardingRuleFactory) match(name string) bool { return strings.HasPrefix(name, "fr") } func (forwardingRuleFactory) id(g *Graph, n *Node) *cloud.ResourceID { switch { case n.Region == "" && n.Zone == "": return forwardingrule.ID(getProject(g, n), meta.GlobalKey(n.Name)) case n.Region != "": return forwardingrule.ID(getProject(g, n), meta.RegionalKey(n.Name, n.Region)) default: panicf("invalid id: %+v", n) } panic("not reached") } func (f forwardingRuleFactory) builder(g *Graph, n *Node) rnode.Builder { id := f.id(g, n) b := forwardingrule.NewBuilder(id) setCommonOptions(n, b) if b.State() == rnode.NodeExists { ma := forwardingrule.NewMutableForwardingRule(id.ProjectID, id.Key) err := ma.Access(func(x *compute.ForwardingRule) { for _, ref := range n.Refs { switch ref.Field { case "IPAddress": x.IPAddress = g.ids.selfLink(ref.To) case "Target": x.Target = g.ids.selfLink(ref.To) default: panicf("invalid Ref Field: %q (must be one of [IPAddress,Target])", ref.Field) } } if n.SetupFunc != nil { sf, ok := n.SetupFunc.(func(x *compute.ForwardingRule)) if !ok { panicf("invalid type for SetupFunc: %T", n.SetupFunc) } sf(x) } }) if g.Options&PanicOnAccessErr != 0 && err != nil { panicf("forwardingRuleFactory %s: Access: %v", id, err) } r, err := ma.Freeze() if err != nil { panic(err) } err = b.SetResource(r) if err != nil { panic(err) } } return b } type healthCheckFactory struct{} func (healthCheckFactory) match(name string) bool { return strings.HasPrefix(name, "hc") } func (healthCheckFactory) id(g *Graph, n *Node) *cloud.ResourceID { switch { case n.Region == "" && n.Zone == "": return healthcheck.ID(getProject(g, n), meta.GlobalKey(n.Name)) case n.Region != "": return healthcheck.ID(getProject(g, n), meta.RegionalKey(n.Name, n.Region)) default: panicf("invalid id: %+v", n) } panic("not reached") } func (f healthCheckFactory) builder(g *Graph, n *Node) rnode.Builder { id := f.id(g, n) b := healthcheck.NewBuilder(id) setCommonOptions(n, b) if b.State() == rnode.NodeExists { ma := healthcheck.NewMutableHealthCheck(id.ProjectID, id.Key) err := ma.Access(func(x *compute.HealthCheck) { if n.SetupFunc != nil { sf, ok := n.SetupFunc.(func(x *compute.HealthCheck)) if !ok { panicf("invalid type for SetupFunc: %T", n.SetupFunc) } sf(x) } }) if g.Options&PanicOnAccessErr != 0 && err != nil { panicf("healthCheckFactory %s: Access: %v", id, err) } r, err := ma.Freeze() if err != nil { panic(err) } err = b.SetResource(r) if err != nil { panic(err) } } return b } type negFactory struct{} func (negFactory) match(name string) bool { return strings.HasPrefix(name, "neg") } func (negFactory) id(g *Graph, n *Node) *cloud.ResourceID { switch { case n.Zone != "": return networkendpointgroup.ID(getProject(g, n), meta.ZonalKey(n.Name, n.Zone)) default: panicf("invalid id: %+v", n) } panic("not reached") } func (f negFactory) builder(g *Graph, n *Node) rnode.Builder { id := f.id(g, n) b := networkendpointgroup.NewBuilder(id) setCommonOptions(n, b) if b.State() == rnode.NodeExists { ma := networkendpointgroup.NewMutableNetworkEndpointGroup(id.ProjectID, id.Key) err := ma.Access(func(x *compute.NetworkEndpointGroup) { if n.SetupFunc != nil { sf, ok := n.SetupFunc.(func(x *compute.NetworkEndpointGroup)) if !ok { panicf("invalid type for SetupFunc: %T", n.SetupFunc) } sf(x) } }) if g.Options&PanicOnAccessErr != 0 && err != nil { panicf("negFactory %s: Access: %v", id, err) } r, err := ma.Freeze() if err != nil { panic(err) } err = b.SetResource(r) if err != nil { panic(err) } } return b } type targetHttpProxyFactory struct{} func (targetHttpProxyFactory) match(name string) bool { return strings.HasPrefix(name, "thp") } func (targetHttpProxyFactory) id(g *Graph, n *Node) *cloud.ResourceID { switch { case n.Region == "" && n.Zone == "": return targethttpproxy.ID(getProject(g, n), meta.GlobalKey(n.Name)) case n.Region != "": return targethttpproxy.ID(getProject(g, n), meta.RegionalKey(n.Name, n.Region)) default: panicf("invalid id: %+v", n) } panic("not reached") } func (f targetHttpProxyFactory) builder(g *Graph, n *Node) rnode.Builder { id := f.id(g, n) b := targethttpproxy.NewBuilder(id) setCommonOptions(n, b) if b.State() == rnode.NodeExists { ma := targethttpproxy.NewMutableTargetHttpProxy(id.ProjectID, id.Key) err := ma.Access(func(x *compute.TargetHttpProxy) { for _, ref := range n.Refs { switch ref.Field { case "UrlMap": x.UrlMap = g.ids.selfLink(ref.To) default: panicf("invalid Ref Field: %q (must be one of [UrlMap])", ref.Field) } } if n.SetupFunc != nil { sf, ok := n.SetupFunc.(func(x *compute.TargetHttpProxy)) if !ok { panicf("invalid type for SetupFunc: %T", n.SetupFunc) } sf(x) } }) if g.Options&PanicOnAccessErr != 0 && err != nil { panicf("targetHttpProxyFactory %s: Access: %v", id, err) } r, err := ma.Freeze() if err != nil { panic(err) } err = b.SetResource(r) if err != nil { panic(err) } } return b } type urlMapFactory struct{} func (urlMapFactory) match(name string) bool { return strings.HasPrefix(name, "um") } func (urlMapFactory) id(g *Graph, n *Node) *cloud.ResourceID { switch { case n.Region == "" && n.Zone == "": return urlmap.ID(getProject(g, n), meta.GlobalKey(n.Name)) case n.Region != "": return urlmap.ID(getProject(g, n), meta.RegionalKey(n.Name, n.Region)) default: panicf("invalid id: %+v", n) } panic("not reached") } func (f urlMapFactory) builder(g *Graph, n *Node) rnode.Builder { id := f.id(g, n) b := urlmap.NewBuilder(id) setCommonOptions(n, b) if b.State() == rnode.NodeExists { ma := urlmap.NewMutableUrlMap(id.ProjectID, id.Key) err := ma.Access(func(x *compute.UrlMap) { for _, ref := range n.Refs { switch ref.Field { case "DefaultService": x.DefaultService = g.ids.selfLink(ref.To) default: panicf("invalid Ref Field: %q (must be one of [DefaultService])", ref.Field) } } if n.SetupFunc != nil { sf, ok := n.SetupFunc.(func(x *compute.UrlMap)) if !ok { panicf("invalid type for SetupFunc: %T", n.SetupFunc) } sf(x) } }) if g.Options&PanicOnAccessErr != 0 && err != nil { panicf("urlMapFactory %s: Access: %v", id, err) } r, err := ma.Freeze() if err != nil { panic(err) } err = b.SetResource(r) if err != nil { panic(err) } } return b } type tcpRouteFactory struct{} func (tcpRouteFactory) match(name string) bool { return strings.HasPrefix(name, "tcp-route") } func (tcpRouteFactory) id(g *Graph, n *Node) *cloud.ResourceID { switch { case n.Region == "" && n.Zone == "": return tcproute.ID(getProject(g, n), meta.GlobalKey(n.Name)) default: panicf("invalid id: %+v", n) } panic("not reached") } func (f tcpRouteFactory) builder(g *Graph, n *Node) rnode.Builder { id := f.id(g, n) b := tcproute.NewBuilder(id) setCommonOptions(n, b) if b.State() == rnode.NodeExists { ma := tcproute.NewMutableTcpRoute(id.ProjectID, id.Key) err := ma.Access(func(x *networkservices.TcpRoute) { for _, ref := range n.Refs { switch ref.Field { case "Rules.Action.Destinations.ServiceName": if len(x.Rules) == 0 || x.Rules[0] == nil { trrr := &networkservices.TcpRouteRouteRule{ Action: &networkservices.TcpRouteRouteAction{}, } x.Rules = []*networkservices.TcpRouteRouteRule{trrr} } dst := &networkservices.TcpRouteRouteDestination{ ServiceName: g.ids.legacySelfLink(ref.To), } x.Rules[0].Action.Destinations = append(x.Rules[0].Action.Destinations, dst) default: panicf("invalid Ref Field: %q (must be one of [Rules.Destinations.ServiceName])", ref.Field) } } if n.SetupFunc != nil { sf, ok := n.SetupFunc.(func(x *networkservices.TcpRoute)) if !ok { panicf("invalid type for SetupFunc: %T", n.SetupFunc) } sf(x) } }) if g.Options&PanicOnAccessErr != 0 && err != nil { panicf("tcpRouteFactory %s: Access: %v", id, err) } r, err := ma.Freeze() if err != nil { panic(err) } err = b.SetResource(r) if err != nil { panic(err) } } return b }