From 0b6d8d6b2fe4f1dc2355305729698e0acbacf776 Mon Sep 17 00:00:00 2001 From: raffaelespazzoli Date: Fri, 23 Jul 2021 10:38:10 +0200 Subject: [PATCH] first draft Signed-off-by: raffaelespazzoli --- Makefile | 2 +- .../v1beta1/networkendpointgroup_types.go | 280 ++++++++++ apis/compute/v1beta1/register.go | 9 + apis/compute/v1beta1/zz_generated.deepcopy.go | 394 ++++++++++++++ apis/compute/v1beta1/zz_generated.managed.go | 56 ++ .../v1beta1/zz_generated.managedlist.go | 9 + go.mod | 1 + go.sum | 4 + ...p.crossplane.io_networkendpointgroups.yaml | 304 +++++++++++ pkg/clients/gcp.go | 9 + .../networkendpointgroup.go | 193 +++++++ .../networkendpointgroup_test.go | 496 ++++++++++++++++++ .../compute/networkedpointgroup_test.go | 168 ++++++ .../compute/networkendpointgroup.go | 298 +++++++++++ pkg/controller/gcp.go | 1 + 15 files changed, 2223 insertions(+), 1 deletion(-) create mode 100644 apis/compute/v1beta1/networkendpointgroup_types.go create mode 100644 package/crds/compute.gcp.crossplane.io_networkendpointgroups.yaml create mode 100644 pkg/clients/networkendpointgroup/networkendpointgroup.go create mode 100644 pkg/clients/networkendpointgroup/networkendpointgroup_test.go create mode 100644 pkg/controller/compute/networkedpointgroup_test.go create mode 100644 pkg/controller/compute/networkendpointgroup.go diff --git a/Makefile b/Makefile index 8bbefd81b..a7f23aac0 100644 --- a/Makefile +++ b/Makefile @@ -106,7 +106,7 @@ submodules: run: go.build @$(INFO) Running Crossplane locally out-of-cluster . . . @# To see other arguments that can be provided, run the command with --help instead - $(GO_OUT_DIR)/$(PROJECT_NAME) --debug + $(GO_OUT_DIR)/provider --debug dev: $(KIND) $(KUBECTL) @$(INFO) Creating kind cluster diff --git a/apis/compute/v1beta1/networkendpointgroup_types.go b/apis/compute/v1beta1/networkendpointgroup_types.go new file mode 100644 index 000000000..607a39c3e --- /dev/null +++ b/apis/compute/v1beta1/networkendpointgroup_types.go @@ -0,0 +1,280 @@ +/* +Copyright 2020 The Crossplane Authors. + +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 + + http://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 v1beta1 + +import ( + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// netowork endpoint types statuses. +const ( + GceVMIPPort = "GCE_VM_IP_PORT" + NonGgpPrivateIPPort = "NON_GCP_PRIVATE_IP_PORT" + InternetFQDNPort = "INTERNET_FQDN_PORT" + InternetIPPort = "INTERNET_IP_PORT" + Serverless = "SERVERLESS" +) + +// NetworkEndpointGroupParameters define the desired state of a Google Compute Engine +// Global Address. Most fields map directly to an Address: +// https://cloud.google.com/compute/docs/reference/rest/v1/networkEndpointGroups +type NetworkEndpointGroupParameters struct { + + // Description: An optional description of this resource. + // +optional + // +immutable + Description *string `json:"description,omitempty"` + + // Region : The URL of the region where the network endpoint group is located. + Region *string `json:"region,omitempty"` + + // Zone : The URL of the zone where the network endpoint group is located. + Zone *string `json:"zone,omitempty"` + + // NetworkEndpointType: Type of network endpoints in this network endpoint group. Can be one of GCE_VM_IP_PORT, NON_GCP_PRIVATE_IP_PORT, INTERNET_FQDN_PORT, INTERNET_IP_PORT, or SERVERLESS. + // + // Possible values: + // "GCE_VM_IP_PORT" + // "NON_GCP_PRIVATE_IP_PORT" + // "INTERNET_FQDN_PORT" + // "INTERNET_IP_PORT" + // "SERVERLESS" + // +immutable + // +kubebuilder:validation:Enum=GCE_VM_IP_PORT;NON_GCP_PRIVATE_IP_PORT;INTERNET_FQDN_PORT;INTERNET_IP_PORT;SERVERLESS + NetworkEndpointType string `json:"networkEndpointType"` + + // Network: The URL of the network to which all network endpoints in the NEG belong. Uses "default" project network if unspecified. + // +optional + // +immutable + Network *string `json:"network,omitempty"` + + // Subnetwork: Optional URL of the subnetwork to which all network endpoints in the NEG belong.. + // +optional + // +immutable + Subnetwork *string `json:"subnetwork,omitempty"` + + // DefaultPort: The default port used if the port number is not specified in the network endpoint. + // +immutable + DefaultPort int64 `json:"defaultPort"` + + // CloudRun : Only valid when networkEndpointType is "SERVERLESS". Only one of cloudRun, appEngine or cloudFunction may be set. + // +optional + // +immutable + CloudRun *CloudRunParameters `json:"cloudRun,omitempty"` + + // AppEngine : Only valid when networkEndpointType is "SERVERLESS". Only one of cloudRun, appEngine or cloudFunction may be set. + // +optional + // +immutable + AppEngine *AppEngineParameters `json:"appEngine,omitempty"` + + // CloudFunction : Only valid when networkEndpointType is "SERVERLESS". Only one of cloudRun, appEngine or cloudFunction may be set. + // +optional + // +immutable + CloudFunction *CloudFunctionParameters `json:"cloudFunction,omitempty"` + + // NetworkEndpoints : list of network endpoints for this network endpoint group. List elements are immutable, but they can be added/removed. + // +optional + NetworkEndpoints []NetworkEndpoint `json:"networkEndpoints,omitempty"` +} + +// NetworkEndpoint : represents a single network endpoint +type NetworkEndpoint struct { + // IPAddress : Optional IPv4 address of network endpoint. The IP address must belong to a VM in Compute Engine (either the primary IP or as part of an aliased IP range). If the IP address is not specified, then the primary IP address for the VM instance in the network that the network endpoint group belongs to will be used. + // +optional + // +immutable + IPAddress *string `json:"ipAddress,omitempty"` + + // FQDN : Optional fully qualified domain name of network endpoint. This can only be specified when NetworkEndpointGroup.network_endpoint_type is NON_GCP_FQDN_PORT. + // +optional + // +immutable + FQDN *string `json:"fqdn,omitempty"` + + // Port : Optional port number of network endpoint. If not specified, the defaultPort for the network endpoint group will be used. + // +optional + // +immutable + Port *int64 `json:"port,omitempty"` + + // Instance : The name for a specific VM instance that the IP address belongs to. This is required for network endpoints of type GCE_VM_IP_PORT. The instance must be in the same zone of network endpoint group. + // The name must be 1-63 characters long, and comply with RFC1035. + // Authorization requires the following IAM permission on the specified resource instance: + // compute.instances.use + // +optional + // +immutable + Instance *string `json:"instance,omitempty"` +} + +// NetworkEndpointHealth : health status of a network endpoint +type NetworkEndpointHealth struct { + // ForwardingRule : URL of the forwarding rule associated with the health state of the network endpoint. + ForwardingRule NetworkEndpointForwardingRule `json:"forwardingRule,omitempty"` + + // BackendService : URL of the backend service associated with the health state of the network endpoint. + BackendService NetworkEndpointBackendService `json:"backendService,omitempty"` + + // HealthCheck : URL of the health check associated with the health state of the network endpoint. + HealthCheck NetworkEndpointHealthCheck `json:"healthCheck,omitempty"` + + // HealthCheckService : URL of the health check service associated with the health state of the network endpoint. + HealthCheckService NetworkEndpointHealthCheckService `json:"healthCheckService,omitempty"` + + // HealthState : Health state of the network endpoint determined based on the health checks configured. + HealthState string `json:"healthState,omitempty"` +} + +// NetworkEndpointForwardingRule : forwarding rule that indirectly references this endpoint +type NetworkEndpointForwardingRule struct { + // ForwardingRule : URL of the forwarding rule associated with the health state of the network endpoint. + ForwardingRule string `json:"forwardingRule,omitempty"` +} + +// NetworkEndpointBackendService : forwarding rule that indirectly references this endpoint +type NetworkEndpointBackendService struct { + // BackendService : URL of the backend service associated with the health state of the network endpoint. + BackendService string `json:"backendService,omitempty"` +} + +// NetworkEndpointHealthCheck : forwarding rule that references this endpoint +type NetworkEndpointHealthCheck struct { + // HealthCheck : URL of the health check associated with the health state of the network endpoint. + HealthCheck string `json:"healthCheck,omitempty"` +} + +// NetworkEndpointHealthCheckService : forwarding rule that indirectly references this endpoint +type NetworkEndpointHealthCheckService struct { + // HealthCheckService : URL of the health check service associated with the health state of the network endpoint. + HealthCheckService string `json:"healthCheckService,omitempty"` +} + +// NetworkEndpointObservation : an observation of a network endpoint +type NetworkEndpointObservation struct { + NetworkEndpoint `json:",inline"` + Annotations map[string]string `json:"annotations,omitempty"` + NetworkEndpointHealths []NetworkEndpointHealth `json:"networkEndpointHealths,omitempty"` +} + +// CloudRunParameters : fill these if you load balance to a cloud run function +type CloudRunParameters struct { + // Service : Cloud Run service is the main resource of Cloud Run. + // The service must be 1-63 characters long, and comply with RFC1035. + // Example value: "run-service". + Service string `json:"service"` + + // Tag : Optional Cloud Run tag represents the "named-revision" to provide additional fine-grained traffic routing information. + // The tag must be 1-63 characters long, and comply with RFC1035. + // Example value: "revision-0010". + // +optional + // +immutable + Tag *string `json:"tag,omitempty"` + + // URLMask : A template to parse service and tag fields from a request URL. URL mask allows for routing to multiple Run services without having to create multiple network endpoint groups and backend services. + // For example, request URLs "foo1.domain.com/bar1" and "foo1.domain.com/bar2" can be backed by the same Serverless Network Endpoint Group (NEG) with URL mask ".domain.com/". The URL mask will parse them to { service="bar1", tag="foo1" } and { service="bar2", tag="foo2" } respectively. + // +optional + // +immutable + URLMask *string `json:"urlMask,omitempty"` +} + +// AppEngineParameters : fill these if you load balance to a app engine function +type AppEngineParameters struct { + // Service : Optional serving service. + // The service name is case-sensitive and must be 1-63 characters long. + // Example value: "default", "my-service". + // +optional + // +immutable + Service *string `json:"service,omitempty"` + + // Version : Optional serving version. + // The version name is case-sensitive and must be 1-100 characters long. + // Example value: "v1", "v2". + // +optional + // +immutable + Version *string `json:"version,omitempty"` + + // URLMask : A template to parse service and version fields from a request URL. URL mask allows for routing to multiple App Engine services without having to create multiple Network Endpoint Groups and backend services. + // For example, the request URLs "foo1-dot-appname.appspot.com/v1" and "foo1-dot-appname.appspot.com/v2" can be backed by the same Serverless NEG with URL mask "-dot-appname.appspot.com/". The URL mask will parse them to { service = "foo1", version = "v1" } and { service = "foo1", version = "v2" } respectively. + // +optional + // +immutable + URLMask *string `json:"urlMask,omitempty"` +} + +// CloudFunctionParameters : fill these if you load balance to a cloud function +type CloudFunctionParameters struct { + // Function : A user-defined name of the Cloud Function. + // The function name is case-sensitive and must be 1-63 characters long. + // Example value: "func1". + // +immutable + Function string `json:"function"` + + // URLMask : A template to parse function field from a request URL. URL mask allows for routing to multiple Cloud Functions without having to create multiple Network Endpoint Groups and backend services. + // For example, request URLs "mydomain.com/function1" and "mydomain.com/function2" can be backed by the same Serverless NEG with URL mask "/". The URL mask will parse them to { function = "function1" } and { function = "function2" } respectively. + // +optional + // +immutable + URLMask *string `json:"urlMask,omitempty"` +} + +// A NetworkEndpointGroupObservation reflects the observed state of a GlobalAddress on GCP. +type NetworkEndpointGroupObservation struct { + // CreationTimestamp in RFC3339 text format. + CreationTimestamp string `json:"creationTimestamp,omitempty"` + + // ID for the resource. This identifier is defined by the server. + ID uint64 `json:"id,omitempty"` + + // SelfLink: Server-defined URL for the resource. + SelfLink string `json:"selfLink,omitempty"` + + // Size: Number of network endpoints in the network endpoint group. + Size int64 `json:"size,omitempty"` + + NetworkEndpointObservations []NetworkEndpointObservation `json:"networkEndpointObservations,omitempty"` +} + +// A NetworkEndpointGroupSpec defines the desired state of a NetworkEndpointGroup. +type NetworkEndpointGroupSpec struct { + xpv1.ResourceSpec `json:",inline"` + ForProvider NetworkEndpointGroupParameters `json:"forProvider"` +} + +// A NetworkEndpointGroupStatus represents the observed state of a NetworkEndpointGroup. +type NetworkEndpointGroupStatus struct { + xpv1.ResourceStatus `json:",inline"` + AtProvider NetworkEndpointGroupObservation `json:"atProvider,omitempty"` +} + +// A NetworkEndpointGroup is a managed resource that represents a Google Compute Engine +// Network Endpoint Group (https://cloud.google.com/compute/docs/reference/rest/v1/networkEndpointGroups). +// +kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status" +// +kubebuilder:printcolumn:name="SYNCED",type="string",JSONPath=".status.conditions[?(@.type=='Synced')].status" +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster,categories={crossplane,managed,gcp} +type NetworkEndpointGroup struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec NetworkEndpointGroupSpec `json:"spec"` + Status NetworkEndpointGroupStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// NetworkEndpointGroupList contains a list of NetworkEndpointGroups. +type NetworkEndpointGroupList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []NetworkEndpointGroup `json:"items"` +} diff --git a/apis/compute/v1beta1/register.go b/apis/compute/v1beta1/register.go index 9b244756d..574a14936 100644 --- a/apis/compute/v1beta1/register.go +++ b/apis/compute/v1beta1/register.go @@ -61,8 +61,17 @@ var ( GlobalAddressGroupVersionKind = SchemeGroupVersion.WithKind(GlobalAddressKind) ) +// GlobalAddress type metadata. +var ( + NetworkEndpointGroupKind = reflect.TypeOf(NetworkEndpointGroup{}).Name() + NetworkEndpointGroupGroupKind = schema.GroupKind{Group: Group, Kind: NetworkEndpointGroupKind}.String() + NetworkEndpointGroupKindAPIVersion = NetworkEndpointGroupKind + "." + SchemeGroupVersion.String() + NetworkEndpointGroupGroupVersionKind = SchemeGroupVersion.WithKind(NetworkEndpointGroupKind) +) + func init() { SchemeBuilder.Register(&Network{}, &NetworkList{}) SchemeBuilder.Register(&Subnetwork{}, &SubnetworkList{}) SchemeBuilder.Register(&GlobalAddress{}, &GlobalAddressList{}) + SchemeBuilder.Register(&NetworkEndpointGroup{}, &NetworkEndpointGroupList{}) } diff --git a/apis/compute/v1beta1/zz_generated.deepcopy.go b/apis/compute/v1beta1/zz_generated.deepcopy.go index c2f7b61b0..149bbe7d5 100644 --- a/apis/compute/v1beta1/zz_generated.deepcopy.go +++ b/apis/compute/v1beta1/zz_generated.deepcopy.go @@ -25,6 +25,81 @@ 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 *AppEngineParameters) DeepCopyInto(out *AppEngineParameters) { + *out = *in + if in.Service != nil { + in, out := &in.Service, &out.Service + *out = new(string) + **out = **in + } + if in.Version != nil { + in, out := &in.Version, &out.Version + *out = new(string) + **out = **in + } + if in.URLMask != nil { + in, out := &in.URLMask, &out.URLMask + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AppEngineParameters. +func (in *AppEngineParameters) DeepCopy() *AppEngineParameters { + if in == nil { + return nil + } + out := new(AppEngineParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudFunctionParameters) DeepCopyInto(out *CloudFunctionParameters) { + *out = *in + if in.URLMask != nil { + in, out := &in.URLMask, &out.URLMask + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudFunctionParameters. +func (in *CloudFunctionParameters) DeepCopy() *CloudFunctionParameters { + if in == nil { + return nil + } + out := new(CloudFunctionParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CloudRunParameters) DeepCopyInto(out *CloudRunParameters) { + *out = *in + if in.Tag != nil { + in, out := &in.Tag, &out.Tag + *out = new(string) + **out = **in + } + if in.URLMask != nil { + in, out := &in.URLMask, &out.URLMask + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CloudRunParameters. +func (in *CloudRunParameters) DeepCopy() *CloudRunParameters { + if in == nil { + return nil + } + out := new(CloudRunParameters) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *GlobalAddress) DeepCopyInto(out *GlobalAddress) { *out = *in @@ -240,6 +315,325 @@ func (in *Network) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkEndpoint) DeepCopyInto(out *NetworkEndpoint) { + *out = *in + if in.IPAddress != nil { + in, out := &in.IPAddress, &out.IPAddress + *out = new(string) + **out = **in + } + if in.FQDN != nil { + in, out := &in.FQDN, &out.FQDN + *out = new(string) + **out = **in + } + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(int64) + **out = **in + } + if in.Instance != nil { + in, out := &in.Instance, &out.Instance + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpoint. +func (in *NetworkEndpoint) DeepCopy() *NetworkEndpoint { + if in == nil { + return nil + } + out := new(NetworkEndpoint) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkEndpointBackendService) DeepCopyInto(out *NetworkEndpointBackendService) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpointBackendService. +func (in *NetworkEndpointBackendService) DeepCopy() *NetworkEndpointBackendService { + if in == nil { + return nil + } + out := new(NetworkEndpointBackendService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkEndpointForwardingRule) DeepCopyInto(out *NetworkEndpointForwardingRule) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpointForwardingRule. +func (in *NetworkEndpointForwardingRule) DeepCopy() *NetworkEndpointForwardingRule { + if in == nil { + return nil + } + out := new(NetworkEndpointForwardingRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkEndpointGroup) DeepCopyInto(out *NetworkEndpointGroup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpointGroup. +func (in *NetworkEndpointGroup) DeepCopy() *NetworkEndpointGroup { + if in == nil { + return nil + } + out := new(NetworkEndpointGroup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkEndpointGroup) 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 *NetworkEndpointGroupList) DeepCopyInto(out *NetworkEndpointGroupList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NetworkEndpointGroup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpointGroupList. +func (in *NetworkEndpointGroupList) DeepCopy() *NetworkEndpointGroupList { + if in == nil { + return nil + } + out := new(NetworkEndpointGroupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkEndpointGroupList) 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 *NetworkEndpointGroupObservation) DeepCopyInto(out *NetworkEndpointGroupObservation) { + *out = *in + if in.NetworkEndpointObservations != nil { + in, out := &in.NetworkEndpointObservations, &out.NetworkEndpointObservations + *out = make([]NetworkEndpointObservation, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpointGroupObservation. +func (in *NetworkEndpointGroupObservation) DeepCopy() *NetworkEndpointGroupObservation { + if in == nil { + return nil + } + out := new(NetworkEndpointGroupObservation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkEndpointGroupParameters) DeepCopyInto(out *NetworkEndpointGroupParameters) { + *out = *in + if in.Description != nil { + in, out := &in.Description, &out.Description + *out = new(string) + **out = **in + } + if in.Region != nil { + in, out := &in.Region, &out.Region + *out = new(string) + **out = **in + } + if in.Zone != nil { + in, out := &in.Zone, &out.Zone + *out = new(string) + **out = **in + } + if in.Network != nil { + in, out := &in.Network, &out.Network + *out = new(string) + **out = **in + } + if in.Subnetwork != nil { + in, out := &in.Subnetwork, &out.Subnetwork + *out = new(string) + **out = **in + } + if in.CloudRun != nil { + in, out := &in.CloudRun, &out.CloudRun + *out = new(CloudRunParameters) + (*in).DeepCopyInto(*out) + } + if in.AppEngine != nil { + in, out := &in.AppEngine, &out.AppEngine + *out = new(AppEngineParameters) + (*in).DeepCopyInto(*out) + } + if in.CloudFunction != nil { + in, out := &in.CloudFunction, &out.CloudFunction + *out = new(CloudFunctionParameters) + (*in).DeepCopyInto(*out) + } + if in.NetworkEndpoints != nil { + in, out := &in.NetworkEndpoints, &out.NetworkEndpoints + *out = make([]NetworkEndpoint, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpointGroupParameters. +func (in *NetworkEndpointGroupParameters) DeepCopy() *NetworkEndpointGroupParameters { + if in == nil { + return nil + } + out := new(NetworkEndpointGroupParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkEndpointGroupSpec) DeepCopyInto(out *NetworkEndpointGroupSpec) { + *out = *in + in.ResourceSpec.DeepCopyInto(&out.ResourceSpec) + in.ForProvider.DeepCopyInto(&out.ForProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpointGroupSpec. +func (in *NetworkEndpointGroupSpec) DeepCopy() *NetworkEndpointGroupSpec { + if in == nil { + return nil + } + out := new(NetworkEndpointGroupSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkEndpointGroupStatus) DeepCopyInto(out *NetworkEndpointGroupStatus) { + *out = *in + in.ResourceStatus.DeepCopyInto(&out.ResourceStatus) + in.AtProvider.DeepCopyInto(&out.AtProvider) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpointGroupStatus. +func (in *NetworkEndpointGroupStatus) DeepCopy() *NetworkEndpointGroupStatus { + if in == nil { + return nil + } + out := new(NetworkEndpointGroupStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkEndpointHealth) DeepCopyInto(out *NetworkEndpointHealth) { + *out = *in + out.ForwardingRule = in.ForwardingRule + out.BackendService = in.BackendService + out.HealthCheck = in.HealthCheck + out.HealthCheckService = in.HealthCheckService +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpointHealth. +func (in *NetworkEndpointHealth) DeepCopy() *NetworkEndpointHealth { + if in == nil { + return nil + } + out := new(NetworkEndpointHealth) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkEndpointHealthCheck) DeepCopyInto(out *NetworkEndpointHealthCheck) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpointHealthCheck. +func (in *NetworkEndpointHealthCheck) DeepCopy() *NetworkEndpointHealthCheck { + if in == nil { + return nil + } + out := new(NetworkEndpointHealthCheck) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkEndpointHealthCheckService) DeepCopyInto(out *NetworkEndpointHealthCheckService) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpointHealthCheckService. +func (in *NetworkEndpointHealthCheckService) DeepCopy() *NetworkEndpointHealthCheckService { + if in == nil { + return nil + } + out := new(NetworkEndpointHealthCheckService) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkEndpointObservation) DeepCopyInto(out *NetworkEndpointObservation) { + *out = *in + in.NetworkEndpoint.DeepCopyInto(&out.NetworkEndpoint) + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.NetworkEndpointHealths != nil { + in, out := &in.NetworkEndpointHealths, &out.NetworkEndpointHealths + *out = make([]NetworkEndpointHealth, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEndpointObservation. +func (in *NetworkEndpointObservation) DeepCopy() *NetworkEndpointObservation { + if in == nil { + return nil + } + out := new(NetworkEndpointObservation) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *NetworkList) DeepCopyInto(out *NetworkList) { *out = *in diff --git a/apis/compute/v1beta1/zz_generated.managed.go b/apis/compute/v1beta1/zz_generated.managed.go index 7de9a425f..19b9d50dc 100644 --- a/apis/compute/v1beta1/zz_generated.managed.go +++ b/apis/compute/v1beta1/zz_generated.managed.go @@ -132,6 +132,62 @@ func (mg *Network) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) mg.Spec.WriteConnectionSecretToReference = r } +// GetCondition of this NetworkEndpointGroup. +func (mg *NetworkEndpointGroup) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return mg.Status.GetCondition(ct) +} + +// GetDeletionPolicy of this NetworkEndpointGroup. +func (mg *NetworkEndpointGroup) GetDeletionPolicy() xpv1.DeletionPolicy { + return mg.Spec.DeletionPolicy +} + +// GetProviderConfigReference of this NetworkEndpointGroup. +func (mg *NetworkEndpointGroup) GetProviderConfigReference() *xpv1.Reference { + return mg.Spec.ProviderConfigReference +} + +/* +GetProviderReference of this NetworkEndpointGroup. +Deprecated: Use GetProviderConfigReference. +*/ +func (mg *NetworkEndpointGroup) GetProviderReference() *xpv1.Reference { + return mg.Spec.ProviderReference +} + +// GetWriteConnectionSecretToReference of this NetworkEndpointGroup. +func (mg *NetworkEndpointGroup) GetWriteConnectionSecretToReference() *xpv1.SecretReference { + return mg.Spec.WriteConnectionSecretToReference +} + +// SetConditions of this NetworkEndpointGroup. +func (mg *NetworkEndpointGroup) SetConditions(c ...xpv1.Condition) { + mg.Status.SetConditions(c...) +} + +// SetDeletionPolicy of this NetworkEndpointGroup. +func (mg *NetworkEndpointGroup) SetDeletionPolicy(r xpv1.DeletionPolicy) { + mg.Spec.DeletionPolicy = r +} + +// SetProviderConfigReference of this NetworkEndpointGroup. +func (mg *NetworkEndpointGroup) SetProviderConfigReference(r *xpv1.Reference) { + mg.Spec.ProviderConfigReference = r +} + +/* +SetProviderReference of this NetworkEndpointGroup. +Deprecated: Use SetProviderConfigReference. +*/ +func (mg *NetworkEndpointGroup) SetProviderReference(r *xpv1.Reference) { + mg.Spec.ProviderReference = r +} + +// SetWriteConnectionSecretToReference of this NetworkEndpointGroup. +func (mg *NetworkEndpointGroup) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { + mg.Spec.WriteConnectionSecretToReference = r +} + // GetCondition of this Subnetwork. func (mg *Subnetwork) GetCondition(ct xpv1.ConditionType) xpv1.Condition { return mg.Status.GetCondition(ct) diff --git a/apis/compute/v1beta1/zz_generated.managedlist.go b/apis/compute/v1beta1/zz_generated.managedlist.go index 5c4364329..82137f0d7 100644 --- a/apis/compute/v1beta1/zz_generated.managedlist.go +++ b/apis/compute/v1beta1/zz_generated.managedlist.go @@ -29,6 +29,15 @@ func (l *GlobalAddressList) GetItems() []resource.Managed { return items } +// GetItems of this NetworkEndpointGroupList. +func (l *NetworkEndpointGroupList) GetItems() []resource.Managed { + items := make([]resource.Managed, len(l.Items)) + for i := range l.Items { + items[i] = &l.Items[i] + } + return items +} + // GetItems of this NetworkList. func (l *NetworkList) GetItems() []resource.Managed { items := make([]resource.Managed, len(l.Items)) diff --git a/go.mod b/go.mod index 96d97cf72..218b37842 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/mitchellh/copystructure v1.0.0 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pkg/errors v0.9.1 + github.com/scylladb/go-set v1.0.2 golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect google.golang.org/api v0.47.0 google.golang.org/genproto v0.0.0-20210524142926-3e3a6030be83 // indirect diff --git a/go.sum b/go.sum index cc491dae2..7909a90c0 100644 --- a/go.sum +++ b/go.sum @@ -161,6 +161,8 @@ github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQo github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA= +github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= @@ -500,6 +502,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/scylladb/go-set v1.0.2 h1:SkvlMCKhP0wyyct6j+0IHJkBkSZL+TDzZ4E7f7BCcRE= +github.com/scylladb/go-set v1.0.2/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= diff --git a/package/crds/compute.gcp.crossplane.io_networkendpointgroups.yaml b/package/crds/compute.gcp.crossplane.io_networkendpointgroups.yaml new file mode 100644 index 000000000..cf13bec85 --- /dev/null +++ b/package/crds/compute.gcp.crossplane.io_networkendpointgroups.yaml @@ -0,0 +1,304 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.3.0 + creationTimestamp: null + name: networkendpointgroups.compute.gcp.crossplane.io +spec: + group: compute.gcp.crossplane.io + names: + categories: + - crossplane + - managed + - gcp + kind: NetworkEndpointGroup + listKind: NetworkEndpointGroupList + plural: networkendpointgroups + singular: networkendpointgroup + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Ready')].status + name: READY + type: string + - jsonPath: .status.conditions[?(@.type=='Synced')].status + name: SYNCED + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: A NetworkEndpointGroup is a managed resource that represents a Google Compute Engine Network Endpoint Group (https://cloud.google.com/compute/docs/reference/rest/v1/networkEndpointGroups). + 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: A NetworkEndpointGroupSpec defines the desired state of a NetworkEndpointGroup. + properties: + deletionPolicy: + default: Delete + description: DeletionPolicy specifies what will happen to the underlying external when this managed resource is deleted - either "Delete" or "Orphan" the external resource. + enum: + - Orphan + - Delete + type: string + forProvider: + description: 'NetworkEndpointGroupParameters define the desired state of a Google Compute Engine Global Address. Most fields map directly to an Address: https://cloud.google.com/compute/docs/reference/rest/v1/networkEndpointGroups' + properties: + appEngine: + description: 'AppEngine : Only valid when networkEndpointType is "SERVERLESS". Only one of cloudRun, appEngine or cloudFunction may be set.' + properties: + service: + description: 'Service : Optional serving service. The service name is case-sensitive and must be 1-63 characters long. Example value: "default", "my-service".' + type: string + urlMask: + description: 'URLMask : A template to parse service and version fields from a request URL. URL mask allows for routing to multiple App Engine services without having to create multiple Network Endpoint Groups and backend services. For example, the request URLs "foo1-dot-appname.appspot.com/v1" and "foo1-dot-appname.appspot.com/v2" can be backed by the same Serverless NEG with URL mask "-dot-appname.appspot.com/". The URL mask will parse them to { service = "foo1", version = "v1" } and { service = "foo1", version = "v2" } respectively.' + type: string + version: + description: 'Version : Optional serving version. The version name is case-sensitive and must be 1-100 characters long. Example value: "v1", "v2".' + type: string + type: object + cloudFunction: + description: 'CloudFunction : Only valid when networkEndpointType is "SERVERLESS". Only one of cloudRun, appEngine or cloudFunction may be set.' + properties: + function: + description: 'Function : A user-defined name of the Cloud Function. The function name is case-sensitive and must be 1-63 characters long. Example value: "func1".' + type: string + urlMask: + description: 'URLMask : A template to parse function field from a request URL. URL mask allows for routing to multiple Cloud Functions without having to create multiple Network Endpoint Groups and backend services. For example, request URLs "mydomain.com/function1" and "mydomain.com/function2" can be backed by the same Serverless NEG with URL mask "/". The URL mask will parse them to { function = "function1" } and { function = "function2" } respectively.' + type: string + required: + - function + type: object + cloudRun: + description: 'CloudRun : Only valid when networkEndpointType is "SERVERLESS". Only one of cloudRun, appEngine or cloudFunction may be set.' + properties: + service: + description: 'Service : Cloud Run service is the main resource of Cloud Run. The service must be 1-63 characters long, and comply with RFC1035. Example value: "run-service".' + type: string + tag: + description: 'Tag : Optional Cloud Run tag represents the "named-revision" to provide additional fine-grained traffic routing information. The tag must be 1-63 characters long, and comply with RFC1035. Example value: "revision-0010".' + type: string + urlMask: + description: 'URLMask : A template to parse service and tag fields from a request URL. URL mask allows for routing to multiple Run services without having to create multiple network endpoint groups and backend services. For example, request URLs "foo1.domain.com/bar1" and "foo1.domain.com/bar2" can be backed by the same Serverless Network Endpoint Group (NEG) with URL mask ".domain.com/". The URL mask will parse them to { service="bar1", tag="foo1" } and { service="bar2", tag="foo2" } respectively.' + type: string + required: + - service + type: object + defaultPort: + description: 'DefaultPort: The default port used if the port number is not specified in the network endpoint.' + format: int64 + type: integer + description: + description: 'Description: An optional description of this resource.' + type: string + network: + description: 'Network: The URL of the network to which all network endpoints in the NEG belong. Uses "default" project network if unspecified.' + type: string + networkEndpointType: + description: "NetworkEndpointType: Type of network endpoints in this network endpoint group. Can be one of GCE_VM_IP_PORT, NON_GCP_PRIVATE_IP_PORT, INTERNET_FQDN_PORT, INTERNET_IP_PORT, or SERVERLESS. \n Possible values: \"GCE_VM_IP_PORT\" \"NON_GCP_PRIVATE_IP_PORT\" \"INTERNET_FQDN_PORT\" \"INTERNET_IP_PORT\" \"SERVERLESS\"" + enum: + - GCE_VM_IP_PORT + - NON_GCP_PRIVATE_IP_PORT + - INTERNET_FQDN_PORT + - INTERNET_IP_PORT + - SERVERLESS + type: string + networkEndpoints: + description: 'NetworkEndpoints : list of network endpoints for this network endpoint group. List elements are immutable, but they can be added/removed.' + items: + description: 'NetworkEndpoint : represents a single network endpoint' + properties: + fqdn: + description: 'FQDN : Optional fully qualified domain name of network endpoint. This can only be specified when NetworkEndpointGroup.network_endpoint_type is NON_GCP_FQDN_PORT.' + type: string + instance: + description: 'Instance : The name for a specific VM instance that the IP address belongs to. This is required for network endpoints of type GCE_VM_IP_PORT. The instance must be in the same zone of network endpoint group. The name must be 1-63 characters long, and comply with RFC1035. Authorization requires the following IAM permission on the specified resource instance: compute.instances.use' + type: string + ipAddress: + description: 'IPAddress : Optional IPv4 address of network endpoint. The IP address must belong to a VM in Compute Engine (either the primary IP or as part of an aliased IP range). If the IP address is not specified, then the primary IP address for the VM instance in the network that the network endpoint group belongs to will be used.' + type: string + port: + description: 'Port : Optional port number of network endpoint. If not specified, the defaultPort for the network endpoint group will be used.' + format: int64 + type: integer + type: object + type: array + region: + description: 'Region : The URL of the region where the network endpoint group is located.' + type: string + subnetwork: + description: 'Subnetwork: Optional URL of the subnetwork to which all network endpoints in the NEG belong..' + type: string + zone: + description: 'Zone : The URL of the zone where the network endpoint group is located.' + type: string + required: + - defaultPort + - networkEndpointType + type: object + providerConfigRef: + default: + name: default + description: ProviderConfigReference specifies how the provider that will be used to create, observe, update, and delete this managed resource should be configured. + properties: + name: + description: Name of the referenced object. + type: string + required: + - name + type: object + providerRef: + description: 'ProviderReference specifies the provider that will be used to create, observe, update, and delete this managed resource. Deprecated: Please use ProviderConfigReference, i.e. `providerConfigRef`' + properties: + name: + description: Name of the referenced object. + type: string + required: + - name + type: object + writeConnectionSecretToRef: + description: WriteConnectionSecretToReference specifies the namespace and name of a Secret to which any connection details for this managed resource should be written. Connection details frequently include the endpoint, username, and password required to connect to the managed resource. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + - namespace + type: object + required: + - forProvider + type: object + status: + description: A NetworkEndpointGroupStatus represents the observed state of a NetworkEndpointGroup. + properties: + atProvider: + description: A NetworkEndpointGroupObservation reflects the observed state of a GlobalAddress on GCP. + properties: + creationTimestamp: + description: CreationTimestamp in RFC3339 text format. + type: string + id: + description: ID for the resource. This identifier is defined by the server. + format: int64 + type: integer + networkEndpointObservations: + items: + description: 'NetworkEndpointObservation : an observation of a network endpoint' + properties: + annotations: + additionalProperties: + type: string + type: object + fqdn: + description: 'FQDN : Optional fully qualified domain name of network endpoint. This can only be specified when NetworkEndpointGroup.network_endpoint_type is NON_GCP_FQDN_PORT.' + type: string + instance: + description: 'Instance : The name for a specific VM instance that the IP address belongs to. This is required for network endpoints of type GCE_VM_IP_PORT. The instance must be in the same zone of network endpoint group. The name must be 1-63 characters long, and comply with RFC1035. Authorization requires the following IAM permission on the specified resource instance: compute.instances.use' + type: string + ipAddress: + description: 'IPAddress : Optional IPv4 address of network endpoint. The IP address must belong to a VM in Compute Engine (either the primary IP or as part of an aliased IP range). If the IP address is not specified, then the primary IP address for the VM instance in the network that the network endpoint group belongs to will be used.' + type: string + networkEndpointHealths: + items: + description: 'NetworkEndpointHealth : health status of a network endpoint' + properties: + backendService: + description: 'BackendService : URL of the backend service associated with the health state of the network endpoint.' + properties: + backendService: + description: 'BackendService : URL of the backend service associated with the health state of the network endpoint.' + type: string + type: object + forwardingRule: + description: 'ForwardingRule : URL of the forwarding rule associated with the health state of the network endpoint.' + properties: + forwardingRule: + description: 'ForwardingRule : URL of the forwarding rule associated with the health state of the network endpoint.' + type: string + type: object + healthCheck: + description: 'HealthCheck : URL of the health check associated with the health state of the network endpoint.' + properties: + healthCheck: + description: 'HealthCheck : URL of the health check associated with the health state of the network endpoint.' + type: string + type: object + healthCheckService: + description: 'HealthCheckService : URL of the health check service associated with the health state of the network endpoint.' + properties: + healthCheckService: + description: 'HealthCheckService : URL of the health check service associated with the health state of the network endpoint.' + type: string + type: object + healthState: + description: 'HealthState : Health state of the network endpoint determined based on the health checks configured.' + type: string + type: object + type: array + port: + description: 'Port : Optional port number of network endpoint. If not specified, the defaultPort for the network endpoint group will be used.' + format: int64 + type: integer + type: object + type: array + selfLink: + description: 'SelfLink: Server-defined URL for the resource.' + type: string + size: + description: 'Size: Number of network endpoints in the network endpoint group.' + format: int64 + type: integer + type: object + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time this condition transitioned from one status to another. + format: date-time + type: string + message: + description: A Message containing details about this condition's last transition from one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from one status to another. + type: string + status: + description: Status of this condition; is it currently True, False, or Unknown? + type: string + type: + description: Type of this condition. At most one of each condition type may apply to a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/pkg/clients/gcp.go b/pkg/clients/gcp.go index 426355c9d..603fc78cf 100644 --- a/pkg/clients/gcp.go +++ b/pkg/clients/gcp.go @@ -212,6 +212,15 @@ func LateInitializeStringMap(s map[string]string, from map[string]string) map[st return from } +// LateInitializeStruct implements late initialization for +// string map type. The passed strunct is treated as whole, thre is no recursinve late initialization logic for each field of the struct. +func LateInitializeStruct(s interface{}, from interface{}) interface{} { + if s != nil || from == nil { + return s + } + return from +} + // EquateComputeURLs considers compute APIs to be equal whether they are fully // qualified, partially qualified, or unqualified. The compute API will accept // unqualified or partially qualified URLs for certain fields, but return fully diff --git a/pkg/clients/networkendpointgroup/networkendpointgroup.go b/pkg/clients/networkendpointgroup/networkendpointgroup.go new file mode 100644 index 000000000..3204392af --- /dev/null +++ b/pkg/clients/networkendpointgroup/networkendpointgroup.go @@ -0,0 +1,193 @@ +/* +Copyright 2019 The Crossplane Authors. + +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 + + http://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 networkendpointgroup + +import ( + "fmt" + + "github.com/scylladb/go-set/strset" + + "github.com/crossplane/provider-gcp/apis/compute/v1beta1" + + compute "google.golang.org/api/compute/v1" + + gcp "github.com/crossplane/provider-gcp/pkg/clients" +) + +// LateInitializeSpec updates any unset (i.e. nil) optional fields of the +// supplied NetworkEndpointGroupParameters that are set (i.e. non-zero) on the supplied +// NetworkEndpointGroup. +func LateInitializeSpec(p *v1beta1.NetworkEndpointGroupParameters, observed compute.NetworkEndpointGroup, observedEndpoints []*compute.NetworkEndpointWithHealthStatus) { + p.Network = gcp.LateInitializeString(p.Network, observed.Network) + p.Subnetwork = gcp.LateInitializeString(p.Subnetwork, observed.Subnetwork) + p.Region = gcp.LateInitializeString(p.Region, observed.Region) + p.Zone = gcp.LateInitializeString(p.Zone, observed.Zone) + + p.AppEngine = gcp.LateInitializeStruct(p.AppEngine, observed.AppEngine).(*v1beta1.AppEngineParameters) + p.CloudFunction = gcp.LateInitializeStruct(p.CloudFunction, observed.CloudFunction).(*v1beta1.CloudFunctionParameters) + p.CloudRun = gcp.LateInitializeStruct(p.CloudRun, observed.CloudRun).(*v1beta1.CloudRunParameters) + + networkEndpoints := []v1beta1.NetworkEndpoint{} + for _, observedEndpoint := range observedEndpoints { + networkEndpoints = append(networkEndpoints, v1beta1.NetworkEndpoint{ + IPAddress: &observedEndpoint.NetworkEndpoint.IpAddress, + FQDN: &observedEndpoint.NetworkEndpoint.Fqdn, + Port: &observedEndpoint.NetworkEndpoint.Port, + Instance: &observedEndpoint.NetworkEndpoint.Instance, + }) + } + p.NetworkEndpoints = networkEndpoints +} + +// GenerateNetworkEndpointGroupObservation takes a compute.Address and returns +// *NetworkEndpointGroupObservation. +func GenerateNetworkEndpointGroupObservation(observed compute.NetworkEndpointGroup, observedEndpoints []*compute.NetworkEndpointWithHealthStatus) v1beta1.NetworkEndpointGroupObservation { + networkEndpointObservations := []v1beta1.NetworkEndpointObservation{} + for _, observedEndpoint := range observedEndpoints { + networkEndpointHealths := []v1beta1.NetworkEndpointHealth{} + for _, networkEndpointHealth := range observedEndpoint.Healths { + networkEndpointHealths = append(networkEndpointHealths, v1beta1.NetworkEndpointHealth{ + ForwardingRule: v1beta1.NetworkEndpointForwardingRule{ + ForwardingRule: networkEndpointHealth.ForwardingRule.ForwardingRule, + }, + BackendService: v1beta1.NetworkEndpointBackendService{ + BackendService: networkEndpointHealth.BackendService.BackendService, + }, + HealthCheck: v1beta1.NetworkEndpointHealthCheck{ + HealthCheck: networkEndpointHealth.HealthCheck.HealthCheck, + }, + HealthCheckService: v1beta1.NetworkEndpointHealthCheckService{ + HealthCheckService: networkEndpointHealth.HealthCheckService.HealthCheckService, + }, + HealthState: networkEndpointHealth.HealthState, + }) + } + networkEndpointObservations = append(networkEndpointObservations, v1beta1.NetworkEndpointObservation{ + NetworkEndpoint: v1beta1.NetworkEndpoint{ + IPAddress: &observedEndpoint.NetworkEndpoint.IpAddress, + FQDN: &observedEndpoint.NetworkEndpoint.Fqdn, + Port: &observedEndpoint.NetworkEndpoint.Port, + Instance: &observedEndpoint.NetworkEndpoint.Instance, + }, + NetworkEndpointHealths: networkEndpointHealths, + }) + } + return v1beta1.NetworkEndpointGroupObservation{ + CreationTimestamp: observed.CreationTimestamp, + ID: observed.Id, + SelfLink: observed.SelfLink, + Size: observed.Size, + NetworkEndpointObservations: networkEndpointObservations, + } +} + +// IsUpToDate checks whether current state is up-to-date compared to the given +// set of parameters. +func IsUpToDate(name string, p *v1beta1.NetworkEndpointGroupParameters, observed *compute.NetworkEndpointGroup, observedEndpoints []*compute.NetworkEndpointWithHealthStatus) (upTodate bool, switchToCustom bool, toBeAdded []*compute.NetworkEndpoint, toBeRemoved []*compute.NetworkEndpoint, err error) { + // The onlu thing that can be updated in a network endpoint group are the networkendpoints. + // To find out of we need to update any network endpoints, we turn them into string set and we calculate the left and right difference. + desiredEndpointMap := map[string]*compute.NetworkEndpoint{} + actualEndpointMap := map[string]*compute.NetworkEndpoint{} + desiredEndpointKeyList := []string{} + actualEndpointKeyList := []string{} + for _, observedNetworkEndpoint := range observedEndpoints { + actualEndpointKeyList = append(actualEndpointKeyList, getKeyFromProviderNetworkEndpoint(observedNetworkEndpoint.NetworkEndpoint)) + actualEndpointMap[getKeyFromProviderNetworkEndpoint(observedNetworkEndpoint.NetworkEndpoint)] = observedNetworkEndpoint.NetworkEndpoint + } + for _, desiredNetworkEndpoint := range p.NetworkEndpoints { + key := getKeyFromNetworkEndpoint(desiredNetworkEndpoint.DeepCopy()) + desiredEndpointKeyList = append(desiredEndpointKeyList, key) + desiredEndpointMap[key] = &compute.NetworkEndpoint{ + Fqdn: gcp.StringValue(desiredNetworkEndpoint.FQDN), + Instance: gcp.StringValue(desiredNetworkEndpoint.Instance), + IpAddress: gcp.StringValue(desiredNetworkEndpoint.IPAddress), + Port: gcp.Int64Value(desiredNetworkEndpoint.Port), + } + } + desiredEndpointSet := strset.New(desiredEndpointKeyList...) + actualEndpointSet := strset.New(actualEndpointKeyList...) + toBeAdded = []*compute.NetworkEndpoint{} + toBeRemoved = []*compute.NetworkEndpoint{} + leftDiff := strset.Difference(desiredEndpointSet, actualEndpointSet) + rightDiff := strset.Difference(actualEndpointSet, desiredEndpointSet) + for _, endpointKey := range leftDiff.List() { + toBeAdded = append(toBeAdded, desiredEndpointMap[endpointKey]) + } + for _, endpointkey := range rightDiff.List() { + toBeRemoved = append(toBeRemoved, actualEndpointMap[endpointkey]) + } + return leftDiff.Size() == 0 && rightDiff.Size() == 0, false, toBeAdded, toBeRemoved, nil +} + +func getKeyFromNetworkEndpoint(networkEndpoint *v1beta1.NetworkEndpoint) string { + return *networkEndpoint.FQDN + "_" + *networkEndpoint.IPAddress + "_" + fmt.Sprint(*networkEndpoint.Port) + "_" + *networkEndpoint.Instance +} + +func getKeyFromProviderNetworkEndpoint(networkEndpoint *compute.NetworkEndpoint) string { + return networkEndpoint.Fqdn + "_" + networkEndpoint.IpAddress + "_" + fmt.Sprint(networkEndpoint.Port) + "_" + networkEndpoint.Instance +} + +// GenerateNetworkEndpointGroup converts the supplied NetworkEndpointGroupParameters into an +// NetworkEndpointGroup suitable for use with the Google Compute API. +func GenerateNetworkEndpointGroup(name string, in v1beta1.NetworkEndpointGroupParameters) (*compute.NetworkEndpointGroup, []*compute.NetworkEndpoint) { + // Kubernetes API conventions dictate that optional, unspecified fields must + // be nil. GCP API clients omit any field set to its zero value, using + // NullFields and ForceSendFields to handle edge cases around unsetting + // previously set values, or forcing zero values to be set. The Address API + // does not support updates, so we can safely convert any nil pointer to + // string or int64 to their zero values. + networkEndpointGroup := &compute.NetworkEndpointGroup{} + networkEndpointGroup.Description = gcp.StringValue(in.Description) + if in.AppEngine != nil { + networkEndpointGroup.AppEngine = &compute.NetworkEndpointGroupAppEngine{ + Service: gcp.StringValue(in.AppEngine.Service), + UrlMask: gcp.StringValue(in.AppEngine.URLMask), + Version: gcp.StringValue(in.AppEngine.Version), + } + } + if in.CloudFunction != nil { + networkEndpointGroup.CloudFunction = &compute.NetworkEndpointGroupCloudFunction{ + Function: gcp.StringValue(&in.CloudFunction.Function), + UrlMask: gcp.StringValue(in.CloudFunction.URLMask), + } + } + if in.CloudRun != nil { + networkEndpointGroup.CloudRun = &compute.NetworkEndpointGroupCloudRun{ + Service: gcp.StringValue(&in.CloudRun.Service), + UrlMask: gcp.StringValue(in.CloudRun.URLMask), + Tag: gcp.StringValue(in.CloudRun.Tag), + } + } + + networkEndpointGroup.DefaultPort = in.DefaultPort + networkEndpointGroup.Subnetwork = gcp.StringValue(in.Subnetwork) + networkEndpointGroup.Network = gcp.StringValue(in.Network) + networkEndpointGroup.NetworkEndpointType = gcp.StringValue(&in.NetworkEndpointType) + + networkendpoints := []*compute.NetworkEndpoint{} + + for _, networkendpoint := range in.NetworkEndpoints { + networkendpoints = append(networkendpoints, &compute.NetworkEndpoint{ + Fqdn: gcp.StringValue(networkendpoint.FQDN), + Instance: gcp.StringValue(networkendpoint.Instance), + IpAddress: gcp.StringValue(networkendpoint.IPAddress), + Port: gcp.Int64Value(networkendpoint.Port), + }) + } + + return networkEndpointGroup, networkendpoints +} diff --git a/pkg/clients/networkendpointgroup/networkendpointgroup_test.go b/pkg/clients/networkendpointgroup/networkendpointgroup_test.go new file mode 100644 index 000000000..3b9298f70 --- /dev/null +++ b/pkg/clients/networkendpointgroup/networkendpointgroup_test.go @@ -0,0 +1,496 @@ +/* +Copyright 2019 The Crossplane Authors. + +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 + + http://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 networkendpointgroup + +import ( + "testing" + + "github.com/crossplane/provider-gcp/apis/compute/v1beta1" + + "github.com/google/go-cmp/cmp" + compute "google.golang.org/api/compute/v1" +) + +var ( + description = "coolDescription" + region = "coolRegion" + zone = "coolZone" + networkEndpointType = "coolType" + network = "coolNetwork" + subnetwork = "coolSubnet" + defaultPort int64 = 3002 + + service string = "myservice" + tag = "mytag" + urlMask = "urlmask" + version = "myversion" + function = "myfunction" + + ip1 = "myip1" + fqdn1 = "myfqdn1" + port1 int64 = 3003 + instance1 = "myinstance1" + + ip2 = "myip2" + fqdn2 = "myfqdn2" + port2 int64 = 3004 + instance2 = "myinstance2" + + cloudRun = v1beta1.CloudRunParameters{ + Service: service, + Tag: &tag, + URLMask: &urlMask, + } + appEngine = v1beta1.AppEngineParameters{ + Service: &service, + Version: &version, + URLMask: &urlMask, + } + cloudFunction = v1beta1.CloudFunctionParameters{ + Function: function, + URLMask: &urlMask, + } + + networkEndpoint1 = v1beta1.NetworkEndpoint{ + IPAddress: &ip1, + FQDN: &fqdn1, + Port: &port1, + Instance: &instance1, + } + + networkEndpoint2 = v1beta1.NetworkEndpoint{ + IPAddress: &ip2, + FQDN: &fqdn2, + Port: &port2, + Instance: &instance2, + } + + networkEndpoints = []v1beta1.NetworkEndpoint{ + networkEndpoint1, + networkEndpoint2, + } + + ccloudRun = compute.NetworkEndpointGroupCloudRun{ + Service: service, + Tag: tag, + UrlMask: urlMask, + } + cappEngine = compute.NetworkEndpointGroupAppEngine{ + Service: service, + UrlMask: urlMask, + Version: version, + } + ccloudFunction = compute.NetworkEndpointGroupCloudFunction{ + Function: function, + UrlMask: urlMask, + } + + cnetworkEndpoint1 = compute.NetworkEndpoint{ + IpAddress: ip1, + Fqdn: fqdn1, + Port: port1, + Instance: instance1, + } + + cnetworkEndpoint2 = compute.NetworkEndpoint{ + IpAddress: ip2, + Fqdn: fqdn2, + Port: port2, + Instance: instance2, + } + + cnetworkEndpoints = []compute.NetworkEndpoint{ + cnetworkEndpoint1, + cnetworkEndpoint2, + } + + forwardingRule1 = "myforwardingrule1" + backendService1 = "backendService1" + healhCheck1 = "myhealhCheck1" + healthCheckService1 = "healthCheckService1" + healthok1 = "myhealthok1" + + forwardingRule2 = "myforwardingrule2" + backendService2 = "backendService2" + healhCheck2 = "myhealhCheck2" + healthCheckService2 = "healthCheckService2" + healthok2 = "myhealthok2" + + networkEndpointHealth1 = v1beta1.NetworkEndpointHealth{ + ForwardingRule: v1beta1.NetworkEndpointForwardingRule{ + ForwardingRule: forwardingRule1, + }, + BackendService: v1beta1.NetworkEndpointBackendService{ + BackendService: backendService1, + }, + HealthCheck: v1beta1.NetworkEndpointHealthCheck{ + HealthCheck: healhCheck1, + }, + HealthCheckService: v1beta1.NetworkEndpointHealthCheckService{ + HealthCheckService: healthCheckService1, + }, + HealthState: healthok1, + } + + networkEndpointHealth2 = v1beta1.NetworkEndpointHealth{ + ForwardingRule: v1beta1.NetworkEndpointForwardingRule{ + ForwardingRule: forwardingRule2, + }, + BackendService: v1beta1.NetworkEndpointBackendService{ + BackendService: backendService2, + }, + HealthCheck: v1beta1.NetworkEndpointHealthCheck{ + HealthCheck: healhCheck2, + }, + HealthCheckService: v1beta1.NetworkEndpointHealthCheckService{ + HealthCheckService: healthCheckService2, + }, + HealthState: healthok2, + } + + networkEndpointObservations = []v1beta1.NetworkEndpointObservation{ + { + NetworkEndpoint: networkEndpoint1, + NetworkEndpointHealths: []v1beta1.NetworkEndpointHealth{ + networkEndpointHealth1, + }, + }, + { + NetworkEndpoint: networkEndpoint1, + NetworkEndpointHealths: []v1beta1.NetworkEndpointHealth{ + networkEndpointHealth2, + }, + }, + } + + healthStatusForNetworkEndpoint1 = compute.HealthStatusForNetworkEndpoint{ + BackendService: &compute.BackendServiceReference{ + BackendService: backendService1, + }, + ForwardingRule: &compute.ForwardingRuleReference{ + ForwardingRule: forwardingRule1, + }, + HealthCheck: &compute.HealthCheckReference{ + HealthCheck: healhCheck1, + }, + HealthCheckService: &compute.HealthCheckServiceReference{ + HealthCheckService: healthCheckService1, + }, + HealthState: healthok1, + } + + healthStatusForNetworkEndpoint2 = compute.HealthStatusForNetworkEndpoint{ + BackendService: &compute.BackendServiceReference{ + BackendService: backendService2, + }, + ForwardingRule: &compute.ForwardingRuleReference{ + ForwardingRule: forwardingRule2, + }, + HealthCheck: &compute.HealthCheckReference{ + HealthCheck: healhCheck2, + }, + HealthCheckService: &compute.HealthCheckServiceReference{ + HealthCheckService: healthCheckService2, + }, + HealthState: healthok2, + } + + networkEnpointsStatus = []*compute.NetworkEndpointWithHealthStatus{ + { + NetworkEndpoint: &cnetworkEndpoint1, + Healths: []*compute.HealthStatusForNetworkEndpoint{ + &healthStatusForNetworkEndpoint1, + }, + }, + { + NetworkEndpoint: &cnetworkEndpoint2, + Healths: []*compute.HealthStatusForNetworkEndpoint{ + &healthStatusForNetworkEndpoint2, + }, + }, + } + + timestamp = "coolTime" + link = "coolLink" + name = "coolName" + id uint64 = 3001 + size int64 = 2 +) + +func params(m ...func(*v1beta1.NetworkEndpointGroupParameters)) *v1beta1.NetworkEndpointGroupParameters { + o := &v1beta1.NetworkEndpointGroupParameters{ + Region: ®ion, + Zone: &zone, + NetworkEndpointType: networkEndpointType, + Description: &description, + Network: &network, + Subnetwork: &subnetwork, + DefaultPort: defaultPort, + CloudRun: &cloudRun, + AppEngine: &appEngine, + CloudFunction: &cloudFunction, + NetworkEndpoints: networkEndpoints, + } + + for _, f := range m { + f(o) + } + + return o +} + +func networkEnpointGroup(m ...func(*compute.NetworkEndpointGroup)) *compute.NetworkEndpointGroup { + o := &compute.NetworkEndpointGroup{ + Name: name, + Region: region, + Zone: zone, + NetworkEndpointType: networkEndpointType, + Description: description, + Network: network, + Subnetwork: subnetwork, + DefaultPort: defaultPort, + CloudRun: &ccloudRun, + AppEngine: &cappEngine, + CloudFunction: &ccloudFunction, + } + + for _, f := range m { + f(o) + } + + return o +} + +func networkEnpoints(m ...func([]compute.NetworkEndpoint)) []compute.NetworkEndpoint { + o := cnetworkEndpoints + + for _, f := range m { + f(o) + } + + return o +} + +func networkEnpointsWithHealthStatus(m ...func([]*compute.NetworkEndpointWithHealthStatus)) []*compute.NetworkEndpointWithHealthStatus { + o := networkEnpointsStatus + + for _, f := range m { + f(o) + } + + return o +} + +func addOutputFields(n *compute.NetworkEndpointGroup) { + n.CreationTimestamp = timestamp + n.Id = id + n.SelfLink = link + n.Size = size +} + +func observation(m ...func(*v1beta1.NetworkEndpointGroupObservation)) *v1beta1.NetworkEndpointGroupObservation { + o := &v1beta1.NetworkEndpointGroupObservation{ + CreationTimestamp: timestamp, + ID: id, + SelfLink: link, + Size: size, + NetworkEndpointObservations: networkEndpointObservations, + } + + for _, f := range m { + f(o) + } + + return o +} + +func TestGenerateNetworkEndpointGroup(t *testing.T) { + type args struct { + name string + in v1beta1.NetworkEndpointGroupParameters + } + cases := map[string]struct { + args args + want *compute.NetworkEndpointGroup + wantnes []compute.NetworkEndpoint + }{ + "AllFilled": { + args: args{ + name: name, + in: *params(), + }, + want: networkEnpointGroup(), + wantnes: networkEnpoints(), + }, + "PartialFilled": { + args: args{ + name: name, + in: *params(func(p *v1beta1.NetworkEndpointGroupParameters) { + p.AppEngine = nil + p.CloudRun = nil + p.CloudFunction = nil + p.Zone = nil + p.Region = nil + p.NetworkEndpoints = nil + }), + }, + want: networkEnpointGroup(func(a *compute.NetworkEndpointGroup) { + a.AppEngine = nil + a.CloudRun = nil + a.CloudFunction = nil + a.Zone = "" + a.Region = "" + }), + wantnes: []compute.NetworkEndpoint{}, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + + r, rs := GenerateNetworkEndpointGroup(tc.args.name, tc.args.in) + if diff := cmp.Diff(r, tc.want); diff != "" { + t.Errorf("GenerateGlobalAddress(...): -want, +got:\n%s", diff) + } + if diff := cmp.Diff(rs, tc.wantnes); diff != "" { + t.Errorf("GenerateGlobalAddress(...): -want, +got:\n%s", diff) + } + }) + } +} + +func TestGenerateNetworkEndpointGroupObservation(t *testing.T) { + cases := map[string]struct { + in compute.NetworkEndpointGroup + innegs []*compute.NetworkEndpointWithHealthStatus + out v1beta1.NetworkEndpointGroupObservation + }{ + "AllFilled": { + in: *networkEnpointGroup(addOutputFields), + innegs: networkEnpointsWithHealthStatus(), + out: *observation(), + }, + "PartialFilled": { + in: *networkEnpointGroup(addOutputFields, func(a *compute.NetworkEndpointGroup) { + a.CreationTimestamp = "" + }), + innegs: []*compute.NetworkEndpointWithHealthStatus{}, + out: *observation(func(o *v1beta1.NetworkEndpointGroupObservation) { + o.CreationTimestamp = "" + }), + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + r := GenerateNetworkEndpointGroupObservation(tc.in, tc.innegs) + if diff := cmp.Diff(r, tc.out); diff != "" { + t.Errorf("GenerateGlobalAddressObservation(...): -want, +got:\n%s", diff) + } + }) + } +} + +func TestLateInitializeSpec(t *testing.T) { + type args struct { + spec *v1beta1.NetworkEndpointGroupParameters + in *compute.NetworkEndpointGroup + innegs []*compute.NetworkEndpointWithHealthStatus + } + cases := map[string]struct { + args args + want *v1beta1.NetworkEndpointGroupParameters + }{ + "AllFilledNoDiff": { + args: args{ + spec: params(), + in: networkEnpointGroup(), + innegs: networkEnpointsWithHealthStatus(), + }, + want: params(), + }, + "AllFilledExternalDiff": { + args: args{ + spec: params(), + in: networkEnpointGroup(func(a *compute.NetworkEndpointGroup) { + a.Description = "some other description" + }), + innegs: networkEnpointsWithHealthStatus(), + }, + + want: params(), + }, + "PartialFilled": { + args: args{ + spec: params(func(p *v1beta1.NetworkEndpointGroupParameters) { + p.Region = nil + p.Zone = nil + }), + in: networkEnpointGroup(), + innegs: networkEnpointsWithHealthStatus(), + }, + want: params(func(p *v1beta1.NetworkEndpointGroupParameters) { + p.Region = ®ion + p.Zone = &zone + }), + }, + "NoEndpoints": { + args: args{ + spec: params(), + in: networkEnpointGroup(), + innegs: []*compute.NetworkEndpointWithHealthStatus{}, + }, + want: params(func(p *v1beta1.NetworkEndpointGroupParameters) { + p.NetworkEndpoints = []v1beta1.NetworkEndpoint{} + }), + }, + "LessEndpoints": { + args: args{ + spec: params(), + in: networkEnpointGroup(), + innegs: networkEnpointsWithHealthStatus()[0:1], + }, + want: params(func(p *v1beta1.NetworkEndpointGroupParameters) { + p.NetworkEndpoints = p.NetworkEndpoints[0:1] + }), + }, + "MoreEndpoints": { + args: args{ + spec: params(), + in: networkEnpointGroup(), + innegs: append(networkEnpointsWithHealthStatus(), &compute.NetworkEndpointWithHealthStatus{ + NetworkEndpoint: &cnetworkEndpoint2, + Healths: []*compute.HealthStatusForNetworkEndpoint{ + &healthStatusForNetworkEndpoint2, + }, + }), + }, + want: params(func(p *v1beta1.NetworkEndpointGroupParameters) { + p.NetworkEndpoints = append(p.NetworkEndpoints, networkEndpoint2) + }), + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + LateInitializeSpec(tc.args.spec, *tc.args.in, tc.args.innegs) + if diff := cmp.Diff(tc.args.spec, tc.want); diff != "" { + t.Errorf("LateInitializeSpec(...): -want, +got:\n%s", diff) + } + }) + } +} diff --git a/pkg/controller/compute/networkedpointgroup_test.go b/pkg/controller/compute/networkedpointgroup_test.go new file mode 100644 index 000000000..879daf5b3 --- /dev/null +++ b/pkg/controller/compute/networkedpointgroup_test.go @@ -0,0 +1,168 @@ +/* +Copyright 2019 The Crossplane Authors. + +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 + + http://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 compute + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/pkg/errors" + "google.golang.org/api/compute/v1" + "google.golang.org/api/option" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/meta" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/resource" + "github.com/crossplane/crossplane-runtime/pkg/test" + + "github.com/crossplane/provider-gcp/apis/compute/v1beta1" +) + +const ( + testNetworkEndpointGroupName = "test-networkendpointgroup" +) + +var _ managed.ExternalConnecter = &negConnector{} +var _ managed.ExternalClient = &negExternal{} + +type networkEndpointGroupModifier func(*v1beta1.NetworkEndpointGroup) + +func networkEndpointGroupWithConditions(c ...xpv1.Condition) networkEndpointGroupModifier { + return func(i *v1beta1.NetworkEndpointGroup) { i.Status.SetConditions(c...) } +} + +func networkEndpointGroupWithDescription(d string) networkEndpointGroupModifier { + return func(i *v1beta1.NetworkEndpointGroup) { i.Spec.ForProvider.Description = &d } +} + +// func subnetworkWithPrivateAccess(p bool) subnetworkModifier { +// return func(i *v1beta1.Subnetwork) { i.Spec.ForProvider.PrivateIPGoogleAccess = &p } +// } + +func networkEndpointGroupObj(im ...networkEndpointGroupModifier) *v1beta1.NetworkEndpointGroup { + i := &v1beta1.NetworkEndpointGroup{ + ObjectMeta: metav1.ObjectMeta{ + Name: testNetworkEndpointGroupName, + Finalizers: []string{}, + Annotations: map[string]string{ + meta.AnnotationKeyExternalName: testNetworkEndpointGroupName, + }, + }, + Spec: v1beta1.NetworkEndpointGroupSpec{ + ForProvider: v1beta1.NetworkEndpointGroupParameters{}, + }, + } + + for _, m := range im { + m(i) + } + + return i +} + +func TestNetworkEndpointGroupObserve(t *testing.T) { + type args struct { + mg resource.Managed + } + type want struct { + mg resource.Managed + obs managed.ExternalObservation + err error + } + + cases := map[string]struct { + handler http.Handler + kube client.Client + args args + want want + }{ + "NotNetworkEndpointGroup": { + handler: nil, + args: args{ + mg: &v1beta1.NetworkEndpointGroup{}, + }, + want: want{ + mg: &v1beta1.Network{}, + err: errors.New(errNotNetworkEndpointGroup), + }, + }, + "NotFound": { + handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _ = r.Body.Close() + if diff := cmp.Diff(http.MethodGet, r.Method); diff != "" { + t.Errorf("r: -want, +got:\n%s", diff) + } + w.WriteHeader(http.StatusNotFound) + _ = json.NewEncoder(w).Encode(&compute.NetworkEndpointGroup{}) + }), + args: args{ + mg: networkEndpointGroupObj(), + }, + want: want{ + mg: networkEndpointGroupObj(), + err: nil, + }, + }, + "GetFailed": { + handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _ = r.Body.Close() + if diff := cmp.Diff(http.MethodGet, r.Method); diff != "" { + t.Errorf("r: -want, +got:\n%s", diff) + } + w.WriteHeader(http.StatusBadRequest) + _ = json.NewEncoder(w).Encode(&compute.NetworkEndpointGroup{}) + }), + args: args{ + mg: networkEndpointGroupObj(), + }, + want: want{ + mg: networkEndpointGroupObj(), + err: errors.Wrap(gError(http.StatusBadRequest, ""), errGetNetworkEndpointGroup), + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + server := httptest.NewServer(tc.handler) + defer server.Close() + s, _ := compute.NewService(context.Background(), option.WithEndpoint(server.URL), option.WithoutAuthentication()) + e := subnetworkExternal{ + kube: tc.kube, + projectID: projectID, + Service: s, + } + obs, err := e.Observe(context.Background(), tc.args.mg) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("Observe(...): -want error, +got error:\n%s", diff) + } + if diff := cmp.Diff(tc.want.obs, obs); diff != "" { + t.Errorf("Observe(...): -want, +got:\n%s", diff) + } + if diff := cmp.Diff(tc.want.mg, tc.args.mg); diff != "" { + t.Errorf("Observe(...): -want, +got:\n%s", diff) + } + }) + } +} diff --git a/pkg/controller/compute/networkendpointgroup.go b/pkg/controller/compute/networkendpointgroup.go new file mode 100644 index 000000000..82b5b7a59 --- /dev/null +++ b/pkg/controller/compute/networkendpointgroup.go @@ -0,0 +1,298 @@ +/* +Copyright 2019 The Crossplane Authors. + +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 + + http://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 compute + +import ( + "context" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/pkg/errors" + "google.golang.org/api/compute/v1" + "k8s.io/client-go/util/workqueue" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/event" + "github.com/crossplane/crossplane-runtime/pkg/logging" + "github.com/crossplane/crossplane-runtime/pkg/meta" + "github.com/crossplane/crossplane-runtime/pkg/ratelimiter" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/resource" + + "github.com/crossplane/provider-gcp/apis/compute/v1beta1" + gcp "github.com/crossplane/provider-gcp/pkg/clients" + "github.com/crossplane/provider-gcp/pkg/clients/networkendpointgroup" +) + +// Error strings. +const ( + errNotNetworkEndpointGroup = "managed resource is not a NetworkEndpointGroup" + errGetNetworkEndpointGroup = "cannot get external NetworkEndpointGroup resource" + errGetNetworkEndpointWithHealthStatus = "cannot get external NetworkEndpoint with health status for NetworkEndpointGroup resource" + errCreateNetworkEndpointGroup = "cannot create external NetworkEndpointGroup resource" + errDeleteNetworkEndpointGroup = "cannot delete external NetworkEndpointGroup resource" + errManagedNetworkEndpointGroupUpdate = "cannot update managed NetworkEndpointGroup resource" + errCheckNetworkEndpointGroupUpToDate = "cannot determine if GCP NetworkEndpointGroup is up to date" + errUpdateNetworkEndpointGroupFailed = "update of GCP NetworkEndpointGroup has failed" +) + +// SetupNetworkEndpointGroup adds a controller that reconciles +// NetworkEndpointGroup managed resources. +func SetupNetworkEndpointGroup(mgr ctrl.Manager, l logging.Logger, rl workqueue.RateLimiter, poll time.Duration) error { + name := managed.ControllerName(v1beta1.NetworkEndpointGroupKind) + + return ctrl.NewControllerManagedBy(mgr). + Named(name). + WithOptions(controller.Options{ + RateLimiter: ratelimiter.NewDefaultManagedRateLimiter(rl), + }). + For(&v1beta1.NetworkEndpointGroup{}). + Complete(managed.NewReconciler(mgr, + resource.ManagedKind(v1beta1.NetworkEndpointGroupGroupVersionKind), + managed.WithExternalConnecter(&gaConnector{kube: mgr.GetClient()}), + managed.WithReferenceResolver(managed.NewAPISimpleReferenceResolver(mgr.GetClient())), + managed.WithConnectionPublishers(), + managed.WithPollInterval(poll), + managed.WithLogger(l.WithValues("controller", name)), + managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))))) +} + +type negConnector struct { + kube client.Client +} + +func (c *negConnector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { + projectID, opts, err := gcp.GetAuthInfo(ctx, c.kube, mg) + if err != nil { + return nil, err + } + s, err := compute.NewService(ctx, opts) + if err != nil { + return nil, errors.Wrap(err, errNewClient) + } + return &negExternal{kube: c.kube, Service: s, projectID: projectID}, errors.Wrap(err, errNewClient) +} + +type negExternal struct { + kube client.Client + projectID string + *compute.Service +} + +func (e *negExternal) getNetworkEndpointGroup(ctx context.Context, neg *v1beta1.NetworkEndpointGroup) (observed *compute.NetworkEndpointGroup, observedEndPoints []*compute.NetworkEndpointWithHealthStatus, err error) { + + if neg.Spec.ForProvider.Zone != nil { + observed, err = e.NetworkEndpointGroups.Get(e.projectID, *neg.Spec.ForProvider.Zone, meta.GetExternalName(neg)).Context(ctx).Do() + if err != nil { + return nil, nil, errors.Wrap(resource.Ignore(gcp.IsErrorNotFound, err), errGetNetworkEndpointGroup) + } + err = e.NetworkEndpointGroups.ListNetworkEndpoints(e.projectID, *neg.Spec.ForProvider.Zone, meta.GetExternalName(neg), &compute.NetworkEndpointGroupsListEndpointsRequest{ + HealthStatus: "SHOW", + }).Pages(ctx, func(neglne *compute.NetworkEndpointGroupsListNetworkEndpoints) error { + observedEndPoints = append(observedEndPoints, neglne.Items...) + return nil + }) + if err != nil { + return nil, nil, errors.Wrap(resource.Ignore(gcp.IsErrorNotFound, err), errGetNetworkEndpointWithHealthStatus) + } + } + if neg.Spec.ForProvider.Region != nil { + observed, err = e.RegionNetworkEndpointGroups.Get(e.projectID, *neg.Spec.ForProvider.Region, meta.GetExternalName(neg)).Context(ctx).Do() + if err != nil { + return nil, nil, errors.Wrap(resource.Ignore(gcp.IsErrorNotFound, err), errGetNetworkEndpointGroup) + } + } + if neg.Spec.ForProvider.Zone == nil && neg.Spec.ForProvider.Region == nil { + observed, err = e.GlobalNetworkEndpointGroups.Get(e.projectID, meta.GetExternalName(neg)).Context(ctx).Do() + if err != nil { + return nil, nil, errors.Wrap(resource.Ignore(gcp.IsErrorNotFound, err), errGetNetworkEndpointGroup) + } + err = e.GlobalNetworkEndpointGroups.ListNetworkEndpoints(e.projectID, meta.GetExternalName(neg)).Pages(ctx, func(neglne *compute.NetworkEndpointGroupsListNetworkEndpoints) error { + observedEndPoints = append(observedEndPoints, neglne.Items...) + return nil + }) + if err != nil { + return nil, nil, errors.Wrap(resource.Ignore(gcp.IsErrorNotFound, err), errGetNetworkEndpointWithHealthStatus) + } + } + return observed, observedEndPoints, nil +} + +func (e *negExternal) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) { + neg, ok := mg.(*v1beta1.NetworkEndpointGroup) + if !ok { + return managed.ExternalObservation{}, errors.New(errNotNetworkEndpointGroup) + } + + observed, observedEndPoints, err := e.getNetworkEndpointGroup(ctx, neg) + if err != nil { + return managed.ExternalObservation{}, err + } + + currentSpec := neg.Spec.ForProvider.DeepCopy() + networkendpointgroup.LateInitializeSpec(&neg.Spec.ForProvider, *observed, observedEndPoints) + + if !cmp.Equal(currentSpec, &neg.Spec.ForProvider) { + if err := e.kube.Update(ctx, neg); err != nil { + return managed.ExternalObservation{}, errors.Wrap(err, errManagedNetworkEndpointGroupUpdate) + } + } + + neg.Status.AtProvider = networkendpointgroup.GenerateNetworkEndpointGroupObservation(*observed, observedEndPoints) + + neg.SetConditions(xpv1.Available()) + + u, _, _, _, err := networkendpointgroup.IsUpToDate(meta.GetExternalName(neg), &neg.Spec.ForProvider, observed, observedEndPoints) + if err != nil { + return managed.ExternalObservation{}, errors.Wrap(err, errCheckNetworkEndpointGroupUpToDate) + } + + return managed.ExternalObservation{ + ResourceExists: true, + ResourceUpToDate: u, + ResourceLateInitialized: !cmp.Equal(currentSpec, &neg.Spec.ForProvider), + }, nil +} + +func (e *negExternal) Create(ctx context.Context, mg resource.Managed) (managed.ExternalCreation, error) { + neg, ok := mg.(*v1beta1.NetworkEndpointGroup) + if !ok { + return managed.ExternalCreation{}, errors.New(errNotNetworkEndpointGroup) + } + + neg.Status.SetConditions(xpv1.Creating()) + + networkEndpointGroup, networkendpoints := networkendpointgroup.GenerateNetworkEndpointGroup(meta.GetExternalName(neg), neg.Spec.ForProvider) + + if neg.Spec.ForProvider.Zone != nil { + _, err := e.NetworkEndpointGroups.Insert(e.projectID, *neg.Spec.ForProvider.Zone, networkEndpointGroup).Context(ctx).Do() + if err != nil { + return managed.ExternalCreation{}, errors.Wrap(err, errCreateNetworkEndpointGroup) + } + _, err = e.NetworkEndpointGroups.AttachNetworkEndpoints(e.projectID, *neg.Spec.ForProvider.Zone, meta.GetExternalName(neg), &compute.NetworkEndpointGroupsAttachEndpointsRequest{ + NetworkEndpoints: networkendpoints, + }).Context(ctx).Do() + if err != nil { + return managed.ExternalCreation{}, errors.Wrap(err, errCreateNetworkEndpointGroup) + } + } + if neg.Spec.ForProvider.Region != nil { + _, err := e.RegionNetworkEndpointGroups.Insert(e.projectID, *neg.Spec.ForProvider.Region, networkEndpointGroup).Context(ctx).Do() + if err != nil { + return managed.ExternalCreation{}, errors.Wrap(err, errCreateNetworkEndpointGroup) + } + // no endpoints allowed for regional network endpoint group + } + if neg.Spec.ForProvider.Zone == nil && neg.Spec.ForProvider.Region == nil { + _, err := e.GlobalNetworkEndpointGroups.Insert(e.projectID, networkEndpointGroup).Context(ctx).Do() + if err != nil { + return managed.ExternalCreation{}, errors.Wrap(err, errCreateNetworkEndpointGroup) + } + _, err = e.GlobalNetworkEndpointGroups.AttachNetworkEndpoints(e.projectID, meta.GetExternalName(neg), &compute.GlobalNetworkEndpointGroupsAttachEndpointsRequest{ + NetworkEndpoints: networkendpoints, + }).Context(ctx).Do() + if err != nil { + return managed.ExternalCreation{}, errors.Wrap(err, errCreateNetworkEndpointGroup) + } + } + return managed.ExternalCreation{}, nil +} + +func (e *negExternal) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) { + neg, ok := mg.(*v1beta1.NetworkEndpointGroup) + if !ok { + return managed.ExternalUpdate{}, errors.New(errNotNetworkEndpointGroup) + } + + observed, observedEndPoints, err := e.getNetworkEndpointGroup(ctx, neg) + if err != nil { + return managed.ExternalUpdate{}, err + } + + // only network endpoints can be updated + u, _, toBeAdded, toBeRemoved, err := networkendpointgroup.IsUpToDate(meta.GetExternalName(neg), &neg.Spec.ForProvider, observed, observedEndPoints) + if err != nil { + return managed.ExternalUpdate{}, errors.Wrap(err, errCheckNetworkEndpointGroupUpToDate) + } + if u { + if neg.Spec.ForProvider.Zone != nil { + _, err := e.NetworkEndpointGroups.DetachNetworkEndpoints(e.projectID, *neg.Spec.ForProvider.Zone, meta.GetExternalName(neg), &compute.NetworkEndpointGroupsDetachEndpointsRequest{ + NetworkEndpoints: toBeRemoved, + }).Context(ctx).Do() + if err != nil { + return managed.ExternalUpdate{}, errors.Wrap(err, errUpdateNetworkEndpointGroupFailed) + } + _, err = e.NetworkEndpointGroups.AttachNetworkEndpoints(e.projectID, *neg.Spec.ForProvider.Zone, meta.GetExternalName(neg), &compute.NetworkEndpointGroupsAttachEndpointsRequest{ + NetworkEndpoints: toBeAdded, + }).Context(ctx).Do() + if err != nil { + return managed.ExternalUpdate{}, errors.Wrap(err, errUpdateNetworkEndpointGroupFailed) + } + } + + if neg.Spec.ForProvider.Region != nil { + // no endpoints for regional networkendpoint groups, so we can return + return managed.ExternalUpdate{}, nil + } + if neg.Spec.ForProvider.Zone == nil && neg.Spec.ForProvider.Region == nil { + _, err := e.GlobalNetworkEndpointGroups.DetachNetworkEndpoints(e.projectID, meta.GetExternalName(neg), &compute.GlobalNetworkEndpointGroupsDetachEndpointsRequest{ + NetworkEndpoints: toBeRemoved, + }).Context(ctx).Do() + if err != nil { + return managed.ExternalUpdate{}, errors.Wrap(err, errUpdateNetworkEndpointGroupFailed) + } + _, err = e.GlobalNetworkEndpointGroups.AttachNetworkEndpoints(e.projectID, meta.GetExternalName(neg), &compute.GlobalNetworkEndpointGroupsAttachEndpointsRequest{ + NetworkEndpoints: toBeAdded, + }).Context(ctx).Do() + if err != nil { + return managed.ExternalUpdate{}, errors.Wrap(err, errUpdateNetworkEndpointGroupFailed) + } + } + } + return managed.ExternalUpdate{}, nil +} + +func (e *negExternal) Delete(ctx context.Context, mg resource.Managed) error { + neg, ok := mg.(*v1beta1.NetworkEndpointGroup) + if !ok { + return errors.New(errNotNetworkEndpointGroup) + } + + neg.Status.SetConditions(xpv1.Deleting()) + if neg.Spec.ForProvider.Zone != nil { + _, err := e.NetworkEndpointGroups.Delete(e.projectID, *neg.Spec.ForProvider.Zone, meta.GetExternalName(neg)).Context(ctx).Do() + if err != nil { + return errors.Wrap(err, errDeleteNetworkEndpointGroup) + } + } + if neg.Spec.ForProvider.Region != nil { + _, err := e.RegionNetworkEndpointGroups.Delete(e.projectID, *neg.Spec.ForProvider.Region, meta.GetExternalName(neg)).Context(ctx).Do() + if err != nil { + return errors.Wrap(err, errDeleteNetworkEndpointGroup) + } + } + if neg.Spec.ForProvider.Zone == nil && neg.Spec.ForProvider.Region == nil { + _, err := e.GlobalNetworkEndpointGroups.Delete(e.projectID, meta.GetExternalName(neg)).Context(ctx).Do() + if err != nil { + return errors.Wrap(err, errDeleteNetworkEndpointGroup) + } + } + return nil +} diff --git a/pkg/controller/gcp.go b/pkg/controller/gcp.go index d1455c699..81d4aa133 100644 --- a/pkg/controller/gcp.go +++ b/pkg/controller/gcp.go @@ -44,6 +44,7 @@ func Setup(mgr ctrl.Manager, l logging.Logger, rl workqueue.RateLimiter, poll ti compute.SetupGlobalAddress, compute.SetupNetwork, compute.SetupSubnetwork, + compute.SetupGlobalAddress, container.SetupCluster, container.SetupNodePool, database.SetupCloudSQLInstance,