Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UCP:Support Service LoadBalancerSourceRanges #2610

Merged
merged 10 commits into from
Jun 8, 2020
17 changes: 17 additions & 0 deletions docs/api-references/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -8038,6 +8038,23 @@ string
<p>PortName is the name of service port</p>
</td>
</tr>
<tr>
<td>
<code>loadBalancerSourceRanges</code></br>
<em>
[]string
</em>
</td>
<td>
<em>(Optional)</em>
<p>LoadBalancerSourceRanges is the loadBalancerSourceRanges of service
If specified and supported by the platform, this will restrict traffic through the cloud-provider
load-balancer will be restricted to the specified client IPs. This field will be ignored if the
cloud-provider does not support the feature.&rdquo;
More info: <a href="https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/">https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/</a>
Optional: Defaults to omitted</p>
</td>
</tr>
</tbody>
</table>
<h3 id="status">Status</h3>
Expand Down
4 changes: 4 additions & 0 deletions manifests/crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,10 @@ spec:
type: string
loadBalancerIP:
type: string
loadBalancerSourceRanges:
items:
type: string
type: array
portName:
type: string
type:
Expand Down
14 changes: 14 additions & 0 deletions pkg/apis/pingcap/v1alpha1/openapi_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions pkg/apis/pingcap/v1alpha1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,15 @@ type ServiceSpec struct {
// PortName is the name of service port
// +optional
PortName *string `json:"portName,omitempty"`

// LoadBalancerSourceRanges is the loadBalancerSourceRanges of service
// If specified and supported by the platform, this will restrict traffic through the cloud-provider
// load-balancer will be restricted to the specified client IPs. This field will be ignored if the
// cloud-provider does not support the feature."
// More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/
// Optional: Defaults to omitted
// +optional
LoadBalancerSourceRanges []string `json:"loadBalancerSourceRanges,omitempty"`
}

// +k8s:openapi-gen=true
Expand Down
29 changes: 28 additions & 1 deletion pkg/apis/pingcap/v1alpha1/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import (
"github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1"
"github.com/pingcap/tidb-operator/pkg/label"
corev1 "k8s.io/api/core/v1"

apivalidation "k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
utilnet "k8s.io/utils/net"
)

// ValidateTidbCluster validates a TidbCluster, it performs basic validation for all TidbClusters despite it is legacy
Expand All @@ -42,6 +42,17 @@ func ValidateTidbCluster(tc *v1alpha1.TidbCluster) field.ErrorList {
return allErrs
}

func ValidateTidbMonitor(monitor *v1alpha1.TidbMonitor) field.ErrorList {
allErrs := field.ErrorList{}
// validate monitor service
if monitor.Spec.Grafana != nil {
allErrs = append(allErrs, validateService(&monitor.Spec.Grafana.Service, field.NewPath("spec"))...)
}
allErrs = append(allErrs, validateService(&monitor.Spec.Prometheus.Service, field.NewPath("spec"))...)
shonge marked this conversation as resolved.
Show resolved Hide resolved
allErrs = append(allErrs, validateService(&monitor.Spec.Reloader.Service, field.NewPath("spec"))...)
shonge marked this conversation as resolved.
Show resolved Hide resolved
return allErrs
}

func validateAnnotations(anns map[string]string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateAnnotations(anns, fldPath)...)
Expand Down Expand Up @@ -164,6 +175,9 @@ func validateTiFlashConfig(config *v1alpha1.TiFlashConfig, path *field.Path) fie
func validateTiDBSpec(spec *v1alpha1.TiDBSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, validateComponentSpec(&spec.ComponentSpec, fldPath)...)
if spec.Service != nil {
allErrs = append(allErrs, validateService(&spec.Service.ServiceSpec, fldPath)...)
}
return allErrs
}

Expand Down Expand Up @@ -399,3 +413,16 @@ func validateDeleteSlots(annotations map[string]string, key string, fldPath *fie
}
return allErrs
}

func validateService(spec *v1alpha1.ServiceSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
//validate LoadBalancerSourceRanges field from service
if len(spec.LoadBalancerSourceRanges) > 0 {
ip := spec.LoadBalancerSourceRanges
_, err := utilnet.ParseIPNets(ip...)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("spec.LoadBalancerSourceRanges"), spec.LoadBalancerSourceRanges, "service.Spec.LoadBalancerSourceRanges is not valid. Expecting a list of IP ranges. For example, 10.0.0.0/24."))
}
}
return allErrs
}
76 changes: 76 additions & 0 deletions pkg/apis/pingcap/v1alpha1/validation/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,85 @@ func TestValidateRequestsStorage(t *testing.T) {
}
}

func TestValidateService(t *testing.T) {
g := NewGomegaWithT(t)
tests := []struct {
name string
loadBalancerSourceRanges []string
expectedErrors int
}{
{
name: "correct LoadBalancerSourceRanges",
loadBalancerSourceRanges: strings.Split("192.168.0.1/32", ","),
expectedErrors: 0,
},
{
name: "incorrect LoadBalancerSourceRanges",
loadBalancerSourceRanges: strings.Split("192.168.0.1", ","),
expectedErrors: 1,
shonge marked this conversation as resolved.
Show resolved Hide resolved
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
svc := newService()
svc.LoadBalancerSourceRanges = tt.loadBalancerSourceRanges
err := validateService(svc, field.NewPath("spec"))
r := len(err)
g.Expect(r).Should(Equal(tt.expectedErrors))
})
}
}

func TestValidateTidbMonitor(t *testing.T) {
g := NewGomegaWithT(t)
tests := []struct {
name string
loadBalancerSourceRanges []string
expectedErrors int
}{
{
name: "correct LoadBalancerSourceRanges",
loadBalancerSourceRanges: strings.Split("192.168.0.1/24,192.168.1.1/24", ","),
expectedErrors: 0,
},
{
name: "incorrect LoadBalancerSourceRanges",
loadBalancerSourceRanges: strings.Split("192.168.0.1,192.168.1.1", ","),
expectedErrors: 3,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
monitor := newTidbMonitor()
monitor.Spec.Prometheus.Service.LoadBalancerSourceRanges = tt.loadBalancerSourceRanges
monitor.Spec.Grafana.Service.LoadBalancerSourceRanges = tt.loadBalancerSourceRanges
monitor.Spec.Reloader.Service.LoadBalancerSourceRanges = tt.loadBalancerSourceRanges
err := ValidateTidbMonitor(monitor)
r := len(err)
g.Expect(r).Should(Equal(tt.expectedErrors))
})
}
}

func newTidbCluster() *v1alpha1.TidbCluster {
tc := &v1alpha1.TidbCluster{}
tc.Name = "test-validate-requests-storage"
tc.Namespace = "default"
return tc
}

func newService() *v1alpha1.ServiceSpec {
svc := &v1alpha1.ServiceSpec{}
return svc
}

func newTidbMonitor() *v1alpha1.TidbMonitor {
monitor := &v1alpha1.TidbMonitor{
Spec: v1alpha1.TidbMonitorSpec{
Grafana: &v1alpha1.GrafanaSpec{},
Prometheus: v1alpha1.PrometheusSpec{},
Reloader: v1alpha1.ReloaderSpec{},
},
}
return monitor
}
5 changes: 5 additions & 0 deletions pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions pkg/manager/member/tidb_member_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,13 @@ func getNewTiDBServiceOrNil(tc *v1alpha1.TidbCluster) *corev1.Service {
Selector: tidbLabels,
},
}
if svcSpec.LoadBalancerIP != nil {
tidbSvc.Spec.LoadBalancerIP = *svcSpec.LoadBalancerIP
if svcSpec.Type == corev1.ServiceTypeLoadBalancer {
if svcSpec.LoadBalancerIP != nil {
tidbSvc.Spec.LoadBalancerIP = *svcSpec.LoadBalancerIP
}
if svcSpec.LoadBalancerSourceRanges != nil {
tidbSvc.Spec.LoadBalancerSourceRanges = svcSpec.LoadBalancerSourceRanges
}
}
if svcSpec.ExternalTrafficPolicy != nil {
tidbSvc.Spec.ExternalTrafficPolicy = *svcSpec.ExternalTrafficPolicy
Expand Down
9 changes: 9 additions & 0 deletions pkg/manager/member/tidb_member_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,10 @@ func TestTiDBInitContainers(t *testing.T) {
func TestGetNewTiDBService(t *testing.T) {
g := NewGomegaWithT(t)
trafficPolicy := corev1.ServiceExternalTrafficPolicyTypeLocal
loadBalancerSourceRanges := []string{
"10.0.0.0/8",
"130.211.204.1/32",
}
testCases := []struct {
name string
tc v1alpha1.TidbCluster
Expand Down Expand Up @@ -1499,6 +1503,7 @@ func TestGetNewTiDBService(t *testing.T) {
Annotations: map[string]string{
"lb-type": "testlb",
},
LoadBalancerSourceRanges: loadBalancerSourceRanges,
},
ExternalTrafficPolicy: &trafficPolicy,
ExposeStatus: pointer.BoolPtr(true),
Expand Down Expand Up @@ -1537,6 +1542,10 @@ func TestGetNewTiDBService(t *testing.T) {
Spec: corev1.ServiceSpec{
Type: corev1.ServiceTypeLoadBalancer,
ExternalTrafficPolicy: corev1.ServiceExternalTrafficPolicyTypeLocal,
LoadBalancerSourceRanges: []string{
"10.0.0.0/8",
"130.211.204.1/32",
},
Ports: []corev1.ServicePort{
{
Name: "mysql-client",
Expand Down
9 changes: 9 additions & 0 deletions pkg/monitor/monitor/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,9 @@ func getMonitorService(monitor *v1alpha1.TidbMonitor) []*core.Service {
if monitor.Spec.Prometheus.Service.LoadBalancerIP != nil {
prometheusService.Spec.LoadBalancerIP = *monitor.Spec.Prometheus.Service.LoadBalancerIP
}
if monitor.Spec.Prometheus.Service.LoadBalancerSourceRanges != nil {
prometheusService.Spec.LoadBalancerSourceRanges = monitor.Spec.Prometheus.Service.LoadBalancerSourceRanges
}
}

reloaderService := &core.Service{
Expand Down Expand Up @@ -718,6 +721,9 @@ func getMonitorService(monitor *v1alpha1.TidbMonitor) []*core.Service {
if monitor.Spec.Reloader.Service.LoadBalancerIP != nil {
reloaderService.Spec.LoadBalancerIP = *monitor.Spec.Reloader.Service.LoadBalancerIP
}
if monitor.Spec.Reloader.Service.LoadBalancerSourceRanges != nil {
reloaderService.Spec.LoadBalancerSourceRanges = monitor.Spec.Reloader.Service.LoadBalancerSourceRanges
}
}

services = append(services, prometheusService, reloaderService)
Expand Down Expand Up @@ -748,6 +754,9 @@ func getMonitorService(monitor *v1alpha1.TidbMonitor) []*core.Service {
if monitor.Spec.Grafana.Service.LoadBalancerIP != nil {
grafanaService.Spec.LoadBalancerIP = *monitor.Spec.Grafana.Service.LoadBalancerIP
}
if monitor.Spec.Grafana.Service.LoadBalancerSourceRanges != nil {
grafanaService.Spec.LoadBalancerSourceRanges = monitor.Spec.Grafana.Service.LoadBalancerSourceRanges
}
}

services = append(services, grafanaService)
Expand Down
Loading