Skip to content

Commit

Permalink
feat: support for defining the orders of components created dynamically
Browse files Browse the repository at this point in the history
  • Loading branch information
leon-inf committed Oct 18, 2024
1 parent dcf84ed commit d8f25eb
Show file tree
Hide file tree
Showing 11 changed files with 311 additions and 87 deletions.
12 changes: 11 additions & 1 deletion apis/apps/v1/clusterdefinition_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,13 @@ type ClusterTopology struct {
// ClusterTopologyComponent defines a Component within a ClusterTopology.
type ClusterTopologyComponent struct {
// Defines the unique identifier of the component within the cluster topology.
//
// It follows IANA Service naming rules and is used as part of the Service's DNS name.
// The name must start with a lowercase letter, can contain lowercase letters, numbers,
// and hyphens, and must end with a lowercase letter or number.
//
// If the @template field is set to true, the name will be used as a prefix to match the specific components dynamically created.
//
// Cannot be updated once set.
//
// +kubebuilder:validation:Required
Expand All @@ -156,11 +159,18 @@ type ClusterTopologyComponent struct {
// 2. Flexible and automatic selection of the most up-to-date ComponentDefinition CR
// by specifying a name prefix or regular expression pattern.
//
// Once set, this field cannot be updated.
// Cannot be updated once set.
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=64
CompDef string `json:"compDef"`

// Specifies whether the topology component will be considered as a template for instantiating components upon user requests dynamically.
//
// Cannot be updated once set.
//
// +optional
Template *bool `json:"template,omitempty"`
}

// ClusterTopologySharding defines a sharding within a ClusterTopology.
Expand Down
9 changes: 8 additions & 1 deletion apis/apps/v1/zz_generated.deepcopy.go

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

16 changes: 14 additions & 2 deletions config/crd/bases/apps.kubeblocks.io_clusterdefinitions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,22 +91,34 @@ spec:
Precise selection by providing the exact name of a ComponentDefinition
CR.\n2. Flexible and automatic selection of the most
up-to-date ComponentDefinition CR\n\t by specifying
a name prefix or regular expression pattern.\n\n\nOnce
set, this field cannot be updated."
a name prefix or regular expression pattern.\n\n\nCannot
be updated once set."
maxLength: 64
type: string
name:
description: |-
Defines the unique identifier of the component within the cluster topology.


It follows IANA Service naming rules and is used as part of the Service's DNS name.
The name must start with a lowercase letter, can contain lowercase letters, numbers,
and hyphens, and must end with a lowercase letter or number.


If the @template field is set to true, the name will be used as a prefix to match the specific components dynamically created.


Cannot be updated once set.
maxLength: 16
pattern: ^[a-z]([a-z0-9\-]*[a-z0-9])?$
type: string
template:
description: |-
Specifies whether the topology component will be considered as a template for instantiating components upon user requests dynamically.


Cannot be updated once set.
type: boolean
required:
- compDef
- name
Expand Down
10 changes: 10 additions & 0 deletions controllers/apps/clusterdefinition_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,3 +381,13 @@ func referredClusterTopology(clusterDef *appsv1.ClusterDefinition, name string)
}
return nil
}

func clusterTopologyCompMatched(comp appsv1.ClusterTopologyComponent, compName string) bool {
if comp.Name == compName {
return true
}
if comp.Template != nil && *comp.Template {
return strings.HasPrefix(compName, comp.Name)
}
return false
}
83 changes: 58 additions & 25 deletions controllers/apps/transformer_cluster_component.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,14 +224,14 @@ const (
)

func newCompNShardingHandler(transCtx *clusterTransformContext, op int) clusterConditionalHandler {
orders := definedOrders(transCtx, op)
topology, orders := definedOrders(transCtx, op)
if len(orders) == 0 {
return newParallelHandler(op)
}
return newOrderedHandler(orders, op)
return newOrderedHandler(topology, orders, op)
}

func definedOrders(transCtx *clusterTransformContext, op int) []string {
func definedOrders(transCtx *clusterTransformContext, op int) (appsv1.ClusterTopology, []string) {
var (
cluster = transCtx.Cluster
clusterDef = transCtx.clusterDef
Expand All @@ -242,19 +242,19 @@ func definedOrders(transCtx *clusterTransformContext, op int) []string {
if topology.Orders != nil {
switch op {
case createOp:
return topology.Orders.Provision
return topology, topology.Orders.Provision
case deleteOp:
return topology.Orders.Terminate
return topology, topology.Orders.Terminate
case updateOp:
return topology.Orders.Update
return topology, topology.Orders.Update
default:
panic("runtime error: unknown operation: " + strconv.Itoa(op))
}
}
}
}
}
return nil
return appsv1.ClusterTopology{}, nil
}

