diff --git a/Makefile b/Makefile index e3a11d4a7..b2efceb43 100644 --- a/Makefile +++ b/Makefile @@ -128,6 +128,7 @@ cd $$TMP_DIR ;\ go mod init tmp ;\ echo "Downloading $(2)" ;\ GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\ +GOBIN=$(PROJECT_DIR)/bin go install $(2) ;\ rm -rf $$TMP_DIR ;\ } endef diff --git a/PROJECT b/PROJECT index a70b72593..a3ee540b0 100644 --- a/PROJECT +++ b/PROJECT @@ -27,4 +27,12 @@ resources: kind: SubnetPort path: github.com/vmware-tanzu/nsx-operator/pkg/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: nsx.vmware.com + kind: NSXServiceAccount + path: github.com/vmware-tanzu/nsx-operator/pkg/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/build/image/photon/Dockerfile b/build/image/photon/Dockerfile index bb278ed75..f78a2e612 100644 --- a/build/image/photon/Dockerfile +++ b/build/image/photon/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.17.7 as golang-build +FROM golang:1.19.5 as golang-build WORKDIR /source diff --git a/build/yaml/crd/nsx.vmware.com_nsxserviceaccounts.yaml b/build/yaml/crd/nsx.vmware.com_nsxserviceaccounts.yaml new file mode 100644 index 000000000..3e38b5d1c --- /dev/null +++ b/build/yaml/crd/nsx.vmware.com_nsxserviceaccounts.yaml @@ -0,0 +1,106 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: nsxserviceaccounts.nsx.vmware.com +spec: + group: nsx.vmware.com + names: + kind: NSXServiceAccount + listKind: NSXServiceAccountList + plural: nsxserviceaccounts + singular: nsxserviceaccount + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: NSXServiceAccount is the Schema for the nsxserviceaccounts API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: NSXServiceAccountSpec defines the desired state of NSXServiceAccount + properties: + vpcName: + type: string + type: object + status: + description: NSXServiceAccountStatus defines the observed state of NSXServiceAccount + properties: + clusterID: + type: string + clusterName: + type: string + nsxManagers: + items: + type: string + type: array + phase: + type: string + proxyEndpoints: + properties: + addresses: + items: + properties: + hostname: + type: string + ip: + format: ip + type: string + type: object + type: array + ports: + items: + properties: + name: + type: string + port: + type: integer + protocol: + type: string + type: object + type: array + type: object + reason: + type: string + secrets: + items: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + type: array + vpcPath: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/build/yaml/crd/nsx.vmware.com_subnets.yaml b/build/yaml/crd/nsx.vmware.com_subnets.yaml index fbe0b9e07..422f256bd 100644 --- a/build/yaml/crd/nsx.vmware.com_subnets.yaml +++ b/build/yaml/crd/nsx.vmware.com_subnets.yaml @@ -36,6 +36,37 @@ spec: spec: description: SubnetSpec defines the desired state of Subnet. properties: + DHCPConfig: + description: DHCPConfig DHCP configuration. + properties: + dhcpRelayConfigPath: + description: DHCPRelayConfigPath is policy path of DHCP-relay-config. + type: string + dhcpV4PoolSize: + default: 80 + description: DHCPV4PoolSize IPs in % to be reserved for DHCP ranges. + By default, 80% of IPv4 IPs will be reserved for DHCP. Configure + 0 if no pool is required. + maximum: 100 + minimum: 0 + type: integer + dhcpV6PoolSize: + default: 2000 + description: DHCPV6PoolSize number of IPs to be reserved for DHCP + ranges. By default, 2000 IPv6 IPs will be reserved for DHCP. + type: integer + dnsClientConfig: + description: DNSClientConfig holds DNS configurations. + properties: + dnsServersIPs: + items: + type: string + type: array + type: object + enableDHCP: + default: false + type: boolean + type: object accessMode: default: private description: Access mode of Subnet, accessible only from within VPC @@ -44,6 +75,20 @@ spec: - private - public type: string + advancedConfig: + description: Subnet advanced configuration. + properties: + staticIPAllocation: + description: StaticIPAllocation configuration for subnet ports + with VIF attachment. + properties: + enable: + default: false + description: Enable or disable static IP allocation for subnet + ports with VIF attachment. + type: boolean + type: object + type: object ipAddresses: description: Subnet CIDRS. items: @@ -55,6 +100,8 @@ spec: default: 64 description: Size of Subnet based upon estimated workload count. Defaults to 64. + maximum: 65536 + minimum: 16 type: integer type: object status: diff --git a/build/yaml/crd/nsx.vmware.com_subnetsets.yaml b/build/yaml/crd/nsx.vmware.com_subnetsets.yaml index 0ec5d194b..fe4e4ef24 100644 --- a/build/yaml/crd/nsx.vmware.com_subnetsets.yaml +++ b/build/yaml/crd/nsx.vmware.com_subnetsets.yaml @@ -36,6 +36,37 @@ spec: spec: description: SubnetSetSpec defines the desired state of SubnetSet. properties: + DHCPConfig: + description: DHCPConfig DHCP configuration. + properties: + dhcpRelayConfigPath: + description: DHCPRelayConfigPath is policy path of DHCP-relay-config. + type: string + dhcpV4PoolSize: + default: 80 + description: DHCPV4PoolSize IPs in % to be reserved for DHCP ranges. + By default, 80% of IPv4 IPs will be reserved for DHCP. Configure + 0 if no pool is required. + maximum: 100 + minimum: 0 + type: integer + dhcpV6PoolSize: + default: 2000 + description: DHCPV6PoolSize number of IPs to be reserved for DHCP + ranges. By default, 2000 IPv6 IPs will be reserved for DHCP. + type: integer + dnsClientConfig: + description: DNSClientConfig holds DNS configurations. + properties: + dnsServersIPs: + items: + type: string + type: array + type: object + enableDHCP: + default: false + type: boolean + type: object accessMode: default: private description: Access mode of Subnet, accessible only from within VPC @@ -44,10 +75,26 @@ spec: - private - public type: string + advancedConfig: + description: Subnet advanced configuration. + properties: + staticIPAllocation: + description: StaticIPAllocation configuration for subnet ports + with VIF attachment. + properties: + enable: + default: false + description: Enable or disable static IP allocation for subnet + ports with VIF attachment. + type: boolean + type: object + type: object ipv4SubnetSize: default: 64 description: Size of Subnet based upon estimated workload count. Defaults to 64. + maximum: 65536 + minimum: 16 type: integer type: object status: diff --git a/build/yaml/crd/nsx.vmware.com_vpcnetworkconfigurations.yaml b/build/yaml/crd/nsx.vmware.com_vpcnetworkconfigurations.yaml index 847567856..6dd83f177 100644 --- a/build/yaml/crd/nsx.vmware.com_vpcnetworkconfigurations.yaml +++ b/build/yaml/crd/nsx.vmware.com_vpcnetworkconfigurations.yaml @@ -54,12 +54,6 @@ spec: When a field is not set in a Namespace's VPCNetworkConfiguration, the Namespace will use the value in the default VPCNetworkConfiguration. properties: - appliedToNamespaces: - description: Namespaces to be applied. The default VPCNetworkConfiguration - will not have AppliedToNamespaces set. - items: - type: string - type: array defaultGatewayPath: description: PolicyPath of Tier0 or Tier0 VRF gateway. type: string diff --git a/cmd/main.go b/cmd/main.go index b405dc563..945859382 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -41,11 +41,13 @@ func init() { flag.Parse() var err error + logf.SetLogger(logger.ZapLogger()) cf, err = config.NewNSXOperatorConfigFromFile() if err != nil { + log.Error(err, "load config file error") os.Exit(1) } - logf.SetLogger(logger.ZapLogger()) + if metrics.AreMetricsExposed(cf) { metrics.InitializePrometheusMetrics() } diff --git a/go.mod b/go.mod index 0dd895311..5cd834101 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/vmware-tanzu/nsx-operator -go 1.17 +go 1.19 replace ( github.com/vmware/go-vmware-nsxt => github.com/mengdie-song/go-vmware-nsxt v0.0.0-20220921033217-dbd234470e30 // inventory-keeper includes all commits from github.com/gran-vmv/go-vmware-nsxt v0.0.0-20211206034609-cd7ffaf2c996 - github.com/vmware/vsphere-automation-sdk-go/lib => github.com/TaoZou1/vsphere-automation-sdk-go/lib v0.5.1-0.20221020082725-84e89979deb6 - github.com/vmware/vsphere-automation-sdk-go/runtime => github.com/TaoZou1/vsphere-automation-sdk-go/runtime v0.5.1-0.20221020082725-84e89979deb6 + github.com/vmware/vsphere-automation-sdk-go/lib => github.com/TaoZou1/vsphere-automation-sdk-go/lib v0.5.2 + github.com/vmware/vsphere-automation-sdk-go/runtime => github.com/TaoZou1/vsphere-automation-sdk-go/runtime v0.5.2 github.com/vmware/vsphere-automation-sdk-go/services/nsxt => github.com/TaoZou1/vsphere-automation-sdk-go/services/nsxt v0.9.3 ) @@ -19,21 +19,17 @@ require ( github.com/golang/mock v1.6.0 github.com/kevinburke/ssh_config v1.2.0 github.com/openlyinc/pointy v1.1.2 - github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.11.0 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.0 - github.com/vmware/go-vmware-nsxt v0.0.0-20220328155605-f49a14c1ef5f github.com/vmware/govmomi v0.27.4 github.com/vmware/vsphere-automation-sdk-go/lib v0.5.0 github.com/vmware/vsphere-automation-sdk-go/runtime v0.5.0 github.com/vmware/vsphere-automation-sdk-go/services/nsxt v0.6.0 go.uber.org/zap v1.19.1 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 - golang.org/x/mod v0.4.2 golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 gopkg.in/ini.v1 v1.66.4 - gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.23.4 k8s.io/apimachinery v0.23.4 k8s.io/client-go v0.23.4 @@ -41,7 +37,6 @@ require ( ) require ( - github.com/antihax/optional v1.0.0 // indirect github.com/beevik/etree v1.1.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect @@ -63,6 +58,7 @@ require ( github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.28.0 // indirect @@ -80,6 +76,7 @@ require ( google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.27.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0 // indirect k8s.io/apiextensions-apiserver v0.23.0 // indirect k8s.io/component-base v0.23.0 // indirect @@ -90,4 +87,3 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) - diff --git a/go.sum b/go.sum index 3980f3959..27913865a 100644 --- a/go.sum +++ b/go.sum @@ -53,16 +53,14 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/TaoZou1/vsphere-automation-sdk-go/lib v0.5.1-0.20221020082725-84e89979deb6 h1:kaBpRrC9+KxOvHTbZexQahAphJEYYBRu0F3E13aMeL8= -github.com/TaoZou1/vsphere-automation-sdk-go/lib v0.5.1-0.20221020082725-84e89979deb6/go.mod h1:QJG7MJEjc8SJuo6p++sLJha7Sqt0tzyxjK1peikCB/E= -github.com/TaoZou1/vsphere-automation-sdk-go/runtime v0.5.1-0.20221020082725-84e89979deb6 h1:FF+7dHI10Jyu6GX+GRL1h69qVZ/Tl9doswV4AgZSnWQ= -github.com/TaoZou1/vsphere-automation-sdk-go/runtime v0.5.1-0.20221020082725-84e89979deb6/go.mod h1:GqC85noyNzapJN4vIAO9jJ1EKVo3+jCW4/2VTaMvuSg= -github.com/TaoZou1/vsphere-automation-sdk-go/services/nsxt v0.9.2 h1:P6neTAWLvTeVmisPYTK5r2MY6OHANXTEd8ywWCyr3lY= -github.com/TaoZou1/vsphere-automation-sdk-go/services/nsxt v0.9.2/go.mod h1:9iS9n04yKlWA4AoVU6GpvMcFB7inP1D7kMyZb5RQVdM= +github.com/TaoZou1/vsphere-automation-sdk-go/lib v0.5.2 h1:y70ZbnJc68vXcZagNHNLM4oltM+0DtzijV/BnTDf//Y= +github.com/TaoZou1/vsphere-automation-sdk-go/lib v0.5.2/go.mod h1:QJG7MJEjc8SJuo6p++sLJha7Sqt0tzyxjK1peikCB/E= +github.com/TaoZou1/vsphere-automation-sdk-go/runtime v0.5.2 h1:RgDmwSyqCOfMAGTfIm+A1//jzF26u5fVQhWPBixdjfc= +github.com/TaoZou1/vsphere-automation-sdk-go/runtime v0.5.2/go.mod h1:GqC85noyNzapJN4vIAO9jJ1EKVo3+jCW4/2VTaMvuSg= +github.com/TaoZou1/vsphere-automation-sdk-go/services/nsxt v0.9.3 h1:u7Qr1LRqz98ouREoRE5rZA5O4l8TtMG3NYkrZFYigBc= +github.com/TaoZou1/vsphere-automation-sdk-go/services/nsxt v0.9.3/go.mod h1:9iS9n04yKlWA4AoVU6GpvMcFB7inP1D7kMyZb5RQVdM= github.com/a8m/tree v0.0.0-20210115125333-10a5fd5b637d/go.mod h1:FSdwKX97koS5efgm8WevNf7XS3PqtyFkKDDXrz778cg= github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw= -github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw= -github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw= github.com/agiledragon/gomonkey/v2 v2.4.0 h1:YDQJYiSQ8o78dCMXehU1E4F/Kh4jPX+MV+/iK/yfL7s= github.com/agiledragon/gomonkey/v2 v2.4.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY= @@ -71,7 +69,6 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210826220005-b48c857c3a0e/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= @@ -343,8 +340,6 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/mengdie-song/go-vmware-nsxt v0.0.0-20220921033217-dbd234470e30 h1:6/OYX9dRRIKuFzMB4ySDCjFpPXqt7tIvKQLpJDQFS8Q= -github.com/mengdie-song/go-vmware-nsxt v0.0.0-20220921033217-dbd234470e30/go.mod h1:VEqcmf4Sp7gPB7z05QGyKVmn6xWppr7Nz8cVNvyC80o= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -568,7 +563,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= diff --git a/pkg/apis/v1alpha1/nsxserviceaccount_types.go b/pkg/apis/v1alpha1/nsxserviceaccount_types.go new file mode 100644 index 000000000..2a8c97e38 --- /dev/null +++ b/pkg/apis/v1alpha1/nsxserviceaccount_types.go @@ -0,0 +1,86 @@ +/* Copyright © 2022 VMware, Inc. All Rights Reserved. + SPDX-License-Identifier: Apache-2.0 */ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// NSXServiceAccountSpec defines the desired state of NSXServiceAccount +type NSXServiceAccountSpec struct { + VPCName string `json:"vpcName,omitempty"` +} + +type NSXProxyEndpointAddress struct { + Hostname string `json:"hostname,omitempty"` + //+kubebuilder:validation:Format=ip + IP string `json:"ip,omitempty"` +} + +type NSXProxyProtocol string + +const ( + NSXProxyProtocolTCP NSXProxyProtocol = "TCP" +) + +type NSXProxyEndpointPort struct { + Name string `json:"name,omitempty"` + Port uint16 `json:"port,omitempty"` + Protocol NSXProxyProtocol `json:"protocol,omitempty"` +} + +type NSXProxyEndpoint struct { + Addresses []NSXProxyEndpointAddress `json:"addresses,omitempty"` + Ports []NSXProxyEndpointPort `json:"ports,omitempty"` +} + +type NSXSecret struct { + Name string `json:"name"` + Namespace string `json:"namespace"` +} + +type NSXServiceAccountPhase string + +const ( + NSXServiceAccountPhaseRealized NSXServiceAccountPhase = "realized" + NSXServiceAccountPhaseInProgress NSXServiceAccountPhase = "inProgress" + NSXServiceAccountPhaseFailed NSXServiceAccountPhase = "failed" +) + +// NSXServiceAccountStatus defines the observed state of NSXServiceAccount +type NSXServiceAccountStatus struct { + Phase NSXServiceAccountPhase `json:"phase,omitempty"` + Reason string `json:"reason,omitempty"` + VPCPath string `json:"vpcPath,omitempty"` + NSXManagers []string `json:"nsxManagers,omitempty"` + ProxyEndpoints NSXProxyEndpoint `json:"proxyEndpoints,omitempty"` + ClusterID string `json:"clusterID,omitempty"` + ClusterName string `json:"clusterName,omitempty"` + Secrets []NSXSecret `json:"secrets,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// NSXServiceAccount is the Schema for the nsxserviceaccounts API +type NSXServiceAccount struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NSXServiceAccountSpec `json:"spec,omitempty"` + Status NSXServiceAccountStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// NSXServiceAccountList contains a list of NSXServiceAccount +type NSXServiceAccountList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NSXServiceAccount `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NSXServiceAccount{}, &NSXServiceAccountList{}) +} diff --git a/pkg/apis/v1alpha1/subnet_types.go b/pkg/apis/v1alpha1/subnet_types.go index c99a32a7f..3c4d6d891 100644 --- a/pkg/apis/v1alpha1/subnet_types.go +++ b/pkg/apis/v1alpha1/subnet_types.go @@ -14,6 +14,8 @@ type SubnetSpec struct { // Size of Subnet based upon estimated workload count. // Defaults to 64. // +kubebuilder:default:=64 + // +kubebuilder:validation:Maximum:=65536 + // +kubebuilder:validation:Minimum:=16 IPv4SubnetSize int `json:"ipv4SubnetSize,omitempty"` // Access mode of Subnet, accessible only from within VPC or from outside VPC. // Defaults to private. @@ -24,6 +26,10 @@ type SubnetSpec struct { // +kubebuilder:validation:MinItems=0 // +kubebuilder:validation:MaxItems=2 IPAddresses []string `json:"ipAddresses,omitempty"` + // Subnet advanced configuration. + AdvancedConfig AdvancedConfig `json:"advancedConfig,omitempty"` + // DHCPConfig DHCP configuration. + DHCPConfig DHCPConfig `json:"DHCPConfig,omitempty"` } // SubnetStatus defines the observed state of Subnet. @@ -54,6 +60,44 @@ type SubnetList struct { Items []Subnet `json:"items"` } +// AdvancedConfig is Subnet advanced configuration. +type AdvancedConfig struct { + // StaticIPAllocation configuration for subnet ports with VIF attachment. + StaticIPAllocation StaticIPAllocation `json:"staticIPAllocation,omitempty"` +} + +// StaticIPAllocation is static IP allocation for subnet ports with VIF attachment. +type StaticIPAllocation struct { + // Enable or disable static IP allocation for subnet ports with VIF attachment. + // +kubebuilder:default:=false + Enable bool `json:"enable,omitempty"` +} + +// DHCPConfig is DHCP configuration. +type DHCPConfig struct { + // +kubebuilder:default:=false + EnableDHCP bool `json:"enableDHCP,omitempty"` + // DHCPRelayConfigPath is policy path of DHCP-relay-config. + DHCPRelayConfigPath string `json:"dhcpRelayConfigPath,omitempty"` + // DHCPV4PoolSize IPs in % to be reserved for DHCP ranges. + // By default, 80% of IPv4 IPs will be reserved for DHCP. + // Configure 0 if no pool is required. + // +kubebuilder:default:=80 + // +kubebuilder:validation:Maximum:=100 + // +kubebuilder:validation:Minimum:=0 + DHCPV4PoolSize int `json:"dhcpV4PoolSize,omitempty"` + // DHCPV6PoolSize number of IPs to be reserved for DHCP ranges. + // By default, 2000 IPv6 IPs will be reserved for DHCP. + // +kubebuilder:default:=2000 + DHCPV6PoolSize int `json:"dhcpV6PoolSize,omitempty"` + DNSClientConfig DNSClientConfig `json:"dnsClientConfig,omitempty"` +} + +// DNSClientConfig holds DNS configurations. +type DNSClientConfig struct { + DNSServersIPs []string `json:"dnsServersIPs,omitempty"` +} + func init() { SchemeBuilder.Register(&Subnet{}, &SubnetList{}) } diff --git a/pkg/apis/v1alpha1/subnetset_types.go b/pkg/apis/v1alpha1/subnetset_types.go index 7a4912d88..0be31c259 100644 --- a/pkg/apis/v1alpha1/subnetset_types.go +++ b/pkg/apis/v1alpha1/subnetset_types.go @@ -12,12 +12,18 @@ type SubnetSetSpec struct { // Size of Subnet based upon estimated workload count. // Defaults to 64. // +kubebuilder:default:=64 + // +kubebuilder:validation:Maximum:=65536 + // +kubebuilder:validation:Minimum:=16 IPv4SubnetSize int `json:"ipv4SubnetSize,omitempty"` // Access mode of Subnet, accessible only from within VPC or from outside VPC. // Defaults to private. // +kubebuilder:default:=private // +kubebuilder:validation:Enum=private;public AccessMode AccessMode `json:"accessMode,omitempty"` + // Subnet advanced configuration. + AdvancedConfig AdvancedConfig `json:"advancedConfig,omitempty"` + // DHCPConfig DHCP configuration. + DHCPConfig DHCPConfig `json:"DHCPConfig,omitempty"` } // SubnetInfo defines the observed state of a single Subnet of a SubnetSet. diff --git a/pkg/apis/v1alpha1/vpcnetworkconfiguration_types.go b/pkg/apis/v1alpha1/vpcnetworkconfiguration_types.go index 27a3cac94..c29011d54 100644 --- a/pkg/apis/v1alpha1/vpcnetworkconfiguration_types.go +++ b/pkg/apis/v1alpha1/vpcnetworkconfiguration_types.go @@ -19,9 +19,6 @@ const ( // in a Namespace's VPCNetworkConfiguration, the Namespace will use the value // in the default VPCNetworkConfiguration. type VPCNetworkConfigurationSpec struct { - // Namespaces to be applied. - // The default VPCNetworkConfiguration will not have AppliedToNamespaces set. - AppliedToNamespaces []string `json:"appliedToNamespaces,omitempty"` // PolicyPath of Tier0 or Tier0 VRF gateway. DefaultGatewayPath string `json:"defaultGatewayPath,omitempty"` // Edge cluster path on which the networking elements will be created. diff --git a/pkg/apis/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/v1alpha1/zz_generated.deepcopy.go index e59ef0d09..27a023250 100644 --- a/pkg/apis/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/v1alpha1/zz_generated.deepcopy.go @@ -13,6 +13,22 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AdvancedConfig) DeepCopyInto(out *AdvancedConfig) { + *out = *in + out.StaticIPAllocation = in.StaticIPAllocation +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdvancedConfig. +func (in *AdvancedConfig) DeepCopy() *AdvancedConfig { + if in == nil { + return nil + } + out := new(AdvancedConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CIDRsUsageInfo) DeepCopyInto(out *CIDRsUsageInfo) { *out = *in @@ -44,6 +60,42 @@ func (in *Condition) DeepCopy() *Condition { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DHCPConfig) DeepCopyInto(out *DHCPConfig) { + *out = *in + in.DNSClientConfig.DeepCopyInto(&out.DNSClientConfig) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DHCPConfig. +func (in *DHCPConfig) DeepCopy() *DHCPConfig { + if in == nil { + return nil + } + out := new(DHCPConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DNSClientConfig) DeepCopyInto(out *DNSClientConfig) { + *out = *in + if in.DNSServersIPs != nil { + in, out := &in.DNSServersIPs, &out.DNSServersIPs + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSClientConfig. +func (in *DNSClientConfig) DeepCopy() *DNSClientConfig { + if in == nil { + return nil + } + out := new(DNSClientConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IPBlock) DeepCopyInto(out *IPBlock) { *out = *in @@ -59,6 +111,176 @@ func (in *IPBlock) DeepCopy() *IPBlock { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NSXProxyEndpoint) DeepCopyInto(out *NSXProxyEndpoint) { + *out = *in + if in.Addresses != nil { + in, out := &in.Addresses, &out.Addresses + *out = make([]NSXProxyEndpointAddress, len(*in)) + copy(*out, *in) + } + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]NSXProxyEndpointPort, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NSXProxyEndpoint. +func (in *NSXProxyEndpoint) DeepCopy() *NSXProxyEndpoint { + if in == nil { + return nil + } + out := new(NSXProxyEndpoint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NSXProxyEndpointAddress) DeepCopyInto(out *NSXProxyEndpointAddress) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NSXProxyEndpointAddress. +func (in *NSXProxyEndpointAddress) DeepCopy() *NSXProxyEndpointAddress { + if in == nil { + return nil + } + out := new(NSXProxyEndpointAddress) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NSXProxyEndpointPort) DeepCopyInto(out *NSXProxyEndpointPort) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NSXProxyEndpointPort. +func (in *NSXProxyEndpointPort) DeepCopy() *NSXProxyEndpointPort { + if in == nil { + return nil + } + out := new(NSXProxyEndpointPort) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NSXSecret) DeepCopyInto(out *NSXSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NSXSecret. +func (in *NSXSecret) DeepCopy() *NSXSecret { + if in == nil { + return nil + } + out := new(NSXSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NSXServiceAccount) DeepCopyInto(out *NSXServiceAccount) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NSXServiceAccount. +func (in *NSXServiceAccount) DeepCopy() *NSXServiceAccount { + if in == nil { + return nil + } + out := new(NSXServiceAccount) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NSXServiceAccount) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NSXServiceAccountList) DeepCopyInto(out *NSXServiceAccountList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NSXServiceAccount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NSXServiceAccountList. +func (in *NSXServiceAccountList) DeepCopy() *NSXServiceAccountList { + if in == nil { + return nil + } + out := new(NSXServiceAccountList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NSXServiceAccountList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NSXServiceAccountSpec) DeepCopyInto(out *NSXServiceAccountSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NSXServiceAccountSpec. +func (in *NSXServiceAccountSpec) DeepCopy() *NSXServiceAccountSpec { + if in == nil { + return nil + } + out := new(NSXServiceAccountSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NSXServiceAccountStatus) DeepCopyInto(out *NSXServiceAccountStatus) { + *out = *in + if in.NSXManagers != nil { + in, out := &in.NSXManagers, &out.NSXManagers + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.ProxyEndpoints.DeepCopyInto(&out.ProxyEndpoints) + if in.Secrets != nil { + in, out := &in.Secrets, &out.Secrets + *out = make([]NSXSecret, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NSXServiceAccountStatus. +func (in *NSXServiceAccountStatus) DeepCopy() *NSXServiceAccountStatus { + if in == nil { + return nil + } + out := new(NSXServiceAccountStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NextHop) DeepCopyInto(out *NextHop) { *out = *in @@ -311,6 +533,21 @@ func (in *SecurityPolicyTarget) DeepCopy() *SecurityPolicyTarget { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StaticIPAllocation) DeepCopyInto(out *StaticIPAllocation) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StaticIPAllocation. +func (in *StaticIPAllocation) DeepCopy() *StaticIPAllocation { + if in == nil { + return nil + } + out := new(StaticIPAllocation) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StaticRoute) DeepCopyInto(out *StaticRoute) { *out = *in @@ -629,7 +866,7 @@ func (in *SubnetSet) DeepCopyInto(out *SubnetSet) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) in.Status.DeepCopyInto(&out.Status) } @@ -686,6 +923,8 @@ func (in *SubnetSetList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SubnetSetSpec) DeepCopyInto(out *SubnetSetSpec) { *out = *in + out.AdvancedConfig = in.AdvancedConfig + in.DHCPConfig.DeepCopyInto(&out.DHCPConfig) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetSetSpec. @@ -735,6 +974,8 @@ func (in *SubnetSpec) DeepCopyInto(out *SubnetSpec) { *out = make([]string, len(*in)) copy(*out, *in) } + out.AdvancedConfig = in.AdvancedConfig + in.DHCPConfig.DeepCopyInto(&out.DHCPConfig) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubnetSpec. @@ -912,11 +1153,6 @@ func (in *VPCNetworkConfigurationList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VPCNetworkConfigurationSpec) DeepCopyInto(out *VPCNetworkConfigurationSpec) { *out = *in - if in.AppliedToNamespaces != nil { - in, out := &in.AppliedToNamespaces, &out.AppliedToNamespaces - *out = make([]string, len(*in)) - copy(*out, *in) - } if in.PublicIPv4Blocks != nil { in, out := &in.PublicIPv4Blocks, &out.PublicIPv4Blocks *out = make([]string, len(*in)) diff --git a/pkg/nsx/services/common/types.go b/pkg/nsx/services/common/types.go index 908309704..ceca03799 100644 --- a/pkg/nsx/services/common/types.go +++ b/pkg/nsx/services/common/types.go @@ -29,6 +29,8 @@ const ( TagScopeNCPVIFProject string = "ncp/vif_project" TagScopeNCPPod string = "ncp/pod" TagScopeNCPVNETInterface string = "ncp/vnet_interface" + TagScopeVPCCRName string = "nsx-op/vpc_cr_name" + TagScopeVPCCRUID string = "nsx-op/vpc_cr_uid" GCInterval = 60 * time.Second FinalizerName = "securitypolicy.nsx.vmware.com/finalizer" @@ -39,6 +41,7 @@ var ( ResourceTypeSecurityPolicy = "SecurityPolicy" ResourceTypeGroup = "Group" ResourceTypeRule = "Rule" + ResourceTypeVPC = "VPC" ) type Service struct { diff --git a/pkg/nsx/services/vpc/store.go b/pkg/nsx/services/vpc/store.go new file mode 100644 index 000000000..b5b96e58c --- /dev/null +++ b/pkg/nsx/services/vpc/store.go @@ -0,0 +1,87 @@ +package vpc + +import ( + "errors" + + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" + + "github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/common" +) + +// keyFunc is used to get the key of a resource, usually, which is the ID of the resource +func keyFunc(obj interface{}) (string, error) { + switch v := obj.(type) { + case model.Vpc: + return *v.Id, nil + default: + return "", errors.New("keyFunc doesn't support unknown type") + } +} + +// indexFunc is used to get index of a resource, usually, which is the UID of the CR controller reconciles, +// index is used to filter out resources which are related to the CR +func indexFunc(obj interface{}) ([]string, error) { + res := make([]string, 0, 5) + switch o := obj.(type) { + case model.Vpc: + return filterTag(o.Tags), nil + default: + return res, errors.New("indexFunc doesn't support unknown type") + } +} + +var filterTag = func(v []model.Tag) []string { + res := make([]string, 0, 5) + for _, tag := range v { + if *tag.Scope == common.TagScopeVPCCRUID { + res = append(res, *tag.Tag) + } + } + return res +} + +// VPCStore is a store for VPCs +type VPCStore struct { + common.ResourceStore +} + +func (vs *VPCStore) Operate(i interface{}) error { + if i == nil { + return nil + } + vpc := i.(*model.Vpc) + if vpc.MarkedForDelete != nil && *vpc.MarkedForDelete { + err := vs.Delete(*vpc) + log.V(1).Info("delete VPC from store", "VPC", vpc) + if err != nil { + return err + } + } else { + err := vs.Add(*vpc) + log.V(1).Info("add VPC to store", "VPC", vpc) + if err != nil { + return err + } + } + return nil +} + +func (vs *VPCStore) GetVPCsByNamespace(ns string) []model.Vpc { + var ret []model.Vpc + vpcs := vs.List() + if len(vpcs) == 0 { + log.V(1).Info("No vpc found in vpc store") + return ret + } + + for _, vpc := range vpcs { + mvpc := vpc.(model.Vpc) + tags := mvpc.Tags + for _, tag := range tags { + if *tag.Scope == common.TagScopeNamespace && *tag.Tag == ns { + ret = append(ret, mvpc) + } + } + } + return ret +} diff --git a/pkg/nsx/services/vpc/store_test.go b/pkg/nsx/services/vpc/store_test.go new file mode 100644 index 000000000..18120c118 --- /dev/null +++ b/pkg/nsx/services/vpc/store_test.go @@ -0,0 +1,252 @@ +package vpc + +import ( + "fmt" + "reflect" + "sync" + "testing" + + "github.com/agiledragon/gomonkey" + "github.com/stretchr/testify/assert" + "github.com/vmware/vsphere-automation-sdk-go/runtime/bindings" + "github.com/vmware/vsphere-automation-sdk-go/runtime/data" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" + "k8s.io/client-go/tools/cache" + + "github.com/vmware-tanzu/nsx-operator/pkg/config" + "github.com/vmware-tanzu/nsx-operator/pkg/nsx" + "github.com/vmware-tanzu/nsx-operator/pkg/nsx/ratelimiter" + "github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/common" +) + +type fakeQueryClient struct { +} + +func (qIface *fakeQueryClient) List(_ string, _ *string, _ *string, _ *int64, _ *bool, _ *string) (model.SearchResponse, error) { + cursor := "2" + resultCount := int64(2) + return model.SearchResponse{ + Results: []*data.StructValue{&data.StructValue{}}, + Cursor: &cursor, ResultCount: &resultCount, + }, nil +} + +func Test_IndexFunc(t *testing.T) { + mId, mTag, mScope := "test_id", "test_tag", "nsx-op/vpc_cr_uid" + v := model.Vpc{ + Id: &mId, + Tags: []model.Tag{{Tag: &mTag, Scope: &mScope}}, + } + t.Run("1", func(t *testing.T) { + got, _ := indexFunc(v) + if !reflect.DeepEqual(got, []string{"test_tag"}) { + t.Errorf("VPCCRUIDScopeIndexFunc() = %v, want %v", got, model.Tag{Tag: &mTag, Scope: &mScope}) + } + }) +} + +func Test_filterTag(t *testing.T) { + mTag, mScope := "test_tag", "nsx-op/vpc_cr_uid" + mTag2, mScope2 := "test_tag", "nsx" + tags := []model.Tag{{Scope: &mScope, Tag: &mTag}} + tags2 := []model.Tag{{Scope: &mScope2, Tag: &mTag2}} + var res []string + var res2 []string + type args struct { + v []model.Tag + res []string + } + tests := []struct { + name string + args args + want []string + }{ + {"1", args{v: tags, res: res}, []string{"test_tag"}}, + {"1", args{v: tags2, res: res2}, []string{}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := filterTag(tt.args.v); !reflect.DeepEqual(got, tt.want) { + t.Errorf("filterTag() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_KeyFunc(t *testing.T) { + Id := "test_id" + v := model.Vpc{Id: &Id} + t.Run("1", func(t *testing.T) { + got, _ := keyFunc(v) + if got != "test_id" { + t.Errorf("keyFunc() = %v, want %v", got, "test_id") + } + }) +} + +func Test_InitializeVPCStore(t *testing.T) { + config2 := nsx.NewConfig("localhost", "1", "1", "", 10, 3, 20, 20, true, true, true, ratelimiter.AIMD, nil, nil, []string{}) + cluster, _ := nsx.NewCluster(config2) + rc, _ := cluster.NewRestConnector() + + service := VPCService{ + Service: common.Service{ + NSXClient: &nsx.Client{ + QueryClient: &fakeQueryClient{}, + RestConnector: rc, + NsxConfig: &config.NSXOperatorConfig{ + CoeConfig: &config.CoeConfig{ + Cluster: "k8scl-one:test", + }, + }, + }, + NSXConfig: &config.NSXOperatorConfig{ + CoeConfig: &config.CoeConfig{ + Cluster: "k8scl-one:test", + }, + }, + }, + } + vpcCacheIndexer := cache.NewIndexer(keyFunc, cache.Indexers{common.TagScopeVPCCRUID: indexFunc}) + vpcStore = &VPCStore{ResourceStore: common.ResourceStore{ + Indexer: vpcCacheIndexer, + BindingType: model.VpcBindingType(), + }} + + wg := sync.WaitGroup{} + fatalErrors := make(chan error) + wg.Add(3) + + var tc *bindings.TypeConverter + patches2 := gomonkey.ApplyMethod(reflect.TypeOf(tc), "ConvertToGolang", + func(_ *bindings.TypeConverter, d data.DataValue, b bindings.BindingType) (interface{}, []error) { + mId, mTag, mScope := "test_id", "test_tag", "test_scope" + m := model.Vpc{ + Id: &mId, + Tags: []model.Tag{{Tag: &mTag, Scope: &mScope}}, + } + var j interface{} = m + return j, nil + }) + defer patches2.Reset() + + service.InitializeResourceStore(&wg, fatalErrors, ResourceTypeVPC, vpcStore) +} + +func TestVPCStore_CRUDResource(t *testing.T) { + vpcCacheIndexer := cache.NewIndexer(keyFunc, cache.Indexers{common.TagScopeVPCCRUID: indexFunc}) + resourceStore := common.ResourceStore{ + Indexer: vpcCacheIndexer, + BindingType: model.VpcBindingType(), + } + vpcStore = &VPCStore{ResourceStore: resourceStore} + type args struct { + i interface{} + } + tests := []struct { + name string + args args + wantErr assert.ErrorAssertionFunc + }{ + {"1", args{i: &model.Vpc{Id: common.String("1")}}, assert.NoError}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.wantErr(t, vpcStore.Operate(tt.args.i), fmt.Sprintf("CRUDResource(%v)", tt.args.i)) + }) + } +} + +func TestVPCStore_CRUDResource_List(t *testing.T) { + vpcCacheIndexer := cache.NewIndexer(keyFunc, cache.Indexers{common.TagScopeVPCCRUID: indexFunc}) + resourceStore := common.ResourceStore{ + Indexer: vpcCacheIndexer, + BindingType: model.VpcBindingType(), + } + vpcStore := &VPCStore{ResourceStore: resourceStore} + type args struct { + i interface{} + j interface{} + } + ns1 := "test-ns-1" + tag1 := []model.Tag{ + { + Scope: &tagScopeCluster, + Tag: &cluster, + }, + { + Scope: &tagScopeNamespace, + Tag: &ns1, + }, + { + Scope: &tagScopeVPCCRName, + Tag: &tagValueVPCCRName, + }, + { + Scope: &tagScopeVPCCRUID, + Tag: &tagValueVPCCRUID, + }, + } + ns2 := "test-ns-2" + tag2 := []model.Tag{ + { + Scope: &tagScopeCluster, + Tag: &cluster, + }, + { + Scope: &tagScopeNamespace, + Tag: &ns2, + }, + { + Scope: &tagScopeVPCCRName, + Tag: &tagValueVPCCRName, + }, + { + Scope: &tagScopeVPCCRUID, + Tag: &tagValueVPCCRUID, + }, + } + vpc1 := model.Vpc{ + + DisplayName: &vpcName1, + Id: &vpcID1, + Tags: tag1, + IpAddressType: &IPv4Type, + PrivateIpv4Blocks: []string{"1.1.1.0/24"}, + PublicIpv4Blocks: []string{"2.2.2.0/24"}, + } + vpc2 := model.Vpc{ + + DisplayName: &vpcName2, + Id: &vpcID2, + Tags: tag2, + IpAddressType: &IPv4Type, + PrivateIpv4Blocks: []string{"3.3.3.0/24"}, + PublicIpv4Blocks: []string{"4.4.4.0/24"}, + } + tests := []struct { + name string + args args + wantErr assert.ErrorAssertionFunc + }{ + {"1", args{i: vpc1, j: vpc2}, assert.NoError}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + vpcStore.Operate(&vpc1) + vpcStore.Operate(&vpc2) + got := vpcStore.List() + if len(got) != 2 { + t.Errorf("size = %v, want %v", len(got), 2) + } + vpc_list_1 := vpcStore.GetVPCsByNamespace("invalid") + if len(vpc_list_1) != 0 { + t.Errorf("size = %v, want %v", len(vpc_list_1), 0) + } + vpc_list_2 := vpcStore.GetVPCsByNamespace(ns2) + if len(vpc_list_2) != 1 && *vpc_list_2[0].DisplayName != vpcName2 { + t.Errorf("size = %v, want %v, display = %s, want %s", len(vpc_list_2), 1, *vpc_list_2[0].DisplayName, vpcName2) + } + }) + } +} diff --git a/pkg/nsx/services/vpc/vpc.go b/pkg/nsx/services/vpc/vpc.go new file mode 100644 index 000000000..2bf75072a --- /dev/null +++ b/pkg/nsx/services/vpc/vpc.go @@ -0,0 +1,61 @@ +package vpc + +import ( + "sync" + + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" + "k8s.io/client-go/tools/cache" + + "github.com/vmware-tanzu/nsx-operator/pkg/logger" + "github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/common" +) + +var ( + log = logger.Log + ResourceTypeVPC = common.ResourceTypeVPC + NewConverter = common.NewConverter + // The following variables are defined as interface, they should be initialized as concrete type + vpcStore common.Store +) + +type VPCService struct { + common.Service + vpcStore *VPCStore +} + +// InitializeVPC sync NSX resources +func InitializeVPC(service common.Service) (*VPCService, error) { + wg := sync.WaitGroup{} + wgDone := make(chan bool) + fatalErrors := make(chan error) + + wg.Add(1) + + VPCService := &VPCService{Service: service} + + VPCService.vpcStore = &VPCStore{ResourceStore: common.ResourceStore{ + Indexer: cache.NewIndexer(keyFunc, cache.Indexers{common.TagScopeVPCCRUID: indexFunc}), + BindingType: model.VpcBindingType(), + }} + + go VPCService.InitializeResourceStore(&wg, fatalErrors, ResourceTypeVPC, vpcStore) + + go func() { + wg.Wait() + close(wgDone) + }() + + select { + case <-wgDone: + break + case err := <-fatalErrors: + close(fatalErrors) + return VPCService, err + } + + return VPCService, nil +} + +func (s *VPCService) GetVPCsByNamespace(namespace string) []model.Vpc { + return s.vpcStore.GetVPCsByNamespace(namespace) +} diff --git a/pkg/nsx/services/vpc/vpc_test.go b/pkg/nsx/services/vpc/vpc_test.go new file mode 100644 index 000000000..459b22574 --- /dev/null +++ b/pkg/nsx/services/vpc/vpc_test.go @@ -0,0 +1,144 @@ +package vpc + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model" + "k8s.io/client-go/tools/cache" + + "github.com/vmware-tanzu/nsx-operator/pkg/nsx/services/common" +) + +var ( + vpcName1 = "ns1-vpc-1" + vpcName2 = "ns1-vpc-2" + vpcID1 = "ns-vpc-uid-1" + vpcID2 = "ns-vpc-uid-2" + IPv4Type = "IPv4" + cluster = "k8scl-one" + tagValueNS = "ns1" + tagScopeVPCCRName = common.TagScopeVPCCRName + tagScopeVPCCRUID = common.TagScopeVPCCRUID + tagValueVPCCRName = "vpcA" + tagValueVPCCRUID = "uidA" + tagScopeCluster = common.TagScopeCluster + tagScopeNamespace = common.TagScopeNamespace + + basicTags = []model.Tag{ + { + Scope: &tagScopeCluster, + Tag: &cluster, + }, + { + Scope: &tagScopeNamespace, + Tag: &tagValueNS, + }, + { + Scope: &tagScopeVPCCRName, + Tag: &tagValueVPCCRName, + }, + { + Scope: &tagScopeVPCCRUID, + Tag: &tagValueVPCCRUID, + }, + } +) + +func TestVPC_GetVPCsByNamespace(t *testing.T) { + vpcCacheIndexer := cache.NewIndexer(keyFunc, cache.Indexers{common.TagScopeVPCCRUID: indexFunc}) + resourceStore := common.ResourceStore{ + Indexer: vpcCacheIndexer, + BindingType: model.VpcBindingType(), + } + vpcStore := &VPCStore{ResourceStore: resourceStore} + service := &VPCService{ + Service: common.Service{NSXClient: nil}, + } + service.vpcStore = vpcStore + type args struct { + i interface{} + j interface{} + } + ns1 := "test-ns-1" + tag1 := []model.Tag{ + { + Scope: &tagScopeCluster, + Tag: &cluster, + }, + { + Scope: &tagScopeNamespace, + Tag: &ns1, + }, + { + Scope: &tagScopeVPCCRName, + Tag: &tagValueVPCCRName, + }, + { + Scope: &tagScopeVPCCRUID, + Tag: &tagValueVPCCRUID, + }, + } + ns2 := "test-ns-2" + tag2 := []model.Tag{ + { + Scope: &tagScopeCluster, + Tag: &cluster, + }, + { + Scope: &tagScopeNamespace, + Tag: &ns2, + }, + { + Scope: &tagScopeVPCCRName, + Tag: &tagValueVPCCRName, + }, + { + Scope: &tagScopeVPCCRUID, + Tag: &tagValueVPCCRUID, + }, + } + vpc1 := model.Vpc{ + + DisplayName: &vpcName1, + Id: &vpcID1, + Tags: tag1, + IpAddressType: &IPv4Type, + PrivateIpv4Blocks: []string{"1.1.1.0/24"}, + PublicIpv4Blocks: []string{"2.2.2.0/24"}, + } + vpc2 := model.Vpc{ + + DisplayName: &vpcName2, + Id: &vpcID2, + Tags: tag2, + IpAddressType: &IPv4Type, + PrivateIpv4Blocks: []string{"3.3.3.0/24"}, + PublicIpv4Blocks: []string{"4.4.4.0/24"}, + } + tests := []struct { + name string + args args + wantErr assert.ErrorAssertionFunc + }{ + {"1", args{i: vpc1, j: vpc2}, assert.NoError}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + vpcStore.Operate(&vpc1) + vpcStore.Operate(&vpc2) + got := vpcStore.List() + if len(got) != 2 { + t.Errorf("size = %v, want %v", len(got), 2) + } + vpc_list_1 := service.GetVPCsByNamespace("invalid") + if len(vpc_list_1) != 0 { + t.Errorf("size = %v, want %v", len(vpc_list_1), 0) + } + vpc_list_2 := service.GetVPCsByNamespace(ns2) + if len(vpc_list_2) != 1 && *vpc_list_2[0].DisplayName != vpcName2 { + t.Errorf("size = %v, want %v, display = %s, want %s", len(vpc_list_2), 1, *vpc_list_2[0].DisplayName, vpcName2) + } + }) + } +} diff --git a/test/e2e/framework.go b/test/e2e/framework.go index 4f7af7ae3..c76599d35 100644 --- a/test/e2e/framework.go +++ b/test/e2e/framework.go @@ -603,6 +603,7 @@ func runCommand(cmd string) (string, error) { command.Stderr = &stderr err := command.Run() if err != nil { + log.Printf("Error when running command %s: %v", cmd, err) return false, nil } outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes()) diff --git a/test/e2e/nsx_security_policy_test.go b/test/e2e/nsx_security_policy_test.go index 655bccc5b..f1a85951e 100644 --- a/test/e2e/nsx_security_policy_test.go +++ b/test/e2e/nsx_security_policy_test.go @@ -414,7 +414,7 @@ func TestSecurityPolicyNamedPort3(t *testing.T) { t.Logf("Pods are %v", ps) assert_nil(t, err, "Error when waiting for IP for Pod ns %s", nsDB) - psb, _, err := testData.deploymentWaitForIPsOrNames(defaultTimeout, nsWeb, labelWeb) + _, psb, err := testData.deploymentWaitForIPsOrNames(defaultTimeout, nsWeb, labelWeb) t.Logf("Pods are %v", psb) assert_nil(t, err, "Error when waiting for IP for Pod ns %s", nsWeb) err = testData.waitForSecurityPolicyReadyOrDeleted(defaultTimeout, nsWeb, securityPolicyName, Ready)