api: define API for MultiClusterService

Signed-off-by: jwcesign <jwcesign@gmail.com>
This commit is contained in:
jwcesign 2023-11-21 15:51:27 +08:00
parent 5b8a7f7859
commit a39416cb14
7 changed files with 109 additions and 23 deletions

View File

@ -21169,10 +21169,26 @@
} }
}, },
"range": { "range": {
"description": "Range specifies the ranges where the referencing service should be exposed. Only valid and optional in case of Types contains CrossCluster. If not set and Types contains CrossCluster, all clusters will be selected, that means the referencing service will be exposed across all registered clusters.", "description": "Range specifies the ranges where the referencing service should be exposed. Only valid and optional in case of Types contains CrossCluster. If not set and Types contains CrossCluster, all clusters will be selected, that means the referencing service will be exposed across all registered clusters. Deprecated: in favor of ServiceProvisionClusters/ServiceConsumptionClusters.",
"default": {}, "default": {},
"$ref": "#/definitions/com.github.karmada-io.karmada.pkg.apis.networking.v1alpha1.ExposureRange" "$ref": "#/definitions/com.github.karmada-io.karmada.pkg.apis.networking.v1alpha1.ExposureRange"
}, },
"serviceConsumptionClusters": {
"description": "ServiceConsumptionClusters specifies the clusters where the service will be exposed, for clients. If leave it empty, the service will be exposed to all clusters.",
"type": "array",
"items": {
"type": "string",
"default": ""
}
},
"serviceProvisionClusters": {
"description": "ServiceProvisionClusters specifies the clusters which will provision the service backend. If leave it empty, we will collect the backend endpoints from all clusters and sync them to the ServiceConsumptionClusters.",
"type": "array",
"items": {
"type": "string",
"default": ""
}
},
"types": { "types": {
"description": "Types specifies how to expose the service referencing by this MultiClusterService.", "description": "Types specifies how to expose the service referencing by this MultiClusterService.",
"type": "array", "type": "array",

View File

@ -66,11 +66,11 @@ spec:
type: object type: object
type: array type: array
range: range:
description: Range specifies the ranges where the referencing service description: 'Range specifies the ranges where the referencing service
should be exposed. Only valid and optional in case of Types contains should be exposed. Only valid and optional in case of Types contains
CrossCluster. If not set and Types contains CrossCluster, all clusters CrossCluster. If not set and Types contains CrossCluster, all clusters
will be selected, that means the referencing service will be exposed will be selected, that means the referencing service will be exposed
across all registered clusters. across all registered clusters. Deprecated: in favor of ServiceProvisionClusters/ServiceConsumptionClusters.'
properties: properties:
clusterNames: clusterNames:
description: ClusterNames is the list of clusters to be selected. description: ClusterNames is the list of clusters to be selected.
@ -78,6 +78,20 @@ spec:
type: string type: string
type: array type: array
type: object type: object
serviceConsumptionClusters:
description: ServiceConsumptionClusters specifies the clusters where
the service will be exposed, for clients. If leave it empty, the
service will be exposed to all clusters.
items:
type: string
type: array
serviceProvisionClusters:
description: ServiceProvisionClusters specifies the clusters which
will provision the service backend. If leave it empty, we will collect
the backend endpoints from all clusters and sync them to the ServiceConsumptionClusters.
items:
type: string
type: array
types: types:
description: Types specifies how to expose the service referencing description: Types specifies how to expose the service referencing
by this MultiClusterService. by this MultiClusterService.

View File

@ -75,8 +75,20 @@ type MultiClusterServiceSpec struct {
// If not set and Types contains CrossCluster, all clusters will // If not set and Types contains CrossCluster, all clusters will
// be selected, that means the referencing service will be exposed // be selected, that means the referencing service will be exposed
// across all registered clusters. // across all registered clusters.
// Deprecated: in favor of ServiceProvisionClusters/ServiceConsumptionClusters.
// +optional // +optional
Range ExposureRange `json:"range,omitempty"` Range ExposureRange `json:"range,omitempty"`
// ServiceProvisionClusters specifies the clusters which will provision the service backend.
// If leave it empty, we will collect the backend endpoints from all clusters and sync
// them to the ServiceConsumptionClusters.
// +optional
ServiceProvisionClusters []string `json:"serviceProvisionClusters,omitempty"`
// ServiceConsumptionClusters specifies the clusters where the service will be exposed, for clients.
// If leave it empty, the service will be exposed to all clusters.
// +optional
ServiceConsumptionClusters []string `json:"serviceConsumptionClusters,omitempty"`
} }
// ExposureType describes how to expose the service. // ExposureType describes how to expose the service.

View File

@ -182,6 +182,16 @@ func (in *MultiClusterServiceSpec) DeepCopyInto(out *MultiClusterServiceSpec) {
copy(*out, *in) copy(*out, *in)
} }
in.Range.DeepCopyInto(&out.Range) in.Range.DeepCopyInto(&out.Range)
if in.ServiceProvisionClusters != nil {
in, out := &in.ServiceProvisionClusters, &out.ServiceProvisionClusters
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ServiceConsumptionClusters != nil {
in, out := &in.ServiceConsumptionClusters, &out.ServiceConsumptionClusters
*out = make([]string, len(*in))
copy(*out, *in)
}
return return
} }