func newParallelHandler(op int) clusterConditionalHandler {
Expand All @@ -266,18 +266,30 @@ func newParallelHandler(op int) clusterConditionalHandler {
panic("runtime error: unknown operation: " + strconv.Itoa(op))
}

func newOrderedHandler(orders []string, op int) clusterConditionalHandler {
func newOrderedHandler(topology appsv1.ClusterTopology, orders []string, op int) clusterConditionalHandler {
switch op {
case createOp, updateOp:
return &orderedCreateNUpdateHandler{
clusterOrderedOrder: clusterOrderedOrder{orders: orders},
phasePrecondition: phasePrecondition{orders: orders},
clusterOrderedOrder: clusterOrderedOrder{
topology: topology,
orders: orders,
},
phasePrecondition: phasePrecondition{
topology: topology,
orders: orders,
},
clusterCompNShardingHandler: clusterCompNShardingHandler{op: op},
}
case deleteOp:
return &orderedDeleteHandler{
clusterOrderedOrder: clusterOrderedOrder{orders: orders},
notExistPrecondition: notExistPrecondition{orders: orders},
clusterOrderedOrder: clusterOrderedOrder{
topology: topology,
orders: orders,
},
notExistPrecondition: notExistPrecondition{
topology: topology,
orders: orders,
},
clusterCompNShardingHandler: clusterCompNShardingHandler{op: op},
}
default:
Expand All @@ -298,16 +310,19 @@ func (o *clusterParallelOrder) ordered(names []string) []string {
}

type clusterOrderedOrder struct {
orders []string
topology appsv1.ClusterTopology
orders []string
}

func (o *clusterOrderedOrder) ordered(names []string) []string {
result := make([]string, 0)
for _, order := range o.orders {
comps := strings.Split(order, ",")
for _, comp := range names {
if slices.Index(comps, comp) >= 0 {
result = append(result, comp)
entities := strings.Split(order, ",")
for _, name := range names {
if slices.ContainsFunc(entities, func(e string) bool {
return clusterTopologyEntityMatched(o.topology, e, name)
}) {
result = append(result, name)
}
}
}
Expand All @@ -324,11 +339,12 @@ func (c *dummyPrecondition) match(*clusterTransformContext, *graph.DAG, string)
}

type notExistPrecondition struct {
orders []string
topology appsv1.ClusterTopology
orders []string
}

func (c *notExistPrecondition) match(transCtx *clusterTransformContext, dag *graph.DAG, name string) (bool, error) {
for _, predecessor := range predecessors(c.orders, name) {
for _, predecessor := range predecessors(c.topology, c.orders, name) {
exist, err := c.predecessorExist(transCtx, dag, predecessor)
if err != nil {
return false, err
Expand Down Expand Up @@ -419,11 +435,12 @@ func (c *notExistPrecondition) shardingExist(transCtx *clusterTransformContext,
}

type phasePrecondition struct {
orders []string
topology appsv1.ClusterTopology
orders []string
}

func (c *phasePrecondition) match(transCtx *clusterTransformContext, dag *graph.DAG, name string) (bool, error) {
for _, predecessor := range predecessors(c.orders, name) {
for _, predecessor := range predecessors(c.topology, c.orders, name) {
match, err := c.predecessorMatch(transCtx, dag, predecessor)
if err != nil {
return false, err
Expand Down Expand Up @@ -548,18 +565,34 @@ func (h *clusterCompNShardingHandler) handle(transCtx *clusterTransformContext,
}
}

func predecessors(orders []string, name string) []string {
func predecessors(topology appsv1.ClusterTopology, orders []string, name string) []string {
var previous []string
for _, order := range orders {
names := strings.Split(order, ",")
if index := slices.Index(names, name); index >= 0 {
entities := strings.Split(order, ",")
if slices.ContainsFunc(entities, func(e string) bool {
return clusterTopologyEntityMatched(topology, e, name)
}) {
return previous
}
previous = names
previous = entities
}
panic("runtime error: cannot find predecessor for component or sharding " + name)
}

func clusterTopologyEntityMatched(topology appsv1.ClusterTopology, entityName, name string) bool {
for _, sharding := range topology.Shardings {
if sharding.Name == entityName {
return entityName == name // full match for sharding
}
}
for _, comp := range topology.Components {
if comp.Name == entityName {
return clusterTopologyCompMatched(comp, name)
}
}
return false // TODO: runtime error
}

type clusterParallelHandler struct {
clusterParallelOrder
dummyPrecondition
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ var _ = Describe("cluster component status transformer", func() {
transCtx.components, transCtx.shardings, err = transformer.resolveCompsNShardingsFromSpecified(transCtx, cluster)
Expect(err).Should(BeNil())

err = transformer.validateNBuildAllCompSpecs(transCtx, cluster)
transCtx.shardingComps, err = transformer.buildShardingComps(transCtx)
Expect(err).Should(BeNil())
}

Expand Down
Loading

0 comments on commit d8f25eb

Please sign in to comment.