diff --git a/Makefile b/Makefile index 4bce06f1ef..d49012741a 100644 --- a/Makefile +++ b/Makefile @@ -101,7 +101,7 @@ ifndef HAS_YQ endif ifndef HAS_KUSTOMIZE echo "installing kustomize" - go get sigs.k8s.io/kustomize/v3/cmd/kustomize + GO111MODULE=off go get sigs.k8s.io/kustomize/kustomize endif ifndef HAS_ENVSUBST echo "installing envsubst" diff --git a/api/v1alpha2/openstackcluster_types.go b/api/v1alpha2/openstackcluster_types.go index 7442cb253d..894dc1cea9 100644 --- a/api/v1alpha2/openstackcluster_types.go +++ b/api/v1alpha2/openstackcluster_types.go @@ -42,6 +42,13 @@ type OpenStackClusterSpec struct { // network, a subnet with NodeCIDR, and a router connected to this subnet. // If you leave this empty, no network will be created. NodeCIDR string `json:"nodeCidr,omitempty"` + + // If NodeCIDR cannot be set this can be used to detect an existing network. + Network Filter `json:"network,omitempty"` + + // If NodeCIDR cannot be set this can be used to detect an existing subnet. + Subnet SubnetFilter `json:"subnet,omitempty"` + // DNSNameservers is the list of nameservers for OpenStack Subnet being created. DNSNameservers []string `json:"dnsNameservers,omitempty"` // ExternalRouterIPs is an array of externalIPs on the respective subnets. diff --git a/api/v1alpha2/zz_generated.deepcopy.go b/api/v1alpha2/zz_generated.deepcopy.go index 82410986ab..fdbb4994ee 100644 --- a/api/v1alpha2/zz_generated.deepcopy.go +++ b/api/v1alpha2/zz_generated.deepcopy.go @@ -242,6 +242,8 @@ func (in *OpenStackClusterSpec) DeepCopyInto(out *OpenStackClusterSpec) { *out = new(v1.SecretReference) **out = **in } + in.Network.DeepCopyInto(&out.Network) + in.Subnet.DeepCopyInto(&out.Subnet) if in.DNSNameservers != nil { in, out := &in.DNSNameservers, &out.DNSNameservers *out = make([]string, len(*in)) diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml index 6dbf2a61ec..bdf770a71e 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_openstackclusters.yaml @@ -192,6 +192,43 @@ spec: and another one that allows all traffic to/from machines belonging to that group. In the future, we could make this more flexible. type: boolean + network: + description: If NodeCIDR cannot be set this can be used to detect an + existing network. + properties: + adminStateUp: + type: boolean + description: + type: string + id: + type: string + limit: + type: integer + marker: + type: string + name: + type: string + notTags: + type: string + notTagsAny: + type: string + projectId: + type: string + shared: + type: boolean + sortDir: + type: string + sortKey: + type: string + status: + type: string + tags: + type: string + tagsAny: + type: string + tenantId: + type: string + type: object nodeCidr: description: NodeCIDR is the OpenStack Subnet to be created. Cluster actuator will create a network, a subnet with NodeCIDR, and a router @@ -209,6 +246,53 @@ spec: format: byte type: string type: object + subnet: + description: If NodeCIDR cannot be set this can be used to detect an + existing subnet. + properties: + cidr: + type: string + description: + type: string + enableDhcp: + type: boolean + gateway_ip: + type: string + id: + type: string + ipVersion: + type: integer + ipv6AddressMode: + type: string + ipv6RaMode: + type: string + limit: + type: integer + marker: + type: string + name: + type: string + networkId: + type: string + notTags: + type: string + notTagsAny: + type: string + projectId: + type: string + sortDir: + type: string + sortKey: + type: string + subnetpoolId: + type: string + tags: + type: string + tagsAny: + type: string + tenantId: + type: string + type: object tags: description: Tags for all resources in cluster items: diff --git a/controllers/openstackcluster_controller.go b/controllers/openstackcluster_controller.go index 987fad56e9..b84b4fe288 100644 --- a/controllers/openstackcluster_controller.go +++ b/controllers/openstackcluster_controller.go @@ -18,6 +18,8 @@ package controllers import ( "context" "fmt" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" + "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -129,7 +131,35 @@ func (r *OpenStackClusterReconciler) reconcileCluster(logger logr.Logger, cluste logger.Info("Reconciling network components") if openStackCluster.Spec.NodeCIDR == "" { - logger.V(4).Info("No need to reconcile network") + logger.V(4).Info("No need to reconcile network, searching network and subnet instead") + + netOpts := networks.ListOpts(openStackCluster.Spec.Network) + networkList, err := networkingService.GetNetworksByFilter(&netOpts) + if err != nil && len(networkList) == 0 { + return reconcile.Result{}, errors.Errorf("failed to find network: %v", err) + } + if len(networkList) > 1 { + return reconcile.Result{}, errors.Errorf("failed to find only one network (result: %v): %v", networkList, err) + } + openStackCluster.Status.Network = &infrav1.Network{ + ID: networkList[0].ID, + Name: networkList[0].Name, + } + + subnetOpts := subnets.ListOpts(openStackCluster.Spec.Subnet) + subnetOpts.NetworkID = networkList[0].ID + subnetList, err := networkingService.GetSubnetsByFilter(&subnetOpts) + if err != nil || len(subnetList) == 0 { + return reconcile.Result{}, errors.Errorf("failed to find subnet: %v", err) + } + if len(subnetList) > 1 { + return reconcile.Result{}, errors.Errorf("failed to find only one subnet (result: %v): %v", subnetList, err) + } + openStackCluster.Status.Network.Subnet = &infrav1.Subnet{ + ID: subnetList[0].ID, + Name: subnetList[0].Name, + CIDR: subnetList[0].CIDR, + } } else { err := networkingService.ReconcileNetwork(clusterName, openStackCluster) if err != nil { diff --git a/examples/controlplane/controlplane.yaml b/examples/controlplane/controlplane.yaml index e3c22eb4ee..d58a4408a2 100644 --- a/examples/controlplane/controlplane.yaml +++ b/examples/controlplane/controlplane.yaml @@ -33,12 +33,6 @@ spec: image: sshKeyName: cluster-api-provider-openstack availabilityZone: nova - networks: - - filter: - name: k8s-clusterapi-cluster-${CLUSTER_NAME}-${CLUSTER_NAME} - subnets: - - filter: - name: k8s-clusterapi-cluster-${CLUSTER_NAME}-${CLUSTER_NAME} # multi-node control-plane: # * Disable the floatingIP property # single-node control-plane: @@ -148,12 +142,6 @@ spec: image: sshKeyName: cluster-api-provider-openstack availabilityZone: nova - networks: - - filter: - name: k8s-clusterapi-cluster-${CLUSTER_NAME}-${CLUSTER_NAME} - subnets: - - filter: - name: k8s-clusterapi-cluster-${CLUSTER_NAME}-${CLUSTER_NAME} cloudName: $CLOUD cloudsSecret: name: cloud-config @@ -229,12 +217,6 @@ spec: image: sshKeyName: cluster-api-provider-openstack availabilityZone: nova - networks: - - filter: - name: k8s-clusterapi-cluster-${CLUSTER_NAME}-${CLUSTER_NAME} - subnets: - - filter: - name: k8s-clusterapi-cluster-${CLUSTER_NAME}-${CLUSTER_NAME} cloudName: $CLOUD cloudsSecret: name: cloud-config diff --git a/examples/machinedeployment/machinedeployment.yaml b/examples/machinedeployment/machinedeployment.yaml index 333cf77bd4..ae590d9800 100644 --- a/examples/machinedeployment/machinedeployment.yaml +++ b/examples/machinedeployment/machinedeployment.yaml @@ -49,12 +49,6 @@ spec: namespace: ${CLUSTER_NAME} flavor: m1.medium image: - networks: - - filter: - name: k8s-clusterapi-cluster-${CLUSTER_NAME}-${CLUSTER_NAME} - subnets: - - filter: - name: k8s-clusterapi-cluster-${CLUSTER_NAME}-${CLUSTER_NAME} sshKeyName: cluster-api-provider-openstack --- apiVersion: bootstrap.cluster.x-k8s.io/v1alpha2 diff --git a/pkg/cloud/services/compute/instance.go b/pkg/cloud/services/compute/instance.go index 8630439592..f0e4e3a197 100644 --- a/pkg/cloud/services/compute/instance.go +++ b/pkg/cloud/services/compute/instance.go @@ -27,7 +27,6 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups" - "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/common/extensions" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces" "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume" @@ -41,7 +40,6 @@ import ( "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" - "github.com/gophercloud/gophercloud/pagination" infrav1 "sigs.k8s.io/cluster-api-provider-openstack/api/v1alpha2" "sigs.k8s.io/cluster-api/util" ) @@ -99,36 +97,51 @@ func (s *Service) InstanceCreate(clusterName string, machine *clusterv1.Machine, } // Get all network UUIDs var nets []ServerNetwork - for _, net := range openStackMachine.Spec.Networks { - opts := networks.ListOpts(net.Filter) - opts.ID = net.UUID - ids, err := getNetworkIDsByFilter(s.networkClient, &opts) - if err != nil { - return nil, err - } - for _, netID := range ids { - if net.Subnets == nil { - nets = append(nets, ServerNetwork{ - networkID: netID, - }) + if len(openStackMachine.Spec.Networks) > 0 { + for _, net := range openStackMachine.Spec.Networks { + opts := networks.ListOpts(net.Filter) + opts.ID = net.UUID + ids, err := networking.GetNetworkIDsByFilter(s.networkClient, &opts) + if err != nil { + return nil, err } - - for _, subnet := range net.Subnets { - subnetOpts := subnets.ListOpts(subnet.Filter) - subnetOpts.ID = subnet.UUID - subnetOpts.NetworkID = netID - subnetsByFilter, err := networking.GetSubnetsByFilter(s.networkClient, &subnetOpts) - if err != nil { - return nil, err - } - for _, subnetByFilter := range subnetsByFilter { + for _, netID := range ids { + if net.Subnets == nil { nets = append(nets, ServerNetwork{ - networkID: subnetByFilter.NetworkID, - subnetID: subnetByFilter.ID, + networkID: netID, }) + continue + } + + for _, subnet := range net.Subnets { + subnetOpts := subnets.ListOpts(subnet.Filter) + subnetOpts.ID = subnet.UUID + subnetOpts.NetworkID = netID + subnetsByFilter, err := networking.GetSubnetsByFilter(s.networkClient, &subnetOpts) + if err != nil { + return nil, err + } + for _, subnetByFilter := range subnetsByFilter { + nets = append(nets, ServerNetwork{ + networkID: subnetByFilter.NetworkID, + subnetID: subnetByFilter.ID, + }) + } } } } + } else { + if openStackCluster.Status.Network == nil { + return nil, fmt.Errorf(".spec.networks not set in Machine and also no network was found in .status.network in OpenStackCluster") + } + if openStackCluster.Status.Network.Subnet == nil { + return nil, fmt.Errorf(".spec.networks not set in Machine and also no subnet was found in .status.network.subnet in OpenStackCluster") + } + + nets = []ServerNetwork{{ + networkID: openStackCluster.Status.Network.ID, + subnetID: openStackCluster.Status.Network.Subnet.ID, + }} } if len(nets) == 0 { return nil, fmt.Errorf("no network was found or provided. Please check your machine configuration and try again") @@ -324,31 +337,6 @@ func isDuplicate(list []string, name string) bool { return false } -// A function for getting the id of a network by querying openstack with filters -func getNetworkIDsByFilter(networkClient *gophercloud.ServiceClient, opts networks.ListOptsBuilder) ([]string, error) { - if opts == nil { - return []string{}, fmt.Errorf("no Filters were passed") - } - pager := networks.List(networkClient, opts) - var uuids []string - err := pager.EachPage(func(page pagination.Page) (bool, error) { - networkList, err := networks.ExtractNetworks(page) - if err != nil { - return false, err - } else if len(networkList) == 0 { - return false, fmt.Errorf("no networks could be found with the filters provided") - } - for _, network := range networkList { - uuids = append(uuids, network.ID) - } - return true, nil - }) - if err != nil { - return []string{}, err - } - return uuids, nil -} - func createPort(is *Service, name string, net ServerNetwork, securityGroups *[]string) (ports.Port, error) { portCreateOpts := ports.CreateOpts{ Name: name, diff --git a/pkg/cloud/services/networking/router.go b/pkg/cloud/services/networking/router.go index abbdae828f..7e5618c333 100644 --- a/pkg/cloud/services/networking/router.go +++ b/pkg/cloud/services/networking/router.go @@ -18,6 +18,7 @@ package networking import ( "fmt" + "github.com/gophercloud/gophercloud/openstack/networking/v2/networks" "sigs.k8s.io/cluster-api-provider-openstack/pkg/record" "github.com/gophercloud/gophercloud" @@ -180,6 +181,50 @@ func (s *Service) getRouterInterfaces(routerID string) ([]ports.Port, error) { return portList, nil } +func (s *Service) GetNetworksByFilter(opts networks.ListOptsBuilder) ([]networks.Network, error) { + return GetNetworksByFilter(s.client, opts) +} + +// getNetworkIDsByFilter retrieves network ids by querying openstack with filters +func GetNetworkIDsByFilter(networkClient *gophercloud.ServiceClient, opts networks.ListOptsBuilder) ([]string, error) { + nets, err := GetNetworksByFilter(networkClient, opts) + if err != nil { + return nil, err + } + ids := []string{} + for _, network := range nets { + ids = append(ids, network.ID) + } + return ids, nil +} + +// GetNetworksByFilter retrieves networks by querying openstack with filters +func GetNetworksByFilter(networkClient *gophercloud.ServiceClient, opts networks.ListOptsBuilder) ([]networks.Network, error) { + if opts == nil { + return nil, fmt.Errorf("no Filters were passed") + } + pager := networks.List(networkClient, opts) + var nets []networks.Network + err := pager.EachPage(func(page pagination.Page) (bool, error) { + networkList, err := networks.ExtractNetworks(page) + if err != nil { + return false, err + } else if len(networkList) == 0 { + return false, fmt.Errorf("no networks could be found with the filters provided") + } + nets = networkList + return true, nil + }) + if err != nil { + return nil, err + } + return nets, nil +} + +func (s *Service) GetSubnetsByFilter(opts subnets.ListOptsBuilder) ([]subnets.Subnet, error) { + return GetSubnetsByFilter(s.client, opts) +} + // A function for getting the id of a subnet by querying openstack with filters func GetSubnetsByFilter(networkClient *gophercloud.ServiceClient, opts subnets.ListOptsBuilder) ([]subnets.Subnet, error) { if opts == nil {