View File

@ -2966,11 +2966,41 @@ func schema_pkg_apis_networking_v1alpha1_MultiClusterServiceSpec(ref common.Refe
}, },
"range": { "range": {
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "Range specifies the ranges where the referencing service should be exposed. Only valid and optional in case of Types contains CrossCluster. If not set and Types contains CrossCluster, all clusters will be selected, that means the referencing service will be exposed across all registered clusters.", Description: "Range specifies the ranges where the referencing service should be exposed. Only valid and optional in case of Types contains CrossCluster. If not set and Types contains CrossCluster, all clusters will be selected, that means the referencing service will be exposed across all registered clusters. Deprecated: in favor of ServiceProvisionClusters/ServiceConsumptionClusters.",
Default: map[string]interface{}{}, Default: map[string]interface{}{},
Ref: ref("github.com/karmada-io/karmada/pkg/apis/networking/v1alpha1.ExposureRange"), Ref: ref("github.com/karmada-io/karmada/pkg/apis/networking/v1alpha1.ExposureRange"),
}, },
}, },
"serviceProvisionClusters": {
SchemaProps: spec.SchemaProps{
Description: "ServiceProvisionClusters specifies the clusters which will provision the service backend. If leave it empty, we will collect the backend endpoints from all clusters and sync them to the ServiceConsumptionClusters.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
"serviceConsumptionClusters": {
SchemaProps: spec.SchemaProps{
Description: "ServiceConsumptionClusters specifies the clusters where the service will be exposed, for clients. If leave it empty, the service will be exposed to all clusters.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
}, },
Required: []string{"types"}, Required: []string{"types"},
}, },

View File

@ -103,10 +103,19 @@ func (v *ValidatingAdmission) validateMultiClusterServiceSpec(mcs *networkingv1a
exposureType := mcs.Spec.Types[i] exposureType := mcs.Spec.Types[i]
allErrs = append(allErrs, v.validateExposureType(&exposureType, typePath)...) allErrs = append(allErrs, v.validateExposureType(&exposureType, typePath)...)
} }
clusterNamesPath := specPath.Child("range").Child("clusterNames") clusterNamesPath := specPath.Child("range").Child("serviceProvisionClusters")
for i := range mcs.Spec.Range.ClusterNames { for i := range mcs.Spec.ServiceProvisionClusters {
clusterNamePath := clusterNamesPath.Index(i) clusterNamePath := clusterNamesPath.Index(i)
clusterName := mcs.Spec.Range.ClusterNames[i] clusterName := mcs.Spec.ServiceProvisionClusters[i]
if errMegs := clustervalidation.ValidateClusterName(clusterName); len(errMegs) > 0 {
allErrs = append(allErrs, field.Invalid(clusterNamePath, clusterName, strings.Join(errMegs, ",")))
}
}
clusterNamesPath = specPath.Child("range").Child("serviceConsumptionClusters")
for i := range mcs.Spec.ServiceConsumptionClusters {
clusterNamePath := clusterNamesPath.Index(i)
clusterName := mcs.Spec.ServiceConsumptionClusters[i]
if errMegs := clustervalidation.ValidateClusterName(clusterName); len(errMegs) > 0 { if errMegs := clustervalidation.ValidateClusterName(clusterName); len(errMegs) > 0 {
allErrs = append(allErrs, field.Invalid(clusterNamePath, clusterName, strings.Join(errMegs, ","))) allErrs = append(allErrs, field.Invalid(clusterNamePath, clusterName, strings.Join(errMegs, ",")))
} }

View File

@ -53,9 +53,8 @@ func TestValidateMultiClusterServiceSpec(t *testing.T) {
networkingv1alpha1.ExposureTypeLoadBalancer, networkingv1alpha1.ExposureTypeLoadBalancer,
networkingv1alpha1.ExposureTypeCrossCluster, networkingv1alpha1.ExposureTypeCrossCluster,
}, },
Range: networkingv1alpha1.ExposureRange{ ServiceProvisionClusters: []string{"member1", "member2"},
ClusterNames: []string{"member1", "member2"}, ServiceConsumptionClusters: []string{"member1", "member2"},
},
}, },
}, },
expectedErr: field.ErrorList{}, expectedErr: field.ErrorList{},
@ -78,9 +77,8 @@ func TestValidateMultiClusterServiceSpec(t *testing.T) {
networkingv1alpha1.ExposureTypeLoadBalancer, networkingv1alpha1.ExposureTypeLoadBalancer,
networkingv1alpha1.ExposureTypeLoadBalancer, networkingv1alpha1.ExposureTypeLoadBalancer,
}, },
Range: networkingv1alpha1.ExposureRange{ ServiceProvisionClusters: []string{"member1", "member2"},
ClusterNames: []string{"member1"}, ServiceConsumptionClusters: []string{"member1", "member2"},
},
}, },
}, },
expectedErr: field.ErrorList{field.Duplicate(specFld.Child("ports").Index(1).Child("name"), "foo")}, expectedErr: field.ErrorList{field.Duplicate(specFld.Child("ports").Index(1).Child("name"), "foo")},
@ -98,9 +96,8 @@ func TestValidateMultiClusterServiceSpec(t *testing.T) {
Types: []networkingv1alpha1.ExposureType{ Types: []networkingv1alpha1.ExposureType{
networkingv1alpha1.ExposureTypeLoadBalancer, networkingv1alpha1.ExposureTypeLoadBalancer,
}, },
Range: networkingv1alpha1.ExposureRange{ ServiceProvisionClusters: []string{"member1", "member2"},
ClusterNames: []string{"member1"}, ServiceConsumptionClusters: []string{"member1", "member2"},
},
}, },
}, },
expectedErr: field.ErrorList{field.Invalid(specFld.Child("ports").Index(0).Child("port"), int32(163121), validation.InclusiveRangeError(1, 65535))}, expectedErr: field.ErrorList{field.Invalid(specFld.Child("ports").Index(0).Child("port"), int32(163121), validation.InclusiveRangeError(1, 65535))},
@ -118,9 +115,8 @@ func TestValidateMultiClusterServiceSpec(t *testing.T) {
Types: []networkingv1alpha1.ExposureType{ Types: []networkingv1alpha1.ExposureType{
"", "",
}, },
Range: networkingv1alpha1.ExposureRange{ ServiceProvisionClusters: []string{"member1", "member2"},
ClusterNames: []string{"member1"}, ServiceConsumptionClusters: []string{"member1", "member2"},
},
}, },
}, },
expectedErr: field.ErrorList{field.Invalid(specFld.Child("types").Index(0), networkingv1alpha1.ExposureType(""), "ExposureType Error")}, expectedErr: field.ErrorList{field.Invalid(specFld.Child("types").Index(0), networkingv1alpha1.ExposureType(""), "ExposureType Error")},
@ -138,12 +134,11 @@ func TestValidateMultiClusterServiceSpec(t *testing.T) {
Types: []networkingv1alpha1.ExposureType{ Types: []networkingv1alpha1.ExposureType{
networkingv1alpha1.ExposureTypeCrossCluster, networkingv1alpha1.ExposureTypeCrossCluster,
}, },
Range: networkingv1alpha1.ExposureRange{ ServiceProvisionClusters: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"},
ClusterNames: []string{"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, ServiceConsumptionClusters: []string{},
},
}, },
}, },
expectedErr: field.ErrorList{field.Invalid(specFld.Child("range").Child("clusterNames").Index(0), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "must be no more than 48 characters")}, expectedErr: field.ErrorList{field.Invalid(specFld.Child("range").Child("serviceProvisionClusters").Index(0), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "must be no more than 48 characters")},
}, },
} }
for _, tt := range tests { for _, tt := range tests {