diff --git a/.gitignore b/.gitignore index 97a03718c..6bd14b1c9 100644 --- a/.gitignore +++ b/.gitignore @@ -45,5 +45,4 @@ bin/ build/ vendor/ -# dubbo cp cache -app/dubbo-cp/cache + diff --git a/api/mesh/v1alpha1/condition_route.pb.go b/api/mesh/v1alpha1/condition_route.pb.go index 5501eaf51..f9cbb44e9 100644 --- a/api/mesh/v1alpha1/condition_route.pb.go +++ b/api/mesh/v1alpha1/condition_route.pb.go @@ -26,11 +26,14 @@ type ConditionRoute struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // Types that are assignable to Conditions: - // - // *ConditionRoute_ConditionsV3 - // *ConditionRoute_ConditionsV3X1 - Conditions isConditionRoute_Conditions `protobuf_oneof:"conditions"` + ConfigVersion string `protobuf:"bytes,1,opt,name=configVersion,proto3" json:"configVersion,omitempty"` + Priority int32 `protobuf:"varint,2,opt,name=priority,proto3" json:"priority,omitempty"` + Enabled bool `protobuf:"varint,3,opt,name=enabled,proto3" json:"enabled,omitempty"` + Force bool `protobuf:"varint,4,opt,name=force,proto3" json:"force,omitempty"` + Runtime bool `protobuf:"varint,5,opt,name=runtime,proto3" json:"runtime,omitempty"` + Key string `protobuf:"bytes,6,opt,name=key,proto3" json:"key,omitempty"` + Scope string `protobuf:"bytes,7,opt,name=scope,proto3" json:"scope,omitempty"` + Conditions []string `protobuf:"bytes,8,rep,name=conditions,proto3" json:"conditions,omitempty"` } func (x *ConditionRoute) Reset() { @@ -63,42 +66,61 @@ func (*ConditionRoute) Descriptor() ([]byte, []int) { return file_api_mesh_v1alpha1_condition_route_proto_rawDescGZIP(), []int{0} } -func (m *ConditionRoute) GetConditions() isConditionRoute_Conditions { - if m != nil { - return m.Conditions +func (x *ConditionRoute) GetConfigVersion() string { + if x != nil { + return x.ConfigVersion } - return nil + return "" } -func (x *ConditionRoute) GetConditionsV3() *ConditionRouteV3 { - if x, ok := x.GetConditions().(*ConditionRoute_ConditionsV3); ok { - return x.ConditionsV3 +func (x *ConditionRoute) GetPriority() int32 { + if x != nil { + return x.Priority } - return nil + return 0 } -func (x *ConditionRoute) GetConditionsV3X1() *ConditionRouteV3X1 { - if x, ok := x.GetConditions().(*ConditionRoute_ConditionsV3X1); ok { - return x.ConditionsV3X1 +func (x *ConditionRoute) GetEnabled() bool { + if x != nil { + return x.Enabled } - return nil + return false } -type isConditionRoute_Conditions interface { - isConditionRoute_Conditions() +func (x *ConditionRoute) GetForce() bool { + if x != nil { + return x.Force + } + return false } -type ConditionRoute_ConditionsV3 struct { - ConditionsV3 *ConditionRouteV3 `protobuf:"bytes,1,opt,name=conditionsV3,proto3,oneof"` +func (x *ConditionRoute) GetRuntime() bool { + if x != nil { + return x.Runtime + } + return false } -type ConditionRoute_ConditionsV3X1 struct { - ConditionsV3X1 *ConditionRouteV3X1 `protobuf:"bytes,2,opt,name=conditionsV3x1,proto3,oneof"` +func (x *ConditionRoute) GetKey() string { + if x != nil { + return x.Key + } + return "" } -func (*ConditionRoute_ConditionsV3) isConditionRoute_Conditions() {} +func (x *ConditionRoute) GetScope() string { + if x != nil { + return x.Scope + } + return "" +} -func (*ConditionRoute_ConditionsV3X1) isConditionRoute_Conditions() {} +func (x *ConditionRoute) GetConditions() []string { + if x != nil { + return x.Conditions + } + return nil +} type ConditionRule struct { state protoimpl.MessageState @@ -251,200 +273,6 @@ func (x *ConditionRuleTo) GetWeight() int32 { return 0 } -type ConditionRouteV3 struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ConfigVersion string `protobuf:"bytes,1,opt,name=configVersion,proto3" json:"configVersion,omitempty"` - Priority int32 `protobuf:"varint,2,opt,name=priority,proto3" json:"priority,omitempty"` - Enabled bool `protobuf:"varint,3,opt,name=enabled,proto3" json:"enabled,omitempty"` - Force bool `protobuf:"varint,4,opt,name=force,proto3" json:"force,omitempty"` - Runtime bool `protobuf:"varint,5,opt,name=runtime,proto3" json:"runtime,omitempty"` - Key string `protobuf:"bytes,6,opt,name=key,proto3" json:"key,omitempty"` - Scope string `protobuf:"bytes,7,opt,name=scope,proto3" json:"scope,omitempty"` - Conditions []string `protobuf:"bytes,8,rep,name=conditions,proto3" json:"conditions,omitempty"` -} - -func (x *ConditionRouteV3) Reset() { - *x = ConditionRouteV3{} - mi := &file_api_mesh_v1alpha1_condition_route_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ConditionRouteV3) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConditionRouteV3) ProtoMessage() {} - -func (x *ConditionRouteV3) ProtoReflect() protoreflect.Message { - mi := &file_api_mesh_v1alpha1_condition_route_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConditionRouteV3.ProtoReflect.Descriptor instead. -func (*ConditionRouteV3) Descriptor() ([]byte, []int) { - return file_api_mesh_v1alpha1_condition_route_proto_rawDescGZIP(), []int{0, 0} -} - -func (x *ConditionRouteV3) GetConfigVersion() string { - if x != nil { - return x.ConfigVersion - } - return "" -} - -func (x *ConditionRouteV3) GetPriority() int32 { - if x != nil { - return x.Priority - } - return 0 -} - -func (x *ConditionRouteV3) GetEnabled() bool { - if x != nil { - return x.Enabled - } - return false -} - -func (x *ConditionRouteV3) GetForce() bool { - if x != nil { - return x.Force - } - return false -} - -func (x *ConditionRouteV3) GetRuntime() bool { - if x != nil { - return x.Runtime - } - return false -} - -func (x *ConditionRouteV3) GetKey() string { - if x != nil { - return x.Key - } - return "" -} - -func (x *ConditionRouteV3) GetScope() string { - if x != nil { - return x.Scope - } - return "" -} - -func (x *ConditionRouteV3) GetConditions() []string { - if x != nil { - return x.Conditions - } - return nil -} - -type ConditionRouteV3X1 struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ConfigVersion string `protobuf:"bytes,1,opt,name=configVersion,proto3" json:"configVersion,omitempty"` - Scope string `protobuf:"bytes,2,opt,name=scope,proto3" json:"scope,omitempty"` // must be chosen from `service` and `application` - Key string `protobuf:"bytes,3,opt,name=key,proto3" json:"key,omitempty"` // specifies which service or application the rule body acts on - Force bool `protobuf:"varint,4,opt,name=force,proto3" json:"force,omitempty"` - Runtime bool `protobuf:"varint,5,opt,name=runtime,proto3" json:"runtime,omitempty"` - Enabled bool `protobuf:"varint,6,opt,name=enabled,proto3" json:"enabled,omitempty"` - Conditions []*ConditionRule `protobuf:"bytes,8,rep,name=conditions,proto3" json:"conditions,omitempty"` -} - -func (x *ConditionRouteV3X1) Reset() { - *x = ConditionRouteV3X1{} - mi := &file_api_mesh_v1alpha1_condition_route_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ConditionRouteV3X1) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ConditionRouteV3X1) ProtoMessage() {} - -func (x *ConditionRouteV3X1) ProtoReflect() protoreflect.Message { - mi := &file_api_mesh_v1alpha1_condition_route_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ConditionRouteV3X1.ProtoReflect.Descriptor instead. -func (*ConditionRouteV3X1) Descriptor() ([]byte, []int) { - return file_api_mesh_v1alpha1_condition_route_proto_rawDescGZIP(), []int{0, 1} -} - -func (x *ConditionRouteV3X1) GetConfigVersion() string { - if x != nil { - return x.ConfigVersion - } - return "" -} - -func (x *ConditionRouteV3X1) GetScope() string { - if x != nil { - return x.Scope - } - return "" -} - -func (x *ConditionRouteV3X1) GetKey() string { - if x != nil { - return x.Key - } - return "" -} - -func (x *ConditionRouteV3X1) GetForce() bool { - if x != nil { - return x.Force - } - return false -} - -func (x *ConditionRouteV3X1) GetRuntime() bool { - if x != nil { - return x.Runtime - } - return false -} - -func (x *ConditionRouteV3X1) GetEnabled() bool { - if x != nil { - return x.Enabled - } - return false -} - -func (x *ConditionRouteV3X1) GetConditions() []*ConditionRule { - if x != nil { - return x.Conditions - } - return nil -} - var File_api_mesh_v1alpha1_condition_route_proto protoreflect.FileDescriptor var file_api_mesh_v1alpha1_condition_route_proto_rawDesc = []byte{ @@ -453,69 +281,43 @@ var file_api_mesh_v1alpha1_condition_route_proto_rawDesc = []byte{ 0x75, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x16, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xaf, 0x05, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x56, 0x33, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x26, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x76, 0x33, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x56, 0x33, 0x12, 0x52, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x56, 0x33, 0x78, 0x31, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x28, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x2e, 0x76, 0x33, 0x78, 0x31, 0x48, 0x00, 0x52, 0x0e, 0x63, 0x6f, 0x6e, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x56, 0x33, 0x78, 0x31, 0x1a, 0xd8, 0x01, 0x0a, 0x02, - 0x76, 0x33, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, - 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, - 0x72, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x14, - 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, - 0x6f, 0x72, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0xe2, 0x01, 0x0a, 0x04, 0x76, 0x33, 0x78, 0x31, 0x12, - 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, - 0x72, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x64, 0x75, - 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x52, - 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x2d, 0xaa, 0x8c, 0x89, - 0xa6, 0x01, 0x27, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x12, 0x0f, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x73, 0x1a, 0x04, 0x6d, 0x65, 0x73, 0x68, 0x42, 0x0c, 0x0a, 0x0a, 0x63, 0x6f, - 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, - 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x3a, 0x0a, 0x04, 0x66, 0x72, - 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, - 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, - 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x46, 0x72, 0x6f, 0x6d, - 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x34, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x6f, 0x52, 0x02, 0x74, 0x6f, 0x22, 0x29, 0x0a, 0x11, - 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x46, 0x72, 0x6f, - 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x22, 0x3f, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x64, 0x69, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, - 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, - 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, - 0x62, 0x62, 0x6f, 0x2d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, - 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x93, 0x02, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x65, + 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, + 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x75, + 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x12, 0x1e, 0x0a, + 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x2d, 0xaa, + 0x8c, 0x89, 0xa6, 0x01, 0x27, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x0f, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x1a, 0x04, 0x6d, 0x65, 0x73, 0x68, 0x22, 0x81, 0x01, 0x0a, + 0x0d, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x3a, + 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x64, + 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, + 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x34, 0x0a, 0x02, 0x74, 0x6f, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, + 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x6f, 0x52, 0x02, 0x74, 0x6f, + 0x22, 0x29, 0x0a, 0x11, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, + 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x22, 0x3f, 0x0a, 0x0f, 0x43, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x54, 0x6f, 0x12, 0x14, + 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, + 0x61, 0x74, 0x63, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x31, 0x5a, 0x2f, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, + 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2f, 0x61, 0x70, + 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -530,26 +332,21 @@ func file_api_mesh_v1alpha1_condition_route_proto_rawDescGZIP() []byte { return file_api_mesh_v1alpha1_condition_route_proto_rawDescData } -var file_api_mesh_v1alpha1_condition_route_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_api_mesh_v1alpha1_condition_route_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_api_mesh_v1alpha1_condition_route_proto_goTypes = []any{ - (*ConditionRoute)(nil), // 0: dubbo.mesh.v1alpha1.ConditionRoute - (*ConditionRule)(nil), // 1: dubbo.mesh.v1alpha1.ConditionRule - (*ConditionRuleFrom)(nil), // 2: dubbo.mesh.v1alpha1.ConditionRuleFrom - (*ConditionRuleTo)(nil), // 3: dubbo.mesh.v1alpha1.ConditionRuleTo - (*ConditionRouteV3)(nil), // 4: dubbo.mesh.v1alpha1.ConditionRoute.v3 - (*ConditionRouteV3X1)(nil), // 5: dubbo.mesh.v1alpha1.ConditionRoute.v3x1 + (*ConditionRoute)(nil), // 0: dubbo.mesh.v1alpha1.ConditionRoute + (*ConditionRule)(nil), // 1: dubbo.mesh.v1alpha1.ConditionRule + (*ConditionRuleFrom)(nil), // 2: dubbo.mesh.v1alpha1.ConditionRuleFrom + (*ConditionRuleTo)(nil), // 3: dubbo.mesh.v1alpha1.ConditionRuleTo } var file_api_mesh_v1alpha1_condition_route_proto_depIdxs = []int32{ - 4, // 0: dubbo.mesh.v1alpha1.ConditionRoute.conditionsV3:type_name -> dubbo.mesh.v1alpha1.ConditionRoute.v3 - 5, // 1: dubbo.mesh.v1alpha1.ConditionRoute.conditionsV3x1:type_name -> dubbo.mesh.v1alpha1.ConditionRoute.v3x1 - 2, // 2: dubbo.mesh.v1alpha1.ConditionRule.from:type_name -> dubbo.mesh.v1alpha1.ConditionRuleFrom - 3, // 3: dubbo.mesh.v1alpha1.ConditionRule.to:type_name -> dubbo.mesh.v1alpha1.ConditionRuleTo - 1, // 4: dubbo.mesh.v1alpha1.ConditionRoute.v3x1.conditions:type_name -> dubbo.mesh.v1alpha1.ConditionRule - 5, // [5:5] is the sub-list for method output_type - 5, // [5:5] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 2, // 0: dubbo.mesh.v1alpha1.ConditionRule.from:type_name -> dubbo.mesh.v1alpha1.ConditionRuleFrom + 3, // 1: dubbo.mesh.v1alpha1.ConditionRule.to:type_name -> dubbo.mesh.v1alpha1.ConditionRuleTo + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_api_mesh_v1alpha1_condition_route_proto_init() } @@ -557,17 +354,13 @@ func file_api_mesh_v1alpha1_condition_route_proto_init() { if File_api_mesh_v1alpha1_condition_route_proto != nil { return } - file_api_mesh_v1alpha1_condition_route_proto_msgTypes[0].OneofWrappers = []any{ - (*ConditionRoute_ConditionsV3)(nil), - (*ConditionRoute_ConditionsV3X1)(nil), - } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_mesh_v1alpha1_condition_route_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/api/mesh/v1alpha1/condition_route.proto b/api/mesh/v1alpha1/condition_route.proto index 6eeddd59a..b2a9870ba 100644 --- a/api/mesh/v1alpha1/condition_route.proto +++ b/api/mesh/v1alpha1/condition_route.proto @@ -12,7 +12,6 @@ message ConditionRoute { option (dubbo.mesh.resource).package = "mesh"; option (dubbo.mesh.resource).is_experimental = false; - message v3 { string configVersion = 1; int32 priority = 2; bool enabled = 3; @@ -21,22 +20,6 @@ message ConditionRoute { string key = 6; string scope = 7; repeated string conditions = 8; - } - - message v3x1 { - string configVersion = 1; - string scope = 2; // must be chosen from `service` and `application` - string key = 3; // specifies which service or application the rule body acts on - bool force = 4; - bool runtime = 5; - bool enabled = 6; - repeated ConditionRule conditions = 8; - } - - oneof conditions { - v3 conditionsV3 = 1; - v3x1 conditionsV3x1 = 2; - } } message ConditionRule { diff --git a/api/mesh/v1alpha1/condition_route_helper.go b/api/mesh/v1alpha1/condition_route_helper.go index d3f3c529f..169ac422b 100644 --- a/api/mesh/v1alpha1/condition_route_helper.go +++ b/api/mesh/v1alpha1/condition_route_helper.go @@ -20,115 +20,9 @@ package v1alpha1 import ( "strings" - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - "github.com/apache/dubbo-admin/pkg/common/constants" - "github.com/apache/dubbo-admin/pkg/common/util/proto" ) -func (x *ConditionRoute) GetVersion() string { - if x.ToConditionRouteV3() != nil { - return constants.ConfiguratorVersionV3 - } else { - return constants.ConfiguratorVersionV3x1 - } -} - -func (x *ConditionRoute) ToYAML() ([]byte, error) { - if msg := x.ToConditionRouteV3x1(); msg != nil { - return proto.ToYAML(msg) - } else if msg := x.ToConditionRouteV3(); msg != nil { - return proto.ToYAML(msg) - } - return nil, errors.New(`ConditionRoute validation failed`) -} - -func ConditionRouteDecodeFromYAML(content []byte) (*ConditionRoute, error) { - _map := map[string]interface{}{} - err := yaml.Unmarshal(content, &_map) - if err != nil { - return nil, err - } - - version, ok := _map[constants.ConfigVersionKey].(string) - if !ok { - return nil, errors.New("invalid condition route format") - } - if version == constants.ConfiguratorVersionV3 { - v3 := new(ConditionRouteV3) - if err = proto.FromYAML(content, v3); err != nil { - return nil, err - } - return v3.ToConditionRoute(), nil - } else if version == constants.ConfiguratorVersionV3x1 { - v3x1 := new(ConditionRouteV3X1) - if err = proto.FromYAML(content, v3x1); err != nil { - return nil, err - } - return v3x1.ToConditionRoute(), nil - } else { - return nil, errors.New("invalid condition route format") - } -} - -func (x *ConditionRouteV3) ToConditionRoute() *ConditionRoute { - return &ConditionRoute{Conditions: &ConditionRoute_ConditionsV3{ConditionsV3: x}} -} - -func (x *ConditionRouteV3X1) ToConditionRoute() *ConditionRoute { - return &ConditionRoute{Conditions: &ConditionRoute_ConditionsV3X1{ConditionsV3X1: x}} -} - -func (x *ConditionRoute) ToConditionRouteV3() *ConditionRouteV3 { - if v, ok := x.Conditions.(*ConditionRoute_ConditionsV3); ok { - return v.ConditionsV3 - } - return nil -} - -func (x *ConditionRoute) ToConditionRouteV3x1() *ConditionRouteV3X1 { - if v, ok := x.Conditions.(*ConditionRoute_ConditionsV3X1); ok { - return v.ConditionsV3X1 - } - return nil -} - -func (x *ConditionRouteV3X1) RangeConditions(f func(r *ConditionRule) (isStop bool)) { - if f == nil { - return - } - for _, condition := range x.Conditions { - if condition != nil && f(condition) { - break - } - } -} - -func (x *ConditionRouteV3) RangeConditions(f func(condition string) (isStop bool)) { - if f == nil { - return - } - for _, condition := range x.Conditions { - if f(condition) { - break - } - } -} - -func (x *ConditionRouteV3X1) RangeConditionsToRemove(f func(r *ConditionRule) (isRemove bool)) { - if f == nil { - return - } - res := make([]*ConditionRule, 0, len(x.Conditions)/2+1) - for _, condition := range x.Conditions { - if condition != nil && !f(condition) { - res = append(res, condition) - } - } - x.Conditions = res -} - func (x *ConditionRule) IsMatchMethod() (string, bool) { conditions := strings.Split(x.From.Match, "&") for _, condition := range conditions { diff --git a/api/mesh/v1alpha1/instance.pb.go b/api/mesh/v1alpha1/instance.pb.go index d30f9d7a8..25bcfc4d0 100644 --- a/api/mesh/v1alpha1/instance.pb.go +++ b/api/mesh/v1alpha1/instance.pb.go @@ -47,8 +47,9 @@ type Instance struct { WorkloadType string `protobuf:"bytes,56,opt,name=workloadType,proto3" json:"workloadType,omitempty"` WorkloadName string `protobuf:"bytes,57,opt,name=workloadName,proto3" json:"workloadName,omitempty"` Node string `protobuf:"bytes,58,opt,name=node,proto3" json:"node,omitempty"` - Probes []*Probe `protobuf:"bytes,59,rep,name=probes,proto3" json:"probes,omitempty"` - Conditions []*Condition `protobuf:"bytes,60,rep,name=conditions,proto3" json:"conditions,omitempty"` + SourceEngine string `protobuf:"bytes,59,opt,name=sourceEngine,proto3" json:"sourceEngine,omitempty"` + Probes []*Probe `protobuf:"bytes,99,rep,name=probes,proto3" json:"probes,omitempty"` + Conditions []*Condition `protobuf:"bytes,100,rep,name=conditions,proto3" json:"conditions,omitempty"` } func (x *Instance) Reset() { @@ -221,6 +222,13 @@ func (x *Instance) GetNode() string { return "" } +func (x *Instance) GetSourceEngine() string { + if x != nil { + return x.SourceEngine + } + return "" +} + func (x *Instance) GetProbes() []*Probe { if x != nil { return x.Probes @@ -245,7 +253,7 @@ var file_api_mesh_v1alpha1_instance_proto_rawDesc = []byte{ 0x68, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x28, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xef, 0x06, 0x0a, 0x08, 0x49, 0x6e, + 0x6e, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x93, 0x07, 0x0a, 0x08, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x70, 0x70, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x70, 0x70, @@ -286,25 +294,27 @@ var file_api_mesh_v1alpha1_instance_proto_rawDesc = []byte{ 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x39, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x6f, 0x64, - 0x65, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x32, 0x0a, - 0x06, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x73, 0x18, 0x3b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x06, 0x70, 0x72, 0x6f, 0x62, 0x65, - 0x73, 0x12, 0x3e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x3c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, - 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x64, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x1a, 0x37, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x23, 0xaa, 0x8c, 0x89, 0xa6, - 0x01, 0x1d, 0x0a, 0x08, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x09, 0x49, 0x6e, - 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x1a, 0x04, 0x6d, 0x65, 0x73, 0x68, 0x20, 0x01, 0x4a, - 0x04, 0x08, 0x0c, 0x10, 0x32, 0x4a, 0x04, 0x08, 0x3d, 0x10, 0x65, 0x42, 0x31, 0x5a, 0x2f, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, - 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2f, 0x61, 0x70, 0x69, - 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x18, 0x3a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x0a, + 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x18, 0x3b, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x6e, 0x67, 0x69, 0x6e, + 0x65, 0x12, 0x32, 0x0a, 0x06, 0x70, 0x72, 0x6f, 0x62, 0x65, 0x73, 0x18, 0x63, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x62, 0x65, 0x52, 0x06, 0x70, + 0x72, 0x6f, 0x62, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x64, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x64, 0x75, 0x62, 0x62, + 0x6f, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x37, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x23, + 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x1d, 0x0a, 0x08, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x12, 0x09, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x1a, 0x04, 0x6d, 0x65, 0x73, + 0x68, 0x20, 0x01, 0x4a, 0x04, 0x08, 0x0c, 0x10, 0x32, 0x4a, 0x04, 0x08, 0x3c, 0x10, 0x63, 0x42, + 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x70, + 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x61, 0x64, 0x6d, 0x69, 0x6e, + 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/api/mesh/v1alpha1/instance.proto b/api/mesh/v1alpha1/instance.proto index 3e10951b8..f9bf9375f 100644 --- a/api/mesh/v1alpha1/instance.proto +++ b/api/mesh/v1alpha1/instance.proto @@ -63,9 +63,11 @@ message Instance { string node = 58; - repeated Probe probes = 59; + string sourceEngine = 59; - repeated Condition conditions = 60; + repeated Probe probes = 99; - reserved 61 to 100; + repeated Condition conditions = 100; + + reserved 60 to 98; } \ No newline at end of file diff --git a/app/dubbo-admin/dubbo-admin.yaml b/app/dubbo-admin/dubbo-admin.yaml index bc1f15a21..b33dfee92 100644 --- a/app/dubbo-admin/dubbo-admin.yaml +++ b/app/dubbo-admin/dubbo-admin.yaml @@ -15,23 +15,22 @@ mode: zone console: - observability: - grafana: xx - prometheus: xx - jaeger: xx - dashboards: - metric: - application: xx - instance: xx - service: xx - trace: - application: xx - instance: xx - service: xx - log: - application: xx - instance: xx - service: xx + grafana: + prometheus: + jaeger: + dashboards: + metric: + application: + instance: + service: + trace: + application: + instance: + service: + log: + application: + instance: + service: auth: user: admin password: dubbo@2025 @@ -39,41 +38,43 @@ console: store: # type: memory type: mysql - address: root:123456@tcp(127.0.0.1:3306)/dubbo-admin?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai + address: root:123456@tcp(127.0.0.1:23306)/dubbo-admin?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai discovery: -# - type: nacos2 -# name: localhost-nacos -# id: localhost-nacos -# address: -# registry: nacos://127.0.0.1:28848?username=nacos&password=nacos -# configCenter: nacos://127.0.0.1:28848?username=nacos&password=nacos -# metadataReport: nacos://127.0.0.1:28848?username=nacos&password=nacos -# properties: -# # [Nacos2] Adjust the interval(time unit is `s`) to adjust the frequency of list-all configs in nacos. -# # Default period is 30s -# configWatchPeriod: 20 -# # [Nacos2] Adjust the interval(time unit is `s`) to adjust the frequency of list-all services in nacos. -# # Default period is 30s -# serviceWatchPeriod: 20 - - type: zookeeper - name: zk3.6 - id: zk3.6 + - type: nacos2 + name: localhost-nacos + id: localhost-nacos address: - registry: zookeeper://127.0.0.1:2181 - configCenter: zookeeper://127.0.0.1:2181 - metadataReport: zookeeper://127.0.0.1:2181 + registry: nacos://101.34.253.152:30848?username=nacos&password=nacos + configCenter: nacos://101.34.253.152:30848?username=nacos&password=nacos + metadataReport: nacos://101.34.253.152:30848?username=nacos&password=nacos + properties: + # [Nacos2] Adjust the interval(time unit is `s`) to adjust the frequency of list-all configs in nacos. + # Default period is 30s + configWatchPeriod: 20 + # [Nacos2] Adjust the interval(time unit is `s`) to adjust the frequency of list-all services in nacos. + # Default period is 30s + serviceWatchPeriod: 20 +# - type: zookeeper +# name: 本地zookeeper +# id: zk3.6 +# address: +# registry: zookeeper://127.0.0.1:2181 +# configCenter: zookeeper://127.0.0.1:2181 +# metadataReport: zookeeper://127.0.0.1:2181 # mock discovery is only for development # - type: mock # id: mock # name: mockRegistry engine: - name: mock - type: mock -# name: k8s1.28.6 -# type: kubernetes +# id: mock +# name: mock +# type: mock + id: default + name: k8s1.28.6 + type: kubernetes properties: - # [Kubernetes] Path to kubernetes config file, if not set, will use in cluster config +# [Kubernetes] Path to kubernetes config file, if not set, will use in cluster config kubeConfigPath: /root/.kube/config # [Kubernetes] Watch pods with specified labels, if not set, will watch all pods # podWatchSelector: org.apache.dubbo/dubbo-apps=true @@ -126,4 +127,3 @@ engine: # mainContainerChooseStrategy: # type: ByIndex # index: 0 -controlPlane: \ No newline at end of file diff --git a/pkg/common/bizerror/error.go b/pkg/common/bizerror/error.go index 67764b3bb..8841fd75f 100644 --- a/pkg/common/bizerror/error.go +++ b/pkg/common/bizerror/error.go @@ -29,6 +29,7 @@ type Error interface { type ErrorCode string const ( + InternalError ErrorCode = "InternalError" UnknownError ErrorCode = "UnknownError" InvalidArgument ErrorCode = "InvalidArgument" StoreError ErrorCode = "StoreError" @@ -40,6 +41,10 @@ const ( NacosError ErrorCode = "NacosError" ZKError ErrorCode = "ZKError" EventError ErrorCode = "EventError" + GovernorError ErrorCode = "GovernorError" + JsonError ErrorCode = "JsonError" + YamlError ErrorCode = "YamlError" + NotFoundError ErrorCode = "NotFoundError" ) type bizError struct { diff --git a/pkg/common/constants/constants.go b/pkg/common/constants/constants.go index c965350c7..a7273c021 100644 --- a/pkg/common/constants/constants.go +++ b/pkg/common/constants/constants.go @@ -61,10 +61,8 @@ const ( SerializationKey = "serialization" PreferSerializationKey = "prefer.serialization" TimeoutKey = "timeout" - DefaultTimeout = 1000 WeightKey = "weight" BalancingKey = "balancing" - DefaultWeight = 100 OwnerKey = "owner" ConfigFileEnvKey = "conf" // config file path @@ -85,6 +83,7 @@ const ( ) const ( + ProtocolKey = "protocol" DubboVersionKey = "dubbo" WorkLoadKey = "workLoad" ReleaseKey = "release" diff --git a/pkg/common/constants/rule.go b/pkg/common/constants/rule.go index b0ac50e30..1749e33ee 100644 --- a/pkg/common/constants/rule.go +++ b/pkg/common/constants/rule.go @@ -44,3 +44,9 @@ const ( NotEqual = "!=" Equal = "=" ) + +const ( + ServiceDefaultTimeout int32 = 1000 + DefaultWeight int32 = 100 + ServiceDefaultRetries int32 = 2 +) diff --git a/pkg/common/util/discovery/discovery.go b/pkg/common/util/discovery/discovery.go new file mode 100644 index 000000000..2dd9ea8e9 --- /dev/null +++ b/pkg/common/util/discovery/discovery.go @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 discovery + +import "github.com/apache/dubbo-admin/pkg/config/app" + +func GetOrDefaultRegistryName(cfg app.AdminConfig, mesh string) string { + if discovery := cfg.FindDiscovery(mesh); discovery != nil { + return discovery.Name + } else { + return "default" + } +} diff --git a/pkg/config/app/admin.go b/pkg/config/app/admin.go index 531987fef..eb232af6f 100644 --- a/pkg/config/app/admin.go +++ b/pkg/config/app/admin.go @@ -50,7 +50,17 @@ type AdminConfig struct { var _ = &AdminConfig{} -func (c *AdminConfig) Sanitize() { +var DefaultAdminConfig = func() AdminConfig { + return AdminConfig{ + Mode: mode.Zone, + Store: store.DefaultStoreConfig(), + Engine: engine.DefaultResourceEngineConfig(), + Diagnostics: diagnostics.DefaultDiagnosticsConfig(), + Console: console.DefaultConsoleConfig(), + } +} + +func (c AdminConfig) Sanitize() { c.Engine.Sanitize() for _, d := range c.Discovery { d.Sanitize() @@ -60,7 +70,16 @@ func (c *AdminConfig) Sanitize() { c.Diagnostics.Sanitize() } -func (c *AdminConfig) PostProcess() error { +func (c AdminConfig) PreProcess() error { + return multierr.Combine( + c.Engine.PreProcess(), + c.Store.PreProcess(), + c.Console.PreProcess(), + c.Diagnostics.PreProcess(), + ) +} + +func (c AdminConfig) PostProcess() error { discoveryPostProcess := func() error { for _, d := range c.Discovery { if err := d.PostProcess(); err != nil { @@ -78,17 +97,7 @@ func (c *AdminConfig) PostProcess() error { ) } -var DefaultAdminConfig = func() AdminConfig { - return AdminConfig{ - Mode: mode.Zone, - Store: store.DefaultStoreConfig(), - Engine: engine.DefaultResourceEngineConfig(), - Diagnostics: diagnostics.DefaultDiagnosticsConfig(), - Console: console.DefaultConsoleConfig(), - } -} - -func (c *AdminConfig) Validate() error { +func (c AdminConfig) Validate() error { if err := mode.ValidateMode(c.Mode); err != nil { return errors.Wrap(err, "Mode Config validation failed") } @@ -128,3 +137,19 @@ func (c *AdminConfig) Validate() error { } return nil } + +// FindDiscovery finds the DiscoveryConfig by id, returns nil if not found +func (c AdminConfig) FindDiscovery(id string) *discovery.Config { + for _, d := range c.Discovery { + if d.ID == id { + return d + } + } + return nil +} + +func (c AdminConfig) Meshes() []string { + return slice.Map(c.Discovery, func(index int, item *discovery.Config) string { + return item.ID + }) +} diff --git a/pkg/config/engine/config.go b/pkg/config/engine/config.go index 394272999..0adc0d520 100644 --- a/pkg/config/engine/config.go +++ b/pkg/config/engine/config.go @@ -17,7 +17,13 @@ package engine -import "github.com/apache/dubbo-admin/pkg/config" +import ( + set "github.com/duke-git/lancet/v2/datastructure/set" + "github.com/duke-git/lancet/v2/strutil" + + "github.com/apache/dubbo-admin/pkg/common/bizerror" + "github.com/apache/dubbo-admin/pkg/config" +) type Type string @@ -29,11 +35,19 @@ const ( type Config struct { config.BaseConfig + ID string `json:"id"` Name string `json:"name"` Type Type `json:"type"` Properties Properties `json:"properties"` } +func (c *Config) Validate() error { + if strutil.IsBlank(c.ID) { + return bizerror.New(bizerror.ConfigError, "engine id can not be empty") + } + return nil +} + type Properties struct { KubeConfigPath string `json:"kubeConfigPath"` PodWatchSelector string `json:"podWatchSelector"` @@ -44,7 +58,8 @@ type Properties struct { } func (p *Properties) GetOrDefaultMainContainerChooseStrategy() *MainContainerChooseStrategy { - if p.MainContainerChooseStrategy == nil { + if p.MainContainerChooseStrategy == nil || + !mainContainerChooseStrategyTypes.Contain(p.MainContainerChooseStrategy.Type) { return &MainContainerChooseStrategy{ Type: ChooseByIndex, Index: 0, @@ -62,6 +77,8 @@ const ( ChooseByAnnotation MainContainerChooseStrategyType = "ByAnnotation" ) +var mainContainerChooseStrategyTypes = set.New(ChooseByLast, ChooseByIndex, ChooseByName, ChooseByAnnotation) + type MainContainerChooseStrategy struct { Type MainContainerChooseStrategyType `json:"type"` Index int `json:"index"` diff --git a/pkg/console/counter/manager.go b/pkg/console/counter/manager.go index f9fb7ea3d..b8d0d549c 100644 --- a/pkg/console/counter/manager.go +++ b/pkg/console/counter/manager.go @@ -70,7 +70,7 @@ func newCounterManager() *counterManager { } cm.RegisterSimpleCounter(meshresource.ApplicationKind) - cm.RegisterSimpleCounter(meshresource.ServiceKind) + cm.RegisterSimpleCounter(meshresource.ServiceProviderMetadataKind) cm.RegisterSimpleCounter(meshresource.InstanceKind) cm.RegisterDistributionCounter(meshresource.InstanceKind, ProtocolCounter, instanceProtocolKey) diff --git a/pkg/console/handler/condition_rule.go b/pkg/console/handler/condition_rule.go index c5554e6cb..653c12e71 100644 --- a/pkg/console/handler/condition_rule.go +++ b/pkg/console/handler/condition_rule.go @@ -18,20 +18,18 @@ package handler import ( - "encoding/json" "fmt" - "io" "net/http" "strings" "github.com/gin-gonic/gin" - "github.com/mitchellh/mapstructure" - meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1" + "github.com/apache/dubbo-admin/pkg/common/bizerror" "github.com/apache/dubbo-admin/pkg/common/constants" consolectx "github.com/apache/dubbo-admin/pkg/console/context" "github.com/apache/dubbo-admin/pkg/console/model" "github.com/apache/dubbo-admin/pkg/console/service" + "github.com/apache/dubbo-admin/pkg/console/util" meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" ) @@ -44,7 +42,7 @@ func ConditionRuleSearch(cs consolectx.Context) gin.HandlerFunc { } resp, err := service.SearchConditionRules(cs, req) if err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) return } c.JSON(http.StatusOK, model.NewSuccessResp(resp)) @@ -53,88 +51,52 @@ func ConditionRuleSearch(cs consolectx.Context) gin.HandlerFunc { func GetConditionRuleWithRuleName(cs consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - var name string ruleName := c.Param("ruleName") - mesh := c.Param("mesh") - if strings.HasSuffix(ruleName, constants.ConditionRuleDotSuffix) { - name = ruleName[:len(ruleName)-len(constants.ConditionRuleDotSuffix)] - } else { + mesh := c.Query("mesh") + if !strings.HasSuffix(ruleName, constants.ConditionRuleDotSuffix) { c.JSON(http.StatusBadRequest, model.NewErrorResp(fmt.Sprintf("ruleName must end with %s", constants.ConditionRuleDotSuffix))) return } - if res, err := service.GetConditionRule(cs, name, mesh); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + res, err := service.GetConditionRule(cs, ruleName, mesh) + if err != nil { + util.HandleServiceError(c, err) return - } else { - if v3x1 := res.Spec.ToConditionRouteV3x1(); v3x1 != nil { - res.Spec = v3x1.ToConditionRoute() - } - c.JSON(http.StatusOK, model.GenConditionRuleToResp(res.Spec)) } + if res == nil { + c.JSON(http.StatusOK, model.NewBizErrorResp( + bizerror.New(bizerror.NotFoundError, fmt.Sprintf("%s not found", ruleName)))) + return + } + c.JSON(http.StatusOK, model.GenConditionRuleToResp(res.Spec)) } } -func bodyToMap(reader io.ReadCloser) (map[string]interface{}, error) { - defer reader.Close() - res := map[string]interface{}{} - err := json.NewDecoder(reader).Decode(&res) - return res, err -} - -func mapToStructure(m map[string]interface{}, s interface{}) error { - decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - Result: s, - TagName: "json", - }) - if err != nil { - return err - } - err = decoder.Decode(m) - return err -} - func PutConditionRuleWithRuleName(cs consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - var name string ruleName := c.Param("ruleName") - mesh := c.Param("mesh") - if strings.HasSuffix(ruleName, constants.ConditionRuleDotSuffix) { - name = ruleName[:len(ruleName)-len(constants.ConditionRuleDotSuffix)] - } else { - c.JSON(http.StatusBadRequest, model.NewErrorResp(fmt.Sprintf("ruleName must end with %s", constants.ConditionRuleDotSuffix))) + mesh := c.Query("mesh") + if !strings.HasSuffix(ruleName, constants.ConditionRuleDotSuffix) { + c.JSON(http.StatusBadRequest, model.NewBizErrorResp(bizerror.New(bizerror.InvalidArgument, + fmt.Sprintf("ruleName must end with %s", constants.ConditionRuleDotSuffix)))) return } - _map, err := bodyToMap(c.Request.Body) + conditionRuleRes, err := service.GetConditionRule(cs, ruleName, mesh) if err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) + return + } + if conditionRuleRes == nil { + util.HandleNotFoundError(c, ruleName) return } - res := meshresource.NewConditionRouteResourceWithAttributes(ruleName, mesh) - if version := _map[constants.ConfigVersionKey]; version == constants.ConfiguratorVersionV3 { - v3 := new(meshproto.ConditionRouteV3) - err = mapToStructure(_map, &v3) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } - res.Spec = v3.ToConditionRoute() - } else if version == constants.ConfiguratorVersionV3x1 { - v3x1 := new(meshproto.ConditionRouteV3X1) - err = mapToStructure(_map, &v3x1) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } - - res.Spec = v3x1.ToConditionRoute() - } else { - c.JSON(http.StatusBadRequest, model.NewErrorResp("invalid request body")) + if err := c.ShouldBindJSON(res.Spec); err != nil { + util.HandleArgumentError(c, err) return } - if err := service.UpdateConditionRule(cs, name, res); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + if err := service.UpdateConditionRule(cs, res); err != nil { + util.HandleServiceError(c, err) return } else { c.JSON(http.StatusOK, model.GenConditionRuleToResp(res.Spec)) @@ -144,46 +106,21 @@ func PutConditionRuleWithRuleName(cs consolectx.Context) gin.HandlerFunc { func PostConditionRuleWithRuleName(cs consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - var name string ruleName := c.Param("ruleName") - mesh := c.Param("mesh") - if strings.HasSuffix(ruleName, constants.ConditionRuleDotSuffix) { - name = ruleName[:len(ruleName)-len(constants.ConditionRuleDotSuffix)] - } else { - c.JSON(http.StatusBadRequest, model.NewErrorResp(fmt.Sprintf("ruleName must end with %s", constants.ConditionRuleDotSuffix))) + mesh := c.Query("mesh") + if !strings.HasSuffix(ruleName, constants.ConditionRuleDotSuffix) { + c.JSON(http.StatusBadRequest, model.NewBizErrorResp(bizerror.New(bizerror.InvalidArgument, + fmt.Sprintf("ruleName must end with %s", constants.ConditionRuleDotSuffix)))) return } - _map, err := bodyToMap(c.Request.Body) - if err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) - return - } - - res := &meshresource.ConditionRouteResource{} - res = meshresource.NewConditionRouteResourceWithAttributes(ruleName, mesh) - if version := _map[constants.ConfigVersionKey]; version == constants.ConfiguratorVersionV3 { - v3 := new(meshproto.ConditionRouteV3) - err = mapToStructure(_map, &v3) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } - res.Spec = v3.ToConditionRoute() - } else if version == constants.ConfiguratorVersionV3x1 { - v3x1 := new(meshproto.ConditionRouteV3X1) - err = mapToStructure(_map, &v3x1) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } - res.Spec = v3x1.ToConditionRoute() - } else { - c.JSON(http.StatusBadRequest, model.NewErrorResp("invalid request body")) + res := meshresource.NewConditionRouteResourceWithAttributes(ruleName, mesh) + if err := c.ShouldBindJSON(res.Spec); err != nil { + util.HandleArgumentError(c, err) return } - if err := service.CreateConditionRule(cs, name, res); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + if err := service.CreateConditionRule(cs, res); err != nil { + c.JSON(http.StatusOK, model.NewErrorResp(err.Error())) return } else { c.JSON(http.StatusOK, model.GenConditionRuleToResp(res.Spec)) @@ -193,17 +130,15 @@ func PostConditionRuleWithRuleName(cs consolectx.Context) gin.HandlerFunc { func DeleteConditionRuleWithRuleName(cs consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - var name string ruleName := c.Param("ruleName") - mesh := c.Param("mesh") - if strings.HasSuffix(ruleName, constants.ConditionRuleDotSuffix) { - name = ruleName[:len(ruleName)-len(constants.ConditionRuleDotSuffix)] - } else { - c.JSON(http.StatusBadRequest, model.NewErrorResp(fmt.Sprintf("ruleName must end with %s", constants.ConditionRuleDotSuffix))) + mesh := c.Query("mesh") + if !strings.HasSuffix(ruleName, constants.ConditionRuleDotSuffix) { + c.JSON(http.StatusBadRequest, model.NewBizErrorResp(bizerror.New(bizerror.InvalidArgument, + fmt.Sprintf("ruleName must end with %s", constants.ConditionRuleDotSuffix)))) return } - if err := service.DeleteConditionRule(cs, name, mesh); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + if err := service.DeleteConditionRule(cs, ruleName, mesh); err != nil { + c.JSON(http.StatusOK, model.NewErrorResp(err.Error())) return } c.JSON(http.StatusOK, model.NewSuccessResp("")) diff --git a/pkg/console/handler/configurator_rule.go b/pkg/console/handler/configurator_rule.go index 6965bc356..0b806715b 100644 --- a/pkg/console/handler/configurator_rule.go +++ b/pkg/console/handler/configurator_rule.go @@ -25,70 +25,41 @@ import ( "github.com/duke-git/lancet/v2/strutil" "github.com/gin-gonic/gin" + "github.com/apache/dubbo-admin/pkg/common/bizerror" "github.com/apache/dubbo-admin/pkg/common/constants" consolectx "github.com/apache/dubbo-admin/pkg/console/context" "github.com/apache/dubbo-admin/pkg/console/model" "github.com/apache/dubbo-admin/pkg/console/service" - "github.com/apache/dubbo-admin/pkg/core/manager" + "github.com/apache/dubbo-admin/pkg/console/util" meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" - coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" - "github.com/apache/dubbo-admin/pkg/core/store/index" ) func ConfiguratorSearch(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - req := model.NewSearchConfiguratorReq() + req := model.NewSearchReq() if err := c.ShouldBindQuery(req); err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } - var pageData *coremodel.PageData[*meshresource.DynamicConfigResource] + var searchResult *model.SearchPaginationResult var err error if strutil.IsBlank(req.Keywords) { - pageData, err = manager.PageListByIndexes[*meshresource.DynamicConfigResource]( - ctx.ResourceManager(), - meshresource.DynamicConfigKind, - map[string]string{ - index.ByMeshIndex: req.Mesh, - }, - req.PageReq, - ) - + searchResult, err = service.PageListConfiguratorRule(ctx, req) } else { - pageData, err = manager.PageSearchResourceByConditions[*meshresource.DynamicConfigResource]( - ctx.ResourceManager(), - meshresource.DynamicConfigKind, - []string{"name=" + req.Keywords}, - req.PageReq, - ) - + searchResult, err = service.SearchConfiguratorRuleByKeywords(ctx, req) } if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) return } - var respList []model.ConfiguratorSearchResp - for _, res := range pageData.Data { - configurator := res.Spec - respList = append(respList, model.ConfiguratorSearchResp{ - RuleName: configurator.Key, - Scope: configurator.Scope, - CreateTime: "", - Enabled: configurator.Enabled, - }) - } - result := model.SearchPaginationResult{ - List: respList, - PageInfo: pageData.Pagination, - } - c.JSON(http.StatusOK, model.NewSuccessResp(result)) + c.JSON(http.StatusOK, model.NewSuccessResp(searchResult)) } } func GetConfiguratorWithRuleName(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { ruleName := c.Param("ruleName") - mesh := c.Param("mesh") + mesh := c.Query("mesh") if strutil.IsBlank(ruleName) { c.JSON(http.StatusBadRequest, model.NewErrorResp("ruleName cannot be empty")) return @@ -99,78 +70,84 @@ func GetConfiguratorWithRuleName(ctx consolectx.Context) gin.HandlerFunc { } res, err := service.GetConfigurator(ctx, ruleName, mesh) if err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) return } + if res == nil { + c.JSON(http.StatusOK, model.NewBizErrorResp( + bizerror.New(bizerror.NotFoundError, fmt.Sprintf("%s not found", ruleName)))) + } c.JSON(http.StatusOK, model.GenDynamicConfigToResp(res.Spec)) } } func PutConfiguratorWithRuleName(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - var name string ruleName := c.Param("ruleName") - mesh := c.Param("mesh") - if strings.HasSuffix(ruleName, constants.ConfiguratorRuleDotSuffix) { - name = ruleName[:len(ruleName)-len(constants.ConfiguratorRuleDotSuffix)] - } else { - c.JSON(http.StatusBadRequest, model.NewErrorResp(fmt.Sprintf("ruleName must end with %s", constants.ConfiguratorRuleDotSuffix))) + mesh := c.Query("mesh") + if !strings.HasSuffix(ruleName, constants.ConfiguratorRuleDotSuffix) { + c.JSON(http.StatusOK, model.NewBizErrorResp(bizerror.New(bizerror.InvalidArgument, + fmt.Sprintf("dynamic config name must end with %s", constants.ConfiguratorRuleDotSuffix)))) return } - res := meshresource.NewDynamicConfigResourceWithAttributes(name, mesh) + res := meshresource.NewDynamicConfigResourceWithAttributes(ruleName, mesh) err := c.Bind(res.Spec) if err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + util.HandleArgumentError(c, err) return } - if err = service.UpdateConfigurator(ctx, name, res); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + configurator, err := service.GetConfigurator(ctx, ruleName, mesh) + if err != nil { + util.HandleServiceError(c, err) return - } else { - c.JSON(http.StatusOK, model.GenDynamicConfigToResp(res.Spec)) } + if configurator == nil { + c.JSON(http.StatusOK, model.NewBizErrorResp( + bizerror.New(bizerror.NotFoundError, fmt.Sprintf("%s not found", ruleName)))) + } + if err = service.UpdateConfigurator(ctx, res); err != nil { + c.JSON(http.StatusOK, model.NewErrorResp(err.Error())) + return + } + c.JSON(http.StatusOK, model.GenDynamicConfigToResp(res.Spec)) } } func PostConfiguratorWithRuleName(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - var name string ruleName := c.Param("ruleName") - mesh := c.Param("mesh") - if strings.HasSuffix(ruleName, constants.ConfiguratorRuleDotSuffix) { - name = ruleName[:len(ruleName)-len(constants.ConfiguratorRuleDotSuffix)] - } else { - c.JSON(http.StatusBadRequest, model.NewErrorResp(fmt.Sprintf("ruleName must end with %s", constants.ConfiguratorRuleDotSuffix))) + mesh := c.Query("mesh") + if !strings.HasSuffix(ruleName, constants.ConfiguratorRuleDotSuffix) { + c.JSON(http.StatusBadRequest, model.NewBizErrorResp(bizerror.New(bizerror.InvalidArgument, + fmt.Sprintf("dynamic config name must end with %s", constants.ConfiguratorRuleDotSuffix)))) return } - res := meshresource.NewDynamicConfigResourceWithAttributes(name, mesh) + res := meshresource.NewDynamicConfigResourceWithAttributes(ruleName, mesh) err := c.Bind(res.Spec) if err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + util.HandleArgumentError(c, err) return } - if err = service.CreateConfigurator(ctx, name, res); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + if err = service.CreateConfigurator(ctx, res); err != nil { + c.JSON(http.StatusOK, model.NewErrorResp(err.Error())) return - } else { - c.JSON(http.StatusOK, model.GenDynamicConfigToResp(res.Spec)) } + c.JSON(http.StatusOK, model.GenDynamicConfigToResp(res.Spec)) + } } func DeleteConfiguratorWithRuleName(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - var name string ruleName := c.Param("ruleName") - mesh := c.Param("mesh") - if strings.HasSuffix(ruleName, constants.ConfiguratorRuleDotSuffix) { - name = ruleName[:len(ruleName)-len(constants.ConfiguratorRuleDotSuffix)] - } else { - c.JSON(http.StatusBadRequest, model.NewErrorResp(fmt.Sprintf("ruleName must end with %s", constants.ConfiguratorRuleDotSuffix))) + mesh := c.Query("mesh") + if !strings.HasSuffix(ruleName, constants.ConfiguratorRuleDotSuffix) { + c.JSON(http.StatusOK, model.NewBizErrorResp(bizerror.New(bizerror.InvalidArgument, + fmt.Sprintf("dynamic config name must end with %s", constants.ConfiguratorRuleDotSuffix)))) return } - if err := service.DeleteConfigurator(ctx, name, mesh); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + if err := service.DeleteConfigurator(ctx, ruleName, mesh); err != nil { + c.JSON(http.StatusOK, model.NewErrorResp(err.Error())) return } c.JSON(http.StatusOK, model.NewSuccessResp("")) diff --git a/pkg/console/handler/instance.go b/pkg/console/handler/instance.go index 2e3a543af..48d4966e4 100644 --- a/pkg/console/handler/instance.go +++ b/pkg/console/handler/instance.go @@ -24,15 +24,12 @@ import ( "github.com/duke-git/lancet/v2/strutil" "github.com/gin-gonic/gin" - "github.com/pkg/errors" - meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1" - "github.com/apache/dubbo-admin/pkg/common/constants" + "github.com/apache/dubbo-admin/pkg/common/bizerror" consolectx "github.com/apache/dubbo-admin/pkg/console/context" "github.com/apache/dubbo-admin/pkg/console/model" "github.com/apache/dubbo-admin/pkg/console/service" - meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" - corestore "github.com/apache/dubbo-admin/pkg/core/store" + "github.com/apache/dubbo-admin/pkg/console/util" ) func GetInstanceDetail(ctx consolectx.Context) gin.HandlerFunc { @@ -45,12 +42,12 @@ func GetInstanceDetail(ctx consolectx.Context) gin.HandlerFunc { resp, err := service.GetInstanceDetail(ctx, req) if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) return } if resp == nil { - c.JSON(http.StatusNotFound, model.NewErrorResp("instance not exist")) + util.HandleNotFoundError(c, req.InstanceName) return } c.JSON(http.StatusOK, model.NewSuccessResp(resp)) @@ -61,13 +58,14 @@ func SearchInstances(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { req := model.NewSearchInstanceReq() if err := c.ShouldBindQuery(req); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + c.JSON(http.StatusBadRequest, model.NewBizErrorResp( + bizerror.New(bizerror.InvalidArgument, "appName is empty"))) return } instances, err := service.SearchInstances(ctx, req) if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) return } @@ -82,207 +80,54 @@ func InstanceConfigTrafficDisableGET(ctx consolectx.Context) gin.HandlerFunc { }{false} applicationName := c.Query("appName") mesh := c.Query("mesh") - if applicationName == "" { - c.JSON(http.StatusBadRequest, model.NewErrorResp("application name is empty")) + if strutil.IsBlank(applicationName) { + c.JSON(http.StatusBadRequest, model.NewBizErrorResp( + bizerror.New(bizerror.InvalidArgument, "appName is empty"))) return } instanceIP := strings.TrimSpace(c.Query("instanceIP")) - if instanceIP == "" { - c.JSON(http.StatusBadRequest, model.NewErrorResp("instanceIP is empty")) + if strutil.IsBlank(instanceIP) { + c.JSON(http.StatusBadRequest, model.NewBizErrorResp( + bizerror.New(bizerror.InvalidArgument, "instanceIP is empty"))) return } - - res, err := service.GetConditionRule(ctx, applicationName, mesh) + trafficStatus, err := service.GetInstanceTrafficStatus(ctx, mesh, applicationName, instanceIP) if err != nil { - if corestore.IsResourceNotFound(err) { - c.JSON(http.StatusOK, model.NewSuccessResp(resp)) - return - } - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) return } - - if res.Spec.GetVersion() != constants.ConfiguratorVersionV3 { - c.JSON(http.StatusServiceUnavailable, model.NewErrorResp("this config only serve condition-route.configVersion == v3, got v3.1 config ")) - return - } - - cr := res.Spec.ToConditionRouteV3() - cr.RangeConditions(func(condition string) (isStop bool) { - _, resp.TrafficDisable = isTrafficDisabledV3(condition, instanceIP) - return resp.TrafficDisable - }) - + resp.TrafficDisable = trafficStatus c.JSON(http.StatusOK, model.NewSuccessResp(resp)) } } -func isTrafficDisabledV3X1(r *meshproto.ConditionRule, targetIP string) bool { - if len(r.To) != 0 { - return false - } - // rule must match `host=x1{,x2,x3}` - if r.From.Match != "" && !strings.Contains(r.From.Match, "&") && strings.Index(r.From.Match, "!=") == -1 { - idx := strings.Index(r.From.Match, "=") - if idx == -1 { - return false - } - then := r.From.Match[idx+1:] - Ips := strings.Split(then, ",") - for _, ip := range Ips { - if strings.TrimSpace(ip) == targetIP { - return true - } - } - } - return false -} - -/* -* -isTrafficDisabledV3 judge if a condition is disabled or not. -A condition include fromCondition and toCondition which is seperated by `=>`. -The first return parameter `exist` indicates if a condition of specific targetIP exists. -The second return parameter `disabled` indicates if the traffic of targetIP is disabled. -*/ -func isTrafficDisabledV3(condition string, targetIP string) (exist bool, disabled bool) { - if len(condition) == 0 { - return false, false - } - condition = strings.ReplaceAll(condition, " ", "") - // only accept string start with `=>` - if !strings.HasPrefix(condition, "=>") { - return false, false - } - toCondition := strings.TrimPrefix(condition, "=>") - // TODO more specific judge - if !strings.Contains(toCondition, targetIP) { - return false, false - } - targetExpression := "host!=" + targetIP - if targetExpression != toCondition { - return true, false - } - return true, true -} - func InstanceConfigTrafficDisablePUT(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { appName := strings.TrimSpace(c.Query("appName")) mesh := c.Query("mesh") - if appName == "" { - c.JSON(http.StatusBadRequest, model.NewErrorResp("application name is empty")) + if strutil.IsBlank(appName) { + c.JSON(http.StatusBadRequest, model.NewBizErrorResp( + bizerror.New(bizerror.InvalidArgument, "appName is empty"))) return } instanceIP := strings.TrimSpace(c.Query("instanceIP")) - if instanceIP == "" { - c.JSON(http.StatusBadRequest, model.NewErrorResp("instanceIP is empty")) + if strutil.IsBlank(instanceIP) { + c.JSON(http.StatusBadRequest, model.NewBizErrorResp( + bizerror.New(bizerror.InvalidArgument, "instanceIP is empty"))) return } - newDisabled, err := strconv.ParseBool(c.Query(`trafficDisable`)) + disableTraffic, err := strconv.ParseBool(c.Query(`trafficDisable`)) if err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(errors.Wrap(err, "parse trafficDisable fail").Error())) + c.JSON(http.StatusBadRequest, model.NewBizErrorResp( + bizerror.Wrap(err, bizerror.InvalidArgument, "parse trafficDisable failed"))) return } - - existRule := true - rawRes, err := service.GetConditionRule(ctx, appName, mesh) - var res *meshproto.ConditionRouteV3 + err = service.UpdateInstanceTrafficStatus(ctx, mesh, appName, instanceIP, disableTraffic) if err != nil { - if !corestore.IsResourceNotFound(err) { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } else if !newDisabled { // not found && cancel traffic-disable - c.JSON(http.StatusOK, model.NewSuccessResp(nil)) - return - } - existRule = false - res = generateDefaultConditionV3(true, true, true, appName, constants.ScopeApplication) - rawRes = meshresource.NewConditionRouteResourceWithAttributes(appName, mesh) - rawRes.Spec = res.ToConditionRoute() - } else if res = rawRes.Spec.ToConditionRouteV3(); res == nil { - c.JSON(http.StatusServiceUnavailable, model.NewErrorResp("this config only serve condition-route.configVersion == v3.1, got v3.0 config ")) + util.HandleServiceError(c, err) return } - - // enable traffic - if !newDisabled { - for i, condition := range res.Conditions { - existCondition, oldDisabled := isTrafficDisabledV3(condition, instanceIP) - if existCondition { - if oldDisabled != newDisabled { - res.Conditions = append(res.Conditions[:i], res.Conditions[i+1:]...) - rawRes.Spec = res.ToConditionRoute() - if err = updateORCreateConditionRule(ctx, existRule, appName, rawRes); err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - } - c.JSON(http.StatusOK, model.NewSuccessResp(nil)) - return - } - } - } - } else { // disable traffic - // check if condition exists - for _, condition := range res.Conditions { - existCondition, oldDisabled := isTrafficDisabledV3(condition, instanceIP) - if existCondition && oldDisabled { - c.JSON(http.StatusBadRequest, model.NewErrorResp("The instance has been disabled!")) - return - } - } - res.Conditions = append(res.Conditions, disableExpression(instanceIP)) - if err = updateORCreateConditionRule(ctx, existRule, appName, rawRes); err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - } - c.JSON(http.StatusOK, model.NewSuccessResp(nil)) - } - } -} - -func disableExpression(instanceIP string) string { - return "=>host!=" + instanceIP -} -func updateORCreateConditionRule(ctx consolectx.Context, existRule bool, appName string, rawRes *meshresource.ConditionRouteResource) error { - if !existRule { - return service.CreateConditionRule(ctx, appName, rawRes) - } else { - return service.UpdateConditionRule(ctx, appName, rawRes) - } -} - -func newDisableConditionV3x1(ip string) *meshproto.ConditionRule { - return &meshproto.ConditionRule{ - From: &meshproto.ConditionRuleFrom{Match: "host=" + ip}, - To: nil, - } -} - -func newDisableConditionV3(ip string) string { - return "=>host!=" + ip -} - -func generateDefaultConditionV3x1(Enabled, Force, Runtime bool, Key, Scope string) *meshproto.ConditionRouteV3X1 { - return &meshproto.ConditionRouteV3X1{ - ConfigVersion: constants.ConfiguratorVersionV3x1, - Enabled: Enabled, - Force: Force, - Runtime: Runtime, - Key: Key, - Scope: Scope, - Conditions: make([]*meshproto.ConditionRule, 0), - } -} - -func generateDefaultConditionV3(Enabled, Force, Runtime bool, Key, Scope string) *meshproto.ConditionRouteV3 { - return &meshproto.ConditionRouteV3{ - ConfigVersion: constants.ConfiguratorVersionV3, - Priority: 0, - Enabled: true, - Force: Force, - Runtime: Runtime, - Key: Key, - Scope: Scope, - Conditions: make([]string, 0), + c.JSON(http.StatusOK, model.NewSuccessResp(nil)) } } @@ -306,41 +151,16 @@ func InstanceConfigOperatorLogGET(ctx consolectx.Context) gin.HandlerFunc { c.JSON(http.StatusBadRequest, model.NewErrorResp("mesh is empty")) return } - appConfiguratorName := applicationName + constants.ConfiguratorRuleDotSuffix - res, err := service.GetConfigurator(ctx, appConfiguratorName, mesh) + accessLogOpenStatus, err := service.GetInstanceAccessLogOpenStatus(ctx, mesh, applicationName, instanceIP) if err != nil { - if corestore.IsResourceNotFound(err) { - c.JSON(http.StatusOK, model.NewSuccessResp(resp)) - return - } - c.JSON(http.StatusNotFound, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) return } - - if res.Spec.Enabled { - res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) (isStop bool) { - resp.OperatorLog = isInstanceOperatorLogOpen(conf, instanceIP) - return resp.OperatorLog - }) - } - + resp.OperatorLog = accessLogOpenStatus c.JSON(http.StatusOK, model.NewSuccessResp(resp)) } } -func isInstanceOperatorLogOpen(conf *meshproto.OverrideConfig, IP string) bool { - if conf != nil && - conf.Match != nil && - conf.Match.Address != nil && - conf.Match.Address.Wildcard == IP+`:*` && - conf.Side == constants.SideProvider && - conf.Parameters != nil && - conf.Parameters[`accesslog`] == `true` { - return true - } - return false -} - func InstanceConfigOperatorLogPUT(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { applicationName := c.Query(`appName`) @@ -353,7 +173,7 @@ func InstanceConfigOperatorLogPUT(ctx consolectx.Context) gin.HandlerFunc { c.JSON(http.StatusBadRequest, model.NewErrorResp("instanceIP is empty")) return } - adminOperatorLog, err := strconv.ParseBool(c.Query(`operatorLog`)) + openAccessLog, err := strconv.ParseBool(c.Query(`operatorLog`)) if err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return @@ -363,59 +183,10 @@ func InstanceConfigOperatorLogPUT(ctx consolectx.Context) gin.HandlerFunc { c.JSON(http.StatusBadRequest, model.NewErrorResp("mesh is empty")) return } - appConfiguratorName := applicationName + constants.ConfiguratorRuleDotSuffix - res, err := service.GetConfigurator(ctx, appConfiguratorName, mesh) - notExist := false - if err != nil { - if !corestore.IsResourceNotFound(err) { - c.JSON(http.StatusNotFound, model.NewErrorResp(err.Error())) - return - } - res = meshresource.NewDynamicConfigResourceWithAttributes(appConfiguratorName, mesh) - res.Spec = &meshproto.DynamicConfig{ - Key: applicationName, - Scope: constants.ScopeApplication, - ConfigVersion: constants.ConfiguratorVersionV3, - Enabled: true, - Configs: make([]*meshproto.OverrideConfig, 0), - } - notExist = true - } - - if !adminOperatorLog { - res.Spec.RangeConfigsToRemove(func(conf *meshproto.OverrideConfig) (IsRemove bool) { - return isInstanceOperatorLogOpen(conf, instanceIP) - }) - } else { - var isExist bool - res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) (isStop bool) { - isExist = isInstanceOperatorLogOpen(conf, instanceIP) - return isExist - }) - if !isExist { - res.Spec.Configs = append(res.Spec.Configs, &meshproto.OverrideConfig{ - Side: constants.SideProvider, - Match: &meshproto.ConditionMatch{Address: &meshproto.AddressMatch{Wildcard: instanceIP + `:*`}}, - Parameters: map[string]string{`accesslog`: `true`}, - XGenerateByCp: true, - }) - } - } - - if notExist { - err = service.CreateConfigurator(ctx, applicationName, res) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } - } else { - err = service.UpdateConfigurator(ctx, applicationName, res) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } + if err = service.UpdateInstanceAccessLogOpenStatus(ctx, mesh, applicationName, instanceIP, openAccessLog); err != nil { + util.HandleServiceError(c, err) + return } - c.JSON(http.StatusOK, model.NewSuccessResp(nil)) } } diff --git a/pkg/console/handler/observability.go b/pkg/console/handler/observability.go index 447d56cc1..07f640195 100644 --- a/pkg/console/handler/observability.go +++ b/pkg/console/handler/observability.go @@ -26,6 +26,7 @@ import ( consolectx "github.com/apache/dubbo-admin/pkg/console/context" "github.com/apache/dubbo-admin/pkg/console/model" "github.com/apache/dubbo-admin/pkg/console/service" + "github.com/apache/dubbo-admin/pkg/console/util" ) type Dimension string @@ -105,7 +106,7 @@ func GetMetricsList(ctx consolectx.Context) gin.HandlerFunc { } resp, err := service.GetInstanceMetrics(ctx, req) if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) return } c.JSON(http.StatusOK, model.NewSuccessResp(resp)) diff --git a/pkg/console/handler/overview.go b/pkg/console/handler/overview.go index c2aac5335..85cd6c633 100644 --- a/pkg/console/handler/overview.go +++ b/pkg/console/handler/overview.go @@ -22,27 +22,48 @@ import ( "github.com/gin-gonic/gin" + "github.com/apache/dubbo-admin/pkg/common/bizerror" consolectx "github.com/apache/dubbo-admin/pkg/console/context" "github.com/apache/dubbo-admin/pkg/console/counter" "github.com/apache/dubbo-admin/pkg/console/model" + "github.com/apache/dubbo-admin/pkg/console/util" meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" ) func AdminMetadata(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - res := model.NewAdminMetadata() - // TODO - c.JSON(http.StatusOK, model.NewSuccessResp(res)) + mesh, exists := c.GetQuery("mesh") + if !exists { + util.HandleServiceError(c, bizerror.New(bizerror.InvalidArgument, "mesh is required")) + return + } + var registryAddr string + var metadataAddr string + var configAddr string + if d := ctx.Config().FindDiscovery(mesh); d != nil { + registryAddr = d.Address.Registry + metadataAddr = d.Address.MetadataReport + configAddr = d.Address.ConfigCenter + } + metadata := model.AdminMetadata{ + Registry: registryAddr, + Metadata: metadataAddr, + Config: configAddr, + Prometheus: ctx.Config().Console.Prometheus, + Grafana: ctx.Config().Console.Grafana, + Tracing: "", + } + c.JSON(http.StatusOK, model.NewSuccessResp(metadata)) } } -// ClusterOverview TODO implement +// ClusterOverview get cluster overview func ClusterOverview(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { resp := model.NewOverviewResp() if counterMgr := ctx.CounterManager(); counterMgr != nil { resp.AppCount = counterMgr.Count(meshresource.ApplicationKind) - resp.ServiceCount = counterMgr.Count(meshresource.ServiceKind) + resp.ServiceCount = counterMgr.Count(meshresource.ServiceProviderMetadataKind) resp.InsCount = counterMgr.Count(meshresource.InstanceKind) resp.Protocols = counterMgr.Distribution(counter.ProtocolCounter) resp.Releases = counterMgr.Distribution(counter.ReleaseCounter) diff --git a/pkg/console/handler/prometheus.go b/pkg/console/handler/prometheus.go index 89b33f61c..0f569ac62 100644 --- a/pkg/console/handler/prometheus.go +++ b/pkg/console/handler/prometheus.go @@ -17,16 +17,17 @@ package handler -// proxy for prometheus - import ( "net/http" "net/http/httputil" "net/url" + "github.com/duke-git/lancet/v2/strutil" "github.com/gin-gonic/gin" + "github.com/apache/dubbo-admin/pkg/common/bizerror" consolectx "github.com/apache/dubbo-admin/pkg/console/context" + "github.com/apache/dubbo-admin/pkg/console/model" ) func PromQL(ctx consolectx.Context) gin.HandlerFunc { @@ -34,7 +35,13 @@ func PromQL(ctx consolectx.Context) gin.HandlerFunc { query := c.Request.URL.Query().Get("query") values := url.Values{} values.Add("query", query) - promUrl := ctx.Config().Console.Prometheus + "/api/v1/query?" + values.Encode() + promBaseUrl := ctx.Config().Console.Prometheus + if strutil.IsBlank(promBaseUrl) { + c.JSON(http.StatusOK, model.NewBizErrorResp( + bizerror.New(bizerror.ConfigError, "Please configure prometheus url to retrieve metrics"))) + return + } + promUrl := promBaseUrl + "/api/v1/query?" + values.Encode() proxyUrl, _ := url.Parse(promUrl) director := func(req *http.Request) { req.URL.Scheme = proxyUrl.Scheme diff --git a/pkg/console/handler/search.go b/pkg/console/handler/search.go index 65eadcfc5..d888b7116 100644 --- a/pkg/console/handler/search.go +++ b/pkg/console/handler/search.go @@ -39,13 +39,13 @@ func BannerGlobalSearch(ctx consolectx.Context) gin.HandlerFunc { var res *model.SearchRes switch req.SearchType { case "ip": - instances, _ := service.BannerSearchIp(ctx, req) + instances, _ := service.SearchInstanceByIp(ctx, req) res = convertInstancesToSearchRes(instances) case "instanceName": - instances, _ := service.BannerSearchInstances(ctx, req) + instances, _ := service.SearchInstanceByName(ctx, req) res = convertInstancesToSearchRes(instances) case "appName": - applications, _ := service.BannerSearchApplications(ctx, req) + applications, _ := service.SearchApplicationsByKeywords(ctx, req) res = convertApplicationsToSearchRes(applications) case "serviceName": sreq := &model.ServiceSearchReq{ @@ -53,7 +53,7 @@ func BannerGlobalSearch(ctx consolectx.Context) gin.HandlerFunc { Keywords: req.Keywords, PageReq: req.PageReq, } - services, _ := service.BannerSearchServices(ctx, sreq) + services, _ := service.SearchServicesByKeywords(ctx, sreq) res = convertServicesToSearchRes(services) default: c.JSON(http.StatusBadRequest, model.NewErrorResp("invalid search type")) diff --git a/pkg/console/handler/service.go b/pkg/console/handler/service.go index 361354f64..5edc4a70f 100644 --- a/pkg/console/handler/service.go +++ b/pkg/console/handler/service.go @@ -19,17 +19,13 @@ package handler import ( "net/http" - "strconv" "github.com/gin-gonic/gin" - meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1" - "github.com/apache/dubbo-admin/pkg/common/constants" consolectx "github.com/apache/dubbo-admin/pkg/console/context" "github.com/apache/dubbo-admin/pkg/console/model" "github.com/apache/dubbo-admin/pkg/console/service" - meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" - corestore "github.com/apache/dubbo-admin/pkg/core/store" + "github.com/apache/dubbo-admin/pkg/console/util" ) const ( @@ -47,7 +43,7 @@ func SearchServices(ctx consolectx.Context) gin.HandlerFunc { resp, err := service.SearchServices(ctx, req) if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) return } @@ -65,7 +61,7 @@ func GetServiceTabDistribution(ctx consolectx.Context) gin.HandlerFunc { resp, err := service.GetServiceTabDistribution(ctx, req) if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) return } @@ -73,72 +69,26 @@ func GetServiceTabDistribution(ctx consolectx.Context) gin.HandlerFunc { } } -func ListServices(ctx consolectx.Context) gin.HandlerFunc { - return func(c *gin.Context) { - // req := &model.SearchInstanceReq{} - - c.JSON(http.StatusOK, model.NewSuccessResp("")) - } -} - -func GetServiceDetail(ctx consolectx.Context) gin.HandlerFunc { - return func(c *gin.Context) { - // req := &model.SearchInstanceReq{} - - c.JSON(http.StatusOK, model.NewSuccessResp("")) - } -} - -func GetServiceInterfaces(ctx consolectx.Context) gin.HandlerFunc { - return func(c *gin.Context) { - // req := &model.SearchInstanceReq{} - - c.JSON(http.StatusOK, model.NewSuccessResp("")) - } -} - func ServiceConfigTimeoutGET(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - param := model.BaseServiceReq{} + req := model.BaseServiceReq{} resp := struct { Timeout int32 `json:"timeout"` }{DefaultTimeout} - if err := param.Query(c); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + if err := req.Query(c); err != nil { + util.HandleArgumentError(c, err) return } - serviceConfiguratorName := param.ServiceKey() + constants.PunctuationPoint + constants.ConfiguratorRuleDotSuffix - res, err := service.GetConfigurator(ctx, serviceConfiguratorName, param.Mesh) + timeout, err := service.GetServiceTimeoutConfig(ctx, req) if err != nil { - if !corestore.IsResourceNotFound(err) { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) - return - } else if false { - // TODO(YarBor) : to check service exist or not - } - c.JSON(http.StatusOK, model.NewSuccessResp(resp)) + util.HandleServiceError(c, err) return } - - res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) (isStop bool) { - resp.Timeout, isStop = getServiceTimeout(conf) - return isStop - }) - + resp.Timeout = timeout c.JSON(http.StatusOK, model.NewSuccessResp(resp)) } } -func getServiceTimeout(conf *meshproto.OverrideConfig) (int32, bool) { - if conf.Side == constants.SideProvider && conf.Parameters != nil && conf.Parameters[`timeout`] != "" { - timeout, err := strconv.Atoi(conf.Parameters[`timeout`]) - if err == nil { - return int32(timeout), true - } - } - return DefaultTimeout, false -} - func ServiceConfigTimeoutPUT(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { param := struct { @@ -149,53 +99,10 @@ func ServiceConfigTimeoutPUT(ctx consolectx.Context) gin.HandlerFunc { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } - - isExist := true - serviceConfiguratorName := param.ServiceKey() + constants.PunctuationPoint + constants.ConfiguratorRuleDotSuffix - res, err := service.GetConfigurator(ctx, serviceConfiguratorName, param.Mesh) + err := service.UpInsertServiceConfigTimeoutConfig(ctx, param.BaseServiceReq, param.Timeout) if err != nil { - if !corestore.IsResourceNotFound(err) { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) - return - } else if false { - // TODO(YarBor) : to check service exist or not - } - res = meshresource.NewDynamicConfigResourceWithAttributes(serviceConfiguratorName, param.Mesh) - res.Spec = &meshproto.DynamicConfig{ - Key: param.ServiceName, - Scope: constants.ScopeService, - ConfigVersion: constants.ConfiguratorVersionV3, - Enabled: true, - Configs: make([]*meshproto.OverrideConfig, 0), - } - isExist = false - } else { - res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) (isStop bool) { - _, ok := getServiceTimeout(conf) - if ok { - conf.Parameters[`timeout`] = strconv.Itoa(int(param.Timeout)) - } - return ok - }) - } - - if !isExist { - res.Spec.Configs = append(res.Spec.Configs, &meshproto.OverrideConfig{ - Side: constants.SideProvider, - Parameters: map[string]string{`timeout`: strconv.Itoa(int(param.Timeout))}, - XGenerateByCp: true, - }) - err = service.CreateConfigurator(ctx, param.ServiceKey(), res) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } - } else { - err = service.UpdateConfigurator(ctx, param.ServiceKey(), res) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } + util.HandleServiceError(c, err) + return } c.JSON(http.StatusOK, model.NewSuccessResp(nil)) } @@ -208,41 +115,19 @@ func ServiceConfigRetryGET(ctx consolectx.Context) gin.HandlerFunc { RetryTimes int32 `json:"retryTimes"` }{DefaultRetries} if err := param.Query(c); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + util.HandleArgumentError(c, err) return } - serviceConfiguratorName := param.ServiceKey() + constants.PunctuationPoint + constants.ConfiguratorRuleDotSuffix - res, err := service.GetConfigurator(ctx, serviceConfiguratorName, param.Mesh) + retries, err := service.GetServiceRetryConfig(ctx, param) if err != nil { - if !corestore.IsResourceNotFound(err) { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) - return - } else if false { - // TODO(YarBor) : to check service exist or not - } - c.JSON(http.StatusOK, model.NewSuccessResp(resp)) + util.HandleServiceError(c, err) return } - - res.Spec.RangeConfig(func(conf *meshproto.OverrideConfig) (isStop bool) { - resp.RetryTimes, isStop = getServiceRetryTimes(conf) - return isStop - }) - + resp.RetryTimes = retries c.JSON(http.StatusOK, model.NewSuccessResp(resp)) } } -func getServiceRetryTimes(conf *meshproto.OverrideConfig) (int32, bool) { - if conf.Side == constants.SideConsumer && conf.Parameters != nil && conf.Parameters[`retries`] != "" { - retries, err := strconv.Atoi(conf.Parameters[`retries`]) - if err == nil { - return int32(retries), true - } - } - return DefaultRetries, false -} - func ServiceConfigRetryPUT(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { param := struct { @@ -250,54 +135,12 @@ func ServiceConfigRetryPUT(ctx consolectx.Context) gin.HandlerFunc { RetryTimes int32 `json:"retryTimes"` }{} if err := c.Bind(¶m); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + util.HandleArgumentError(c, err) return } - - isExist := true - serviceConfiguratorName := param.ServiceKey() + constants.PunctuationPoint + constants.ConfiguratorRuleDotSuffix - res, err := service.GetConfigurator(ctx, serviceConfiguratorName, param.Mesh) - if err != nil { - if !corestore.IsResourceNotFound(err) { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) - return - } else if false { - // TODO(YarBor) : to check service exist or not - } - res = meshresource.NewDynamicConfigResourceWithAttributes(serviceConfiguratorName, param.Mesh) - res.Spec = &meshproto.DynamicConfig{ - Key: param.ServiceName, - Scope: constants.ScopeService, - ConfigVersion: constants.ConfiguratorVersionV3, - Enabled: true, - Configs: make([]*meshproto.OverrideConfig, 0), - } - isExist = false - } - - res.Spec.RangeConfigsToRemove(func(conf *meshproto.OverrideConfig) (isRemove bool) { - _, ok := getServiceRetryTimes(conf) - return ok - }) - - res.Spec.Configs = append(res.Spec.Configs, &meshproto.OverrideConfig{ - Side: constants.SideConsumer, - Parameters: map[string]string{`retries`: strconv.Itoa(int(param.RetryTimes))}, - XGenerateByCp: true, - }) - - if !isExist { - err = service.CreateConfigurator(ctx, param.ServiceKey(), res) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } - } else { - err = service.UpdateConfigurator(ctx, param.ServiceKey(), res) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } + if err := service.UpInsertServiceRetryConfig(ctx, param.BaseServiceReq, param.RetryTimes); err != nil { + util.HandleServiceError(c, err) + return } c.JSON(http.StatusOK, model.NewSuccessResp(nil)) } @@ -305,34 +148,21 @@ func ServiceConfigRetryPUT(ctx consolectx.Context) gin.HandlerFunc { func ServiceConfigRegionPriorityGET(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - param := model.BaseServiceReq{} + req := model.BaseServiceReq{} resp := struct { - Enabled bool `json:"enabled"` - Key string `json:"key"` - Ratio int `json:"ratio"` - }{false, "", 0} - if err := param.Query(c); err != nil { + Enabled bool `json:"enabled"` + }{false} + if err := req.Query(c); err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } - serviceAffinityRouteName := param.ServiceKey() + constants.PunctuationPoint + constants.AffinityRuleDotSuffix - res, err := service.GetAffinityRule(ctx, serviceAffinityRouteName, param.Mesh) + openSameRegionPrior, err := service.GetServiceRegionPriorityConfig(ctx, req) if err != nil { - if !corestore.IsResourceNotFound(err) { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) - return - } else if false { - // TODO(YarBor) : to check service exist or not - } - c.JSON(http.StatusOK, model.NewSuccessResp(resp)) - return - } else { - resp.Enabled = res.Spec.GetEnabled() - resp.Key = res.Spec.GetAffinity().GetKey() - resp.Ratio = int(res.Spec.GetAffinity().GetRatio()) - c.JSON(http.StatusOK, model.NewSuccessResp(resp)) + util.HandleServiceError(c, err) return } + resp.Enabled = openSameRegionPrior + c.JSON(http.StatusOK, model.NewSuccessResp(resp)) } } @@ -340,109 +170,35 @@ func ServiceConfigRegionPriorityPUT(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { param := struct { model.BaseServiceReq - Enabled bool `json:"enabled"` - Key string `json:"key"` - Ratio int `json:"ratio"` + Enabled bool `json:"enabled"` }{} if err := c.Bind(¶m); err != nil { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } - - isExist := true - serviceAffinityRouteName := param.ServiceKey() + constants.PunctuationPoint + constants.AffinityRuleDotSuffix - res, err := service.GetAffinityRule(ctx, serviceAffinityRouteName, param.Mesh) - if err != nil { - if !corestore.IsResourceNotFound(err) { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) - return - } else if false { - // TODO(YarBor) : to check service exist or not - } else { - res = meshresource.NewAffinityRouteResourceWithAttributes(serviceAffinityRouteName, param.Mesh) - res.Spec = generateDefaultAffinityRule( - "service", - param.ServiceName, - param.Key, - false, - true, - param.Ratio, - ) - isExist = false - } - } else { - res.Spec.Enabled = param.Enabled - res.Spec.Affinity.Key = param.Key - res.Spec.Affinity.Ratio = int32(param.Ratio) - } - - if !isExist { - err = service.CreateAffinityRule(ctx, res) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } - } else { - err = service.UpdateAffinityRule(ctx, res) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } + if err := service.UpInsertServiceRegionPriorityConfig(ctx, param.BaseServiceReq, param.Enabled); err != nil { + util.HandleServiceError(c, err) + return } c.JSON(http.StatusOK, model.NewSuccessResp(nil)) - return - } -} - -func generateDefaultAffinityRule(scope, key, focusKey string, runtime, enabled bool, ratio int) *meshproto.AffinityRoute { - return &meshproto.AffinityRoute{ - ConfigVersion: "v3.1", - Scope: scope, - Key: key, - Runtime: runtime, - Enabled: enabled, - Affinity: &meshproto.AffinityAware{ - Key: focusKey, - Ratio: int32(ratio), - }, } } func ServiceConfigArgumentRouteGET(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - param := struct { + req := struct { model.BaseServiceReq }{} - resp := model.ServiceArgumentRoute{Routes: make([]model.ServiceArgument, 0)} - if err := param.Query(c); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + if err := req.Query(c); err != nil { + util.HandleArgumentError(c, err) return } - serviceConditionRuleName := param.ServiceKey() + constants.PunctuationPoint + constants.ConditionRuleDotSuffix - rawRes, err := service.GetConditionRule(ctx, serviceConditionRuleName, param.Mesh) + resp, err := service.GetServiceArgumentRouteConfig(ctx, req.BaseServiceReq) if err != nil { - if !corestore.IsResourceNotFound(err) { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) - return - } else if false { - // TODO(YarBor) : to check service exist or not - } - c.JSON(http.StatusOK, model.NewSuccessResp(resp)) - return - - } else if rawRes.Spec.ToConditionRouteV3() != nil { - c.JSON(http.StatusServiceUnavailable, model.NewErrorResp("this config only serve condition-route.configVersion == v3.1, got v3.0 config ")) - return - - } else { - res := rawRes.Spec.ToConditionRouteV3x1() - res.RangeConditionsToRemove(func(r *meshproto.ConditionRule) (isRemove bool) { - _, ok := r.IsMatchMethod() - return !ok - }) - c.JSON(http.StatusOK, model.NewSuccessResp(model.ConditionV3x1ToServiceArgumentRoute(res.Conditions))) + util.HandleServiceError(c, err) return } + c.JSON(http.StatusOK, model.NewSuccessResp(resp)) } } @@ -453,59 +209,15 @@ func ServiceConfigArgumentRoutePUT(ctx consolectx.Context) gin.HandlerFunc { model.ServiceArgumentRoute }{} if err := c.Bind(¶m); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + util.HandleArgumentError(c, err) return } - isExist := true - serviceConditionRuleName := param.ServiceKey() + constants.PunctuationPoint + constants.ConditionRuleDotSuffix - rawRes, err := service.GetConditionRule(ctx, serviceConditionRuleName, param.Mesh) + err := service.UpInsertServiceArgumentRouteConfig(ctx, param.BaseServiceReq, param.ServiceArgumentRoute) if err != nil { - if !corestore.IsResourceNotFound(err) { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) - return - } else if false { - // TODO(YarBor) : to check service exist or not - } - rawRes = meshresource.NewConditionRouteResourceWithAttributes(serviceConditionRuleName, param.Mesh) - rawRes.Spec = generateDefaultConditionV3x1( - true, - false, - true, - param.ServiceName, - constants.ScopeService).ToConditionRoute() - isExist = false - } - - res := rawRes.Spec.ToConditionRouteV3x1() - if res == nil { - c.JSON(http.StatusServiceUnavailable, model.NewErrorResp("this config only serve condition-route.configVersion == v3.1, got v3.0 config ")) + util.HandleServiceError(c, err) return } - - if res.Conditions == nil { - res.Conditions = make([]*meshproto.ConditionRule, 0) - } - res.RangeConditionsToRemove(func(r *meshproto.ConditionRule) (isRemove bool) { - _, ok := r.IsMatchMethod() - return ok - }) - res.Conditions = append(res.Conditions, param.ToConditionV3x1Condition()...) - rawRes.Spec = res.ToConditionRoute() - - if isExist { - err = service.UpdateConditionRule(ctx, serviceConditionRuleName, rawRes) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } - } else { - err = service.CreateConditionRule(ctx, serviceConditionRuleName, rawRes) - if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) - return - } - } c.JSON(http.StatusOK, model.NewSuccessResp(nil)) } } diff --git a/pkg/console/handler/tag_rule.go b/pkg/console/handler/tag_rule.go index 48aadf7d5..c624faa42 100644 --- a/pkg/console/handler/tag_rule.go +++ b/pkg/console/handler/tag_rule.go @@ -25,14 +25,13 @@ import ( "github.com/duke-git/lancet/v2/strutil" "github.com/gin-gonic/gin" + "github.com/apache/dubbo-admin/pkg/common/bizerror" "github.com/apache/dubbo-admin/pkg/common/constants" consolectx "github.com/apache/dubbo-admin/pkg/console/context" "github.com/apache/dubbo-admin/pkg/console/model" "github.com/apache/dubbo-admin/pkg/console/service" - "github.com/apache/dubbo-admin/pkg/core/manager" + "github.com/apache/dubbo-admin/pkg/console/util" meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" - coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" - "github.com/apache/dubbo-admin/pkg/core/store/index" ) func TagRuleSearch(ctx consolectx.Context) gin.HandlerFunc { @@ -43,84 +42,69 @@ func TagRuleSearch(ctx consolectx.Context) gin.HandlerFunc { c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) return } - var pageData *coremodel.PageData[*meshresource.TagRouteResource] + var searchResult *model.SearchPaginationResult var err error if strutil.IsBlank(req.Keywords) { - pageData, err = manager.PageListByIndexes[*meshresource.TagRouteResource]( - ctx.ResourceManager(), - meshresource.TagRouteKind, - map[string]string{ - index.ByMeshIndex: req.Mesh, - }, - req.PageReq) - + searchResult, err = service.PageListTagRule(ctx, req) } else { - pageData, err = manager.PageSearchResourceByConditions[*meshresource.TagRouteResource]( - ctx.ResourceManager(), - meshresource.TagRouteKind, - []string{"name=" + req.Keywords}, - req.PageReq, - ) - + searchResult, err = service.SearchTagRuleByKeywords(ctx, req) } if err != nil { - c.JSON(http.StatusInternalServerError, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) return } - resp := model.NewSearchPaginationResult() - var list []*model.TagRuleSearchResp - for _, item := range pageData.Data { - list = append(list, &model.TagRuleSearchResp{ - CreateTime: "", - Enabled: item.Spec.Enabled, - RuleName: item.Name, - }) - } - resp.List = list - resp.PageInfo = pageData.Pagination - c.JSON(http.StatusOK, model.NewSuccessResp(resp)) + c.JSON(http.StatusOK, model.NewSuccessResp(searchResult)) } } func GetTagRuleWithRuleName(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - var name string ruleName := c.Param("ruleName") - mesh := c.Param("mesh") - if strings.HasSuffix(ruleName, constants.TagRuleDotSuffix) { - name = ruleName[:len(ruleName)-len(constants.TagRuleDotSuffix)] - } else { - c.JSON(http.StatusBadRequest, model.NewErrorResp(fmt.Sprintf("ruleName must end with %s", constants.TagRuleDotSuffix))) + mesh := c.Query("mesh") + if !strings.HasSuffix(ruleName, constants.TagRuleDotSuffix) { + err := bizerror.New(bizerror.InvalidArgument, fmt.Sprintf("ruleName must end with %s", constants.TagRuleDotSuffix)) + c.JSON(http.StatusBadRequest, model.NewBizErrorResp(err)) return } - if res, err := service.GetTagRule(ctx, name, mesh); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + res, err := service.GetTagRule(ctx, ruleName, mesh) + if err != nil { + util.HandleServiceError(c, err) + return + } + if res == nil { + util.HandleNotFoundError(c, ruleName) return - } else { - c.JSON(http.StatusOK, model.GenTagRouteResp(res.Spec)) } + c.JSON(http.StatusOK, model.GenTagRouteResp(res.Spec)) } } func PutTagRuleWithRuleName(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - var name string ruleName := c.Param("ruleName") - mesh := c.Param("mesh") - if strings.HasSuffix(ruleName, constants.TagRuleDotSuffix) { - name = ruleName[:len(ruleName)-len(constants.TagRuleDotSuffix)] - } else { - c.JSON(http.StatusBadRequest, model.NewErrorResp(fmt.Sprintf("ruleName must end with %s", constants.TagRuleDotSuffix))) + mesh := c.Query("mesh") + if !strings.HasSuffix(ruleName, constants.TagRuleDotSuffix) { + err := bizerror.New(bizerror.InvalidArgument, fmt.Sprintf("ruleName must end with %s", constants.TagRuleDotSuffix)) + c.JSON(http.StatusBadRequest, model.NewBizErrorResp(err)) return } - res := meshresource.NewTagRouteResourceWithAttributes(name, mesh) - err := c.Bind(res.Spec) + tagRuleRes, err := service.GetTagRule(ctx, ruleName, mesh) if err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + util.HandleServiceError(c, err) + return + } + if tagRuleRes == nil { + util.HandleNotFoundError(c, ruleName) + return + } + res := meshresource.NewTagRouteResourceWithAttributes(ruleName, mesh) + err = c.Bind(res.Spec) + if err != nil { + c.JSON(http.StatusOK, model.NewErrorResp(err.Error())) return } if err = service.UpdateTagRule(ctx, res); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + c.JSON(http.StatusOK, model.NewErrorResp(err.Error())) return } else { c.JSON(http.StatusOK, model.GenTagRouteResp(res.Spec)) @@ -132,21 +116,20 @@ func PostTagRuleWithRuleName(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { var name string ruleName := c.Param("ruleName") - mesh := c.Param("mesh") - if strings.HasSuffix(ruleName, constants.TagRuleDotSuffix) { - name = ruleName[:len(ruleName)-len(constants.TagRuleDotSuffix)] - } else { - c.JSON(http.StatusBadRequest, model.NewErrorResp(fmt.Sprintf("ruleName must end with %s", constants.TagRuleDotSuffix))) + mesh := c.Query("mesh") + if !strings.HasSuffix(ruleName, constants.TagRuleDotSuffix) { + err := bizerror.New(bizerror.InvalidArgument, fmt.Sprintf("ruleName must end with %s", constants.TagRuleDotSuffix)) + c.JSON(http.StatusBadRequest, model.NewBizErrorResp(err)) return } res := meshresource.NewTagRouteResourceWithAttributes(name, mesh) err := c.Bind(res.Spec) if err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + c.JSON(http.StatusOK, model.NewErrorResp(err.Error())) return } if err = service.CreateTagRule(ctx, res); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + c.JSON(http.StatusOK, model.NewErrorResp(err.Error())) return } else { c.JSON(http.StatusOK, model.GenTagRouteResp(res.Spec)) @@ -156,17 +139,15 @@ func PostTagRuleWithRuleName(ctx consolectx.Context) gin.HandlerFunc { func DeleteTagRuleWithRuleName(ctx consolectx.Context) gin.HandlerFunc { return func(c *gin.Context) { - var name string ruleName := c.Param("ruleName") - mesh := c.Param("mesh") - if strings.HasSuffix(ruleName, constants.TagRuleDotSuffix) { - name = ruleName[:len(ruleName)-len(constants.TagRuleDotSuffix)] - } else { - c.JSON(http.StatusBadRequest, model.NewErrorResp(fmt.Sprintf("ruleName must end with %s", constants.TagRuleDotSuffix))) + mesh := c.Query("mesh") + if !strings.HasSuffix(ruleName, constants.TagRuleDotSuffix) { + err := bizerror.New(bizerror.InvalidArgument, fmt.Sprintf("ruleName must end with %s", constants.TagRuleDotSuffix)) + c.JSON(http.StatusBadRequest, model.NewBizErrorResp(err)) return } - if err := service.DeleteTagRule(ctx, name, mesh); err != nil { - c.JSON(http.StatusBadRequest, model.NewErrorResp(err.Error())) + if err := service.DeleteTagRule(ctx, ruleName, mesh); err != nil { + c.JSON(http.StatusOK, model.NewErrorResp(err.Error())) return } c.JSON(http.StatusOK, model.NewSuccessResp("")) diff --git a/pkg/console/model/application.go b/pkg/console/model/application.go index 6da5939eb..d11943f62 100644 --- a/pkg/console/model/application.go +++ b/pkg/console/model/application.go @@ -20,7 +20,10 @@ package model import ( "strconv" + "github.com/duke-git/lancet/v2/strutil" + "github.com/apache/dubbo-admin/pkg/common/constants" + "github.com/apache/dubbo-admin/pkg/config/app" meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" ) @@ -85,7 +88,7 @@ func NewApplicationDetail() *ApplicationDetail { Workloads: NewSet(), } } -func (a *ApplicationDetail) MergeInstance(instanceRes *meshresource.InstanceResource) { +func (a *ApplicationDetail) MergeInstance(instanceRes *meshresource.InstanceResource, cfg app.AdminConfig) { instance := instanceRes.Spec if instance.WorkloadType == constants.StatefulSet { a.AppTypes.Add(constants.Stateful) @@ -95,10 +98,19 @@ func (a *ApplicationDetail) MergeInstance(instanceRes *meshresource.InstanceReso a.DubboPorts.Add(strconv.FormatInt(instance.RpcPort, 10)) a.DubboVersions.Add(instance.ReleaseVersion) a.Images.Add(instance.Image) - a.RegisterClusters.Add(instanceRes.Mesh) + if d := cfg.FindDiscovery(instanceRes.Mesh); d != nil { + a.RegisterClusters.Add(instanceRes.Mesh) + } + if cfg.Engine != nil && cfg.Engine.ID == instance.SourceEngine { + a.DeployClusters.Add(cfg.Engine.Name) + } a.RegisterModes.Add(constants.Application) a.RPCProtocols.Add(instance.Protocol) - a.SerialProtocols.Add(instance.Serialization) + if strutil.IsNotBlank(instance.Serialization) { + a.SerialProtocols.Add(instance.Serialization) + } else if strutil.IsNotBlank(instance.PreferSerialization) { + a.SerialProtocols.Add(instance.PreferSerialization) + } a.Workloads.Add(instance.WorkloadName) } @@ -144,9 +156,10 @@ type ApplicationServiceResp struct { type ApplicationServiceFormReq struct { coremodel.PageReq - AppName string `form:"appName"` - Side string `form:"side"` - Mesh string `form:"mesh"` + AppName string `form:"appName"` + ServiceName string `form:"serviceName"` + Side string `form:"side"` + Mesh string `form:"mesh"` } func NewApplicationServiceFormReq() *ApplicationServiceFormReq { @@ -158,11 +171,6 @@ func NewApplicationServiceFormReq() *ApplicationServiceFormReq { } } -type ApplicationServiceFormResp struct { - ServiceName string `json:"serviceName"` - VersionGroups []VersionGroup `json:"versionGroups"` -} - type ApplicationSearchReq struct { coremodel.PageReq diff --git a/pkg/console/model/common.go b/pkg/console/model/common.go index 18ab50b32..1931a297f 100644 --- a/pkg/console/model/common.go +++ b/pkg/console/model/common.go @@ -23,9 +23,9 @@ import ( ) type CommonResp struct { - Code string `json:"code"` - Msg string `json:"msg"` - Data any `json:"data"` + Code string `json:"code"` + Message string `json:"message"` + Data any `json:"data"` } func (r *CommonResp) WithCode(code string) *CommonResp { @@ -34,7 +34,7 @@ func (r *CommonResp) WithCode(code string) *CommonResp { } func (r *CommonResp) WithMsg(msg string) *CommonResp { - r.Msg = msg + r.Message = msg return r } @@ -45,26 +45,26 @@ func (r *CommonResp) WithData(data any) *CommonResp { func NewSuccessResp(data any) *CommonResp { return &CommonResp{ - Code: "Success", - Msg: "success", - Data: data, + Code: "Success", + Message: "success", + Data: data, } } // NewErrorResp TODO replace with NewBizErrorResp func NewErrorResp(msg string) *CommonResp { return &CommonResp{ - Code: string(bizerror.UnknownError), - Msg: msg, - Data: nil, + Code: string(bizerror.UnknownError), + Message: msg, + Data: nil, } } func NewBizErrorResp(err bizerror.Error) *CommonResp { return &CommonResp{ - Code: string(err.Code()), - Msg: err.Message(), - Data: nil, + Code: string(err.Code()), + Message: err.Message(), + Data: nil, } } diff --git a/pkg/console/model/condition_rule.go b/pkg/console/model/condition_rule.go index a65cb8e0c..11b92fe11 100644 --- a/pkg/console/model/condition_rule.go +++ b/pkg/console/model/condition_rule.go @@ -27,8 +27,8 @@ import ( type SearchConditionRuleReq struct { coremodel.PageReq - - Keywords string `json:"keywords"` + Mesh string `form:"mesh"` + Keywords string `form:"keywords"` } func (s *SearchConditionRuleReq) PageRequest() coremodel.PageReq { @@ -67,11 +67,43 @@ type ServiceArgument struct { Method string `json:"method"` } -func (s *ServiceArgument) toFrom() *meshproto.ConditionRuleFrom { - res := "method=" + s.Method - if len(s.Conditions) != 0 { - for i := 0; len(s.Conditions) > i; i++ { - res += " & " + s.Conditions[i].string() +// ToExpression Convert ServiceArgument to expression string +func (sa *ServiceArgument) ToExpression() string { + expression := "method=" + sa.Method + + // Add route conditions + for _, condition := range sa.Conditions { + expression += " & " + condition.string() + } + + // Add destinations if any + if len(sa.Destinations) > 0 { + expression += " => " + + // Process each destination + for destIdx, destination := range sa.Destinations { + for condIdx, condition := range destination.Conditions { + if destIdx > 0 || condIdx > 0 { + expression += " & " + } + expression += condition.string() + } + + // If there are multiple destinations, separate them (using comma as separator) + if destIdx < len(sa.Destinations)-1 { + expression += ", " + } + } + } + + return expression +} + +func (sa *ServiceArgument) toFrom() *meshproto.ConditionRuleFrom { + res := "method=" + sa.Method + if len(sa.Conditions) != 0 { + for i := 0; len(sa.Conditions) > i; i++ { + res += " & " + sa.Conditions[i].string() } } return &meshproto.ConditionRuleFrom{ @@ -79,9 +111,9 @@ func (s *ServiceArgument) toFrom() *meshproto.ConditionRuleFrom { } } -func (s *ServiceArgument) toTo() []*meshproto.ConditionRuleTo { - res := make([]*meshproto.ConditionRuleTo, 0, len(s.Destinations)) - for _, destination := range s.Destinations { +func (sa *ServiceArgument) toTo() []*meshproto.ConditionRuleTo { + res := make([]*meshproto.ConditionRuleTo, 0, len(sa.Destinations)) + for _, destination := range sa.Destinations { match := "" for _, condition := range destination.Conditions { if match == "" { @@ -210,42 +242,14 @@ func GenConditionRuleToResp(data *meshproto.ConditionRoute) *CommonResp { if data == nil { return NewSuccessResp(nil) } - if pb := data.ToConditionRouteV3(); pb != nil { - return NewSuccessResp(ConditionRuleResp{ - Conditions: pb.Conditions, - ConfigVersion: pb.ConfigVersion, - Enabled: pb.Enabled, - Key: pb.Key, - Runtime: pb.Runtime, - Scope: pb.Scope, - }) - } else if pb := data.ToConditionRouteV3x1(); pb != nil { - res := ConditionRuleV3X1{ - Conditions: make([]Condition, 0, len(pb.Conditions)), - ConfigVersion: "v3.1", - Enabled: pb.Enabled, - Force: pb.Force, - Key: pb.Key, - Runtime: pb.Runtime, - Scope: pb.Scope, - } - for _, condition := range pb.Conditions { - resCondition := Condition{ - From: ConditionFrom{Match: condition.From.Match}, - To: make([]ConditionTo, 0, len(condition.To)), - } - for _, to := range condition.To { - resCondition.To = append(resCondition.To, ConditionTo{ - Match: to.Match, - Weight: to.Weight, - }) - } - res.Conditions = append(res.Conditions, resCondition) - } - return NewSuccessResp(res) - } else { - return NewErrorResp("invalid condition rule") - } + return NewSuccessResp(ConditionRuleResp{ + Conditions: data.Conditions, + ConfigVersion: data.ConfigVersion, + Enabled: data.Enabled, + Key: data.Key, + Runtime: data.Runtime, + Scope: data.Scope, + }) } type ConditionRuleV3X1 struct { @@ -276,3 +280,147 @@ type ConditionTo struct { Match string `json:"match"` Weight int32 `json:"weight"` } + +// ParseConditionExpression converts an expression string to a ServiceArgument struct +// Expression format: "method=methodName & condition1 => destination1" +func ParseConditionExpression(expression string) ServiceArgument { + // Split into from and to parts (separated by "=>") + parts := strings.Split(expression, "=>") + fromPart := strings.TrimSpace(parts[0]) + toPart := "" + if len(parts) > 1 { + toPart = strings.TrimSpace(parts[1]) + } + + // Parse from part to get method and conditions + method, conditions := parseFromPart(fromPart) + + // Parse to part to get destinations + destinations := parseToPart(toPart) + + return ServiceArgument{ + Method: method, + Conditions: conditions, + Destinations: destinations, + } +} + +// parseFromPart parses the from part containing method and arguments conditions +func parseFromPart(fromPart string) (string, []RouteCondition) { + conditions := []RouteCondition{} + method := "" + + // Split conditions by "&" + subConditions := strings.Split(fromPart, "&") + + for _, condition := range subConditions { + condition = strings.TrimSpace(condition) + + // Check if it's a method condition + if strings.HasPrefix(condition, "method=") { + method = strings.TrimPrefix(condition, "method=") + method = strings.TrimSpace(method) + } else { + // Parse arguments condition + routeCond := parseRouteCondition(condition) + if routeCond.Index != "" { + conditions = append(conditions, routeCond) + } + } + } + + return method, conditions +} + +// parseRouteCondition parses a single route condition +func parseRouteCondition(condition string) RouteCondition { + var relation string + var relationOp string + + // Check if it's "=" or "!=" + if index := strings.Index(condition, constants.NotEqual); index != -1 { + relation = constants.NotEqual + relationOp = constants.NotEqual + } else if index := strings.Index(condition, constants.Equal); index != -1 { + relation = constants.Equal + relationOp = constants.Equal + } else { + return RouteCondition{} // Invalid condition + } + + // Find the position of the relation + index := strings.Index(condition, relationOp) + leftPart := strings.TrimSpace(condition[:index]) + rightPart := strings.TrimSpace(condition[index+len(relationOp):]) + + // Parse arguments[index] format + if strings.HasPrefix(leftPart, "arguments[") && strings.HasSuffix(leftPart, "]") { + argIndex := leftPart[10 : len(leftPart)-1] // Extract index within brackets + return RouteCondition{ + Index: argIndex, + Relation: relation, + Value: rightPart, + } + } + + // If not arguments format, return generic condition + return RouteCondition{ + Index: leftPart, + Relation: relation, + Value: rightPart, + } +} + +// parseToPart parses the to part (destination conditions) +func parseToPart(toPart string) []Destination { + if toPart == "" { + return []Destination{} + } + + // Split multiple conditions by "&" + conditions := strings.Split(toPart, "&") + destinationConditions := []DestinationCondition{} + + for _, condition := range conditions { + condition = strings.TrimSpace(condition) + destCond := parseDestinationCondition(condition) + if destCond.Tag != "" { + destinationConditions = append(destinationConditions, destCond) + } + } + + return []Destination{ + { + Conditions: destinationConditions, + Weight: 0, // Default weight is 0, can be adjusted as needed + }, + } +} + +// parseDestinationCondition parses destination condition +func parseDestinationCondition(condition string) DestinationCondition { + var relation string + var relationOp string + + // Check if it's "=" or "!=" + if index := strings.Index(condition, constants.NotEqual); index != -1 { + relation = constants.NotEqual + relationOp = constants.NotEqual + } else if index := strings.Index(condition, constants.Equal); index != -1 { + relation = constants.Equal + relationOp = constants.Equal + } else { + return DestinationCondition{} // Invalid condition + } + + // Find the position of the relation + index := strings.Index(condition, relationOp) + leftPart := strings.TrimSpace(condition[:index]) + rightPart := strings.TrimSpace(condition[index+len(relationOp):]) + + return DestinationCondition{ + Tag: leftPart, + Relation: relation, + Value: rightPart, + } +} diff --git a/pkg/console/model/instance.go b/pkg/console/model/instance.go index d53a7227a..e933d7790 100644 --- a/pkg/console/model/instance.go +++ b/pkg/console/model/instance.go @@ -18,9 +18,9 @@ package model import ( - gxset "github.com/dubbogo/gost/container/set" "github.com/duke-git/lancet/v2/strutil" + "github.com/apache/dubbo-admin/pkg/config/app" meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" ) @@ -54,42 +54,41 @@ func NewSearchPaginationResult() *SearchPaginationResult { } type SearchInstanceResp struct { - Ip string `json:"ip"` - Name string `json:"name"` - WorkloadName string `json:"workloadName"` - AppName string `json:"appName"` - DeployState string `json:"deployState"` - DeployCluster string `json:"deployCluster"` - RegisterState string `json:"registerState"` - RegisterClustersSet *gxset.HashSet `json:"-"` - RegisterClusters []string `json:"registerClusters"` - CreateTime string `json:"createTime"` - RegisterTime string `json:"registerTime"` // TODO: not converted - Labels map[string]string `json:"labels"` + Ip string `json:"ip"` + Name string `json:"name"` + WorkloadName string `json:"workloadName"` + AppName string `json:"appName"` + DeployState string `json:"deployState"` + DeployCluster string `json:"deployCluster"` + RegisterState string `json:"registerState"` + RegisterClusters []string `json:"registerClusters"` + CreateTime string `json:"createTime"` + RegisterTime string `json:"registerTime"` + Labels map[string]string `json:"labels"` } func NewSearchInstanceResp() *SearchInstanceResp { return &SearchInstanceResp{ - RegisterClustersSet: gxset.NewSet(), - RegisterClusters: make([]string, 0), + RegisterClusters: make([]string, 0), } } -func (r *SearchInstanceResp) FromInstanceResource(instanceResource *meshresource.InstanceResource) *SearchInstanceResp { +func (r *SearchInstanceResp) FromInstanceResource(instanceResource *meshresource.InstanceResource, cfg app.AdminConfig) *SearchInstanceResp { instance := instanceResource.Spec r.Ip = instance.Ip r.Name = instance.Name r.CreateTime = instance.CreateTime r.RegisterTime = instance.RegisterTime - cluster := instanceResource.Mesh - r.RegisterClustersSet.Add(cluster) - for _, c := range r.RegisterClustersSet.Values() { - r.RegisterClusters = append(r.RegisterClusters, c.(string)) + if d := cfg.FindDiscovery(instanceResource.Mesh); d != nil { + r.RegisterClusters = []string{d.Name} + } + if cfg.Engine != nil && cfg.Engine.ID == instance.SourceEngine { + r.DeployCluster = cfg.Engine.Name } if r.RegisterTime != "" { - r.RegisterState = "Registed" + r.RegisterState = "Registered" } else { - r.RegisterState = "UnRegisted" + r.RegisterState = "UnRegistered" } r.Labels = instance.Tags r.DeployState = instance.DeployState @@ -142,7 +141,7 @@ type Probe struct { Open bool `json:"open"` } -func FromInstanceResource(res *meshresource.InstanceResource) *InstanceDetailResp { +func FromInstanceResource(res *meshresource.InstanceResource, cfg app.AdminConfig) *InstanceDetailResp { r := &InstanceDetailResp{} instance := res.Spec r.RpcPort = instance.RpcPort @@ -153,8 +152,17 @@ func FromInstanceResource(res *meshresource.InstanceResource) *InstanceDetailRes r.CreateTime = instance.CreateTime r.ReadyTime = instance.ReadyTime r.RegisterTime = instance.RegisterTime - r.RegisterClusters = []string{res.Mesh} - r.DeployState = instance.DeployState + if d := cfg.FindDiscovery(res.Mesh); d != nil { + r.RegisterClusters = []string{d.Name} + } + if cfg.Engine.ID == res.Spec.SourceEngine { + r.DeployCluster = cfg.Engine.Name + } + if strutil.IsNotBlank(instance.DeployState) { + r.DeployState = instance.DeployState + } else { + r.DeployState = "Unknown" + } if strutil.IsBlank(r.RegisterTime) { r.RegisterState = "UnRegistered" } else { diff --git a/pkg/console/model/service.go b/pkg/console/model/service.go index 60324a99b..220c63807 100644 --- a/pkg/console/model/service.go +++ b/pkg/console/model/service.go @@ -19,11 +19,9 @@ package model import ( "fmt" - "strings" "github.com/gin-gonic/gin" - "github.com/apache/dubbo-admin/api/mesh/v1alpha1" "github.com/apache/dubbo-admin/pkg/common/constants" coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" ) @@ -46,8 +44,11 @@ func NewServiceSearchReq() *ServiceSearchReq { } type ServiceSearchResp struct { - ServiceName string `json:"serviceName"` - VersionGroups []VersionGroup `json:"versionGroups"` + ServiceName string `json:"serviceName"` + Version string `json:"version"` + Group string `json:"group"` + ProviderAppName string `json:"providerAppName,omitempty"` + ConsumerAppName string `json:"consumerAppName,omitempty"` } type ByServiceName []*ServiceSearchResp @@ -60,55 +61,14 @@ func (a ByServiceName) Less(i, j int) bool { func (a ByServiceName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -type ServiceSearch struct { - ServiceName string - VersionGroups Set -} - -func (s *ServiceSearch) FromServiceInfo(info *v1alpha1.ServiceInfo) { - s.VersionGroups.Add(info.Version + " " + info.Group) -} - -func NewServiceSearch(serviceName string) *ServiceSearch { - return &ServiceSearch{ - ServiceName: serviceName, - VersionGroups: NewSet(), - } -} - -func NewServiceSearchResp() *ServiceSearchResp { - return &ServiceSearchResp{ - ServiceName: "", - VersionGroups: nil, - } -} - -func NewServiceDistributionResp() *ServiceTabDistributionResp { - return &ServiceTabDistributionResp{ - AppName: "", - InstanceName: "", - Endpoint: "", - TimeOut: "", - Retries: "", - } -} - -func (s *ServiceSearchResp) FromServiceSearch(search *ServiceSearch) { - s.ServiceName = search.ServiceName - versionGroupList := make([]VersionGroup, 0) - for _, gv := range search.VersionGroups.Values() { - groupAndVersion := strings.Split(gv, " ") - versionGroupList = append(versionGroupList, VersionGroup{Version: groupAndVersion[0], Group: groupAndVersion[1]}) - } - s.VersionGroups = versionGroupList -} - type ServiceTabDistributionReq struct { - ServiceName string `json:"serviceName" form:"serviceName" binding:"required"` - Version string `json:"version" form:"version"` - Group string `json:"group" form:"group"` - Side string `json:"side" form:"side" binding:"required"` - Mesh string `json:"mesh" form:"mesh" binding:"required"` + ServiceName string `json:"serviceName" form:"serviceName" binding:"required"` + Version string `json:"version" form:"version"` + Group string `json:"group" form:"group"` + Side string `json:"side" form:"side" binding:"required"` + Mesh string `json:"mesh" form:"mesh" binding:"required"` + ProviderAppName string `json:"providerAppName" form:"providerAppName"` + Keywords string `json:"keywords" form:"keywords"` coremodel.PageReq } @@ -139,21 +99,6 @@ type ServiceTabDistribution struct { Retries string } -func NewServiceDistribution() *ServiceTabDistribution { - return &ServiceTabDistribution{ - AppName: "", - InstanceName: "", - Endpoint: "", - TimeOut: "", - Retries: "", - } -} - -type VersionGroup struct { - Version string `json:"version"` - Group string `json:"group"` -} - type BaseServiceReq struct { ServiceName string `json:"serviceName"` Group string `json:"group"` @@ -172,5 +117,5 @@ func (s *BaseServiceReq) Query(c *gin.Context) error { } func (s *BaseServiceReq) ServiceKey() string { - return s.ServiceName + constants.ColonSeparator + s.Group + constants.ColonSeparator + s.Version + return s.ServiceName + constants.ColonSeparator + s.Version + constants.ColonSeparator + s.Group } diff --git a/pkg/console/router/router.go b/pkg/console/router/router.go index f83706997..3abd45fc2 100644 --- a/pkg/console/router/router.go +++ b/pkg/console/router/router.go @@ -104,8 +104,8 @@ func InitRouter(r *gin.Engine, ctx consolectx.Context) { service := router.Group("/service") service.GET("/distribution", handler.GetServiceTabDistribution(ctx)) service.GET("/search", handler.SearchServices(ctx)) - service.GET("/detail", handler.GetServiceDetail(ctx)) - service.GET("/interfaces", handler.GetServiceInterfaces(ctx)) + //service.GET("/detail", handler.GetServiceDetail(ctx)) + //service.GET("/interfaces", handler.GetServiceInterfaces(ctx)) } { diff --git a/pkg/console/service/affinity_rule.go b/pkg/console/service/affinity_rule.go index 3c98d7b90..c7d5f2b2d 100644 --- a/pkg/console/service/affinity_rule.go +++ b/pkg/console/service/affinity_rule.go @@ -56,6 +56,7 @@ func CreateAffinityRule(ctx consolectx.Context, res *meshresource.AffinityRouteR func DeleteAffinityRule(ctx consolectx.Context, name string, mesh string) error { if err := ctx.ResourceManager().DeleteByKey( meshresource.AffinityRouteKind, + mesh, coremodel.BuildResourceKey(mesh, name)); err != nil { return err } diff --git a/pkg/console/service/application.go b/pkg/console/service/application.go index c3fdb4da1..80e7ee685 100644 --- a/pkg/console/service/application.go +++ b/pkg/console/service/application.go @@ -21,13 +21,14 @@ import ( "fmt" "strconv" - "github.com/duke-git/lancet/v2/maputil" "github.com/duke-git/lancet/v2/slice" "github.com/duke-git/lancet/v2/strutil" meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1" "github.com/apache/dubbo-admin/pkg/common/bizerror" "github.com/apache/dubbo-admin/pkg/common/constants" + discoveryutil "github.com/apache/dubbo-admin/pkg/common/util/discovery" + "github.com/apache/dubbo-admin/pkg/config/app" consolectx "github.com/apache/dubbo-admin/pkg/console/context" "github.com/apache/dubbo-admin/pkg/console/model" "github.com/apache/dubbo-admin/pkg/core/logger" @@ -52,7 +53,7 @@ func GetApplicationDetail(ctx consolectx.Context, req *model.ApplicationDetailRe applicationDetail := model.NewApplicationDetail() for _, instanceRes := range instanceResources { - applicationDetail.MergeInstance(instanceRes) + applicationDetail.MergeInstance(instanceRes, ctx.Config()) } respItem := &model.ApplicationDetailResp{ @@ -79,7 +80,7 @@ func GetAppInstanceInfo(ctx consolectx.Context, req *model.ApplicationTabInstanc list := slice.Map[*meshresource.InstanceResource, *model.AppInstanceInfoResp](pageData.Data, func(_ int, item *meshresource.InstanceResource) *model.AppInstanceInfoResp { - return buildAppInstanceInfoResp(item) + return buildAppInstanceInfoResp(item, ctx.Config()) }) searchResult := &model.SearchPaginationResult{ List: list, @@ -88,22 +89,22 @@ func GetAppInstanceInfo(ctx consolectx.Context, req *model.ApplicationTabInstanc return searchResult, nil } -func buildAppInstanceInfoResp(instanceRes *meshresource.InstanceResource) *model.AppInstanceInfoResp { +func buildAppInstanceInfoResp(instanceRes *meshresource.InstanceResource, cfg app.AdminConfig) *model.AppInstanceInfoResp { instance := instanceRes.Spec resp := &model.AppInstanceInfoResp{} resp.Name = instance.Name resp.AppName = instance.AppName resp.CreateTime = instance.CreateTime resp.DeployState = instance.DeployState - resp.DeployClusters = "" + if cfg.Engine.ID == instance.SourceEngine { + resp.DeployClusters = cfg.Engine.Name + } resp.IP = instance.Ip resp.Labels = instance.Tags - resp.RegisterCluster = instanceRes.Mesh - if instance.RegisterTime == "" { - resp.RegisterState = "Registered" - } else { - resp.RegisterState = "UnRegistered" + if d := cfg.FindDiscovery(instanceRes.Mesh); d != nil { + resp.RegisterCluster = d.Name } + resp.RegisterState = "Registered" resp.RegisterTime = instance.RegisterTime resp.WorkloadName = instance.WorkloadName return resp @@ -111,52 +112,52 @@ func buildAppInstanceInfoResp(instanceRes *meshresource.InstanceResource) *model func GetAppServiceInfo(ctx consolectx.Context, req *model.ApplicationServiceFormReq) (*model.SearchPaginationResult, error) { if req.Side == constants.ConsumerSide { - return getAppProvideServiceInfo(ctx, req) - } else { return getAppConsumeServiceInfo(ctx, req) + } else { + return getAppProvideServiceInfo(ctx, req) } } func getAppProvideServiceInfo(ctx consolectx.Context, req *model.ApplicationServiceFormReq) (*model.SearchPaginationResult, error) { - + var indexes map[string]string + if strutil.IsNotBlank(req.ServiceName) { + indexes = map[string]string{ + index.ByMeshIndex: req.Mesh, + index.ByServiceProviderAppName: req.AppName, + index.ByServiceProviderServiceName: req.ServiceName, + } + } else { + indexes = map[string]string{ + index.ByMeshIndex: req.Mesh, + index.ByServiceProviderAppName: req.AppName, + } + } pageData, err := manager.PageListByIndexes[*meshresource.ServiceProviderMetadataResource]( ctx.ResourceManager(), meshresource.ServiceProviderMetadataKind, - map[string]string{ - index.ByMeshIndex: req.Mesh, - index.ByServiceProviderAppName: req.AppName, - }, + indexes, req.PageReq, ) if err != nil { return nil, err } - - serviceMap := make(map[string]*model.ApplicationServiceFormResp) - for _, res := range pageData.Data { - providerMetaData := res.Spec - - if resp, exists := serviceMap[providerMetaData.ServiceName]; exists { - resp.VersionGroups = append(resp.VersionGroups, model.VersionGroup{ - Version: providerMetaData.Version, - Group: providerMetaData.Group, - }) - } else { - serviceMap[providerMetaData.ServiceName] = &model.ApplicationServiceFormResp{ - ServiceName: providerMetaData.ServiceName, - VersionGroups: []model.VersionGroup{ - { - Version: providerMetaData.Version, - Group: providerMetaData.Group, - }, - }, - } - } + if pageData.Data == nil || len(pageData.Data) == 0 { + return &model.SearchPaginationResult{ + List: []*meshresource.ServiceProviderMetadataResourceList{}, + PageInfo: coremodel.Pagination{ + Total: 0, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil } + respList := slice.Map(pageData.Data, func(_ int, item *meshresource.ServiceProviderMetadataResource) *model.ServiceSearchResp { + return ToServiceSearchRespByProvider(item) + }) pageResult := &model.SearchPaginationResult{ - List: maputil.Values(serviceMap), + List: respList, PageInfo: pageData.Pagination, } return pageResult, nil @@ -175,44 +176,66 @@ func getAppConsumeServiceInfo(ctx consolectx.Context, req *model.ApplicationServ if err != nil { return nil, err } - - serviceMap := make(map[string]*model.ApplicationServiceFormResp) - for _, res := range pageData.Data { - consumerMetadata := res.Spec - - if resp, exists := serviceMap[consumerMetadata.ServiceName]; exists { - resp.VersionGroups = append(resp.VersionGroups, model.VersionGroup{ - Version: consumerMetadata.Version, - Group: consumerMetadata.Group, - }) - } else { - serviceMap[consumerMetadata.ServiceName] = &model.ApplicationServiceFormResp{ - ServiceName: consumerMetadata.ServiceName, - VersionGroups: []model.VersionGroup{ - { - Version: consumerMetadata.Version, - Group: consumerMetadata.Group, - }, - }, - } - } + if pageData.Data == nil || len(pageData.Data) == 0 { + return &model.SearchPaginationResult{ + List: []*meshresource.ServiceConsumerMetadataResourceList{}, + PageInfo: coremodel.Pagination{ + Total: 0, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil } - + respList := slice.Map(pageData.Data, func(_ int, item *meshresource.ServiceConsumerMetadataResource) *model.ServiceSearchResp { + return ToServiceSearchRespByConsumer(item) + }) pageResult := &model.SearchPaginationResult{ - List: maputil.Values(serviceMap), + List: respList, PageInfo: pageData.Pagination, } return pageResult, nil } func SearchApplications(ctx consolectx.Context, req *model.ApplicationSearchReq) (*model.SearchPaginationResult, error) { - pageData, err := searchApplications(ctx, req.AppName, req.Mesh, req.PageReq) + if strutil.IsNotBlank(req.Keywords) { + appResList, err := SearchApplicationsByKeywords(ctx, &model.SearchReq{ + PageReq: req.PageReq, + SearchType: "appName", + Keywords: req.Keywords, + Mesh: req.Mesh, + }) + if err != nil || appResList == nil { + return nil, err + } + return &model.SearchPaginationResult{ + List: appResList, + PageInfo: coremodel.Pagination{ + Total: len(appResList), + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil + } + + pageData, err := manager.PageListByIndexes[*meshresource.ApplicationResource]( + ctx.ResourceManager(), + meshresource.ApplicationKind, + map[string]string{ + index.ByMeshIndex: req.Mesh, + }, + req.PageReq, + ) if err != nil { return nil, err } respList := slice.Map[*meshresource.ApplicationResource, *model.ApplicationSearchResp](pageData.Data, func(_ int, item *meshresource.ApplicationResource) *model.ApplicationSearchResp { - return buildApplicationSearchResp(item, req.Mesh) + return &model.ApplicationSearchResp{ + AppName: item.Spec.Name, + DeployClusters: []string{ctx.Config().Engine.Name}, + InstanceCount: item.Spec.InstanceCount, + RegistryClusters: []string{discoveryutil.GetOrDefaultRegistryName(ctx.Config(), item.Mesh)}, + } }) searchResult := &model.SearchPaginationResult{ List: respList, @@ -221,57 +244,26 @@ func SearchApplications(ctx consolectx.Context, req *model.ApplicationSearchReq) return searchResult, nil } -func BannerSearchApplications(ctx consolectx.Context, req *model.SearchReq) ([]*model.ApplicationSearchResp, error) { - pageData, err := searchApplications(ctx, req.Keywords, req.Mesh, req.PageReq) +// SearchApplicationsByKeywords search applications by keywords, for now only support accurate search by appName +func SearchApplicationsByKeywords(ctx consolectx.Context, req *model.SearchReq) ([]*model.ApplicationSearchResp, error) { + appResKey := coremodel.BuildResourceKey(req.Mesh, req.Keywords) + appRes, exists, err := manager.GetByKey[*meshresource.ApplicationResource]( + ctx.ResourceManager(), + meshresource.ApplicationKind, + appResKey) if err != nil { return nil, err } - respList := slice.Map[*meshresource.ApplicationResource, *model.ApplicationSearchResp](pageData.Data, - func(_ int, item *meshresource.ApplicationResource) *model.ApplicationSearchResp { - return buildApplicationSearchResp(item, req.Mesh) - }) - return respList, nil -} - -func searchApplications( - ctx consolectx.Context, - keywords string, - mesh string, - pageReq coremodel.PageReq) (*coremodel.PageData[*meshresource.ApplicationResource], error) { - - var pageData *coremodel.PageData[*meshresource.ApplicationResource] - var err error - if strutil.IsBlank(keywords) { - pageData, err = manager.PageListByIndexes[*meshresource.ApplicationResource]( - ctx.ResourceManager(), - meshresource.ApplicationKind, - map[string]string{ - index.ByMeshIndex: mesh, - }, - pageReq, - ) - } else { - pageData, err = manager.PageSearchResourceByConditions[*meshresource.ApplicationResource]( - ctx.ResourceManager(), - meshresource.ApplicationKind, - []string{"name=" + keywords}, - pageReq, - ) - } - if err != nil { - return nil, err + if !exists { + return nil, nil } - return pageData, nil -} - -func buildApplicationSearchResp(appResource *meshresource.ApplicationResource, mesh string) *model.ApplicationSearchResp { - application := appResource.Spec - return &model.ApplicationSearchResp{ - AppName: application.Name, - DeployClusters: []string{""}, - InstanceCount: application.InstanceCount, - RegistryClusters: []string{mesh}, + searchResp := &model.ApplicationSearchResp{ + AppName: appRes.Spec.Name, + DeployClusters: []string{ctx.Config().Engine.Name}, + InstanceCount: appRes.Spec.InstanceCount, + RegistryClusters: []string{discoveryutil.GetOrDefaultRegistryName(ctx.Config(), appRes.Mesh)}, } + return []*model.ApplicationSearchResp{searchResp}, nil } func isAppAccessLogConfig(conf *meshproto.OverrideConfig, appName string) bool { @@ -328,7 +320,7 @@ func insertConfiguratorWithAccessLog(ctx consolectx.Context, res *meshresource.D Configs: make([]*meshproto.OverrideConfig, 0), } res.Spec.Configs = append(res.Spec.Configs, newAccessLogEnabledConfig(appName)) - err := CreateConfigurator(ctx, appConfiguratorName, res) + err := CreateConfigurator(ctx, res) if err != nil { logger.Errorf("create configurator failed when open accesslog, resourceKey: %s, openAccessLog: %t, err: %s", coremodel.BuildResourceKey(mesh, appName), openAccessLog, err) @@ -363,7 +355,7 @@ func updateConfiguratorWithAccessLog(ctx consolectx.Context, res *meshresource.D // update the access log enabled status as needed accessLogConfig.Enabled = openAccessLog } - err := UpdateConfigurator(ctx, appConfiguratorName, res) + err := UpdateConfigurator(ctx, res) if err != nil { logger.Errorf("update configurator failed when opening accesslog, resourceKey: %s, openAccessLog: %t, err: %s", coremodel.BuildResourceKey(mesh, appName), openAccessLog, err) @@ -504,7 +496,7 @@ func UpInsertAppFlowWeightConfig(ctx consolectx.Context, appName string, mesh st }) res.Spec.Configs = slice.Union(res.Spec.Configs, flowWeightConfigs) - err = UpdateConfigurator(ctx, appConfiguratorName, res) + err = UpdateConfigurator(ctx, res) if err != nil { logger.Errorf("update configurator failed with app flow weight, resourceKey: %s, err: %s", coremodel.BuildResourceKey(mesh, appName), err) @@ -528,7 +520,7 @@ func insertConfiguratorWithFlowWeight( return fromFlowWeightSet(set) }) res.Spec.Configs = flowWeightConfigs - err := CreateConfigurator(ctx, appConfiguratorName, res) + err := CreateConfigurator(ctx, res) if err != nil { logger.Errorf("insert configurator failed with app flow weight, resourceKey: %s, err: %s", coremodel.BuildResourceKey(mesh, appName), err) @@ -554,6 +546,7 @@ func fromFlowWeightSet(set model.FlowWeightSet) *meshproto.OverrideConfig { XGenerateByCp: true, } } + func GetGrayConfig(ctx consolectx.Context, appName string, mesh string) (*model.AppGrayConfigResp, error) { resp := &model.AppGrayConfigResp{} serviceTagRuleName := appName + constants.TagRuleDotSuffix diff --git a/pkg/console/service/condition_rule.go b/pkg/console/service/condition_rule.go index d7890cd6d..7878928c5 100644 --- a/pkg/console/service/condition_rule.go +++ b/pkg/console/service/condition_rule.go @@ -18,48 +18,81 @@ package service import ( + "github.com/duke-git/lancet/v2/slice" + "github.com/duke-git/lancet/v2/strutil" + + "github.com/apache/dubbo-admin/pkg/common/bizerror" "github.com/apache/dubbo-admin/pkg/console/context" "github.com/apache/dubbo-admin/pkg/console/model" "github.com/apache/dubbo-admin/pkg/core/logger" "github.com/apache/dubbo-admin/pkg/core/manager" meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" + "github.com/apache/dubbo-admin/pkg/core/store/index" ) func SearchConditionRules(ctx context.Context, req *model.SearchConditionRuleReq) (*model.SearchPaginationResult, error) { - pageData, err := manager.PageSearchResourceByConditions[*meshresource.ConditionRouteResource]( + if strutil.IsNotBlank(req.Keywords) { + return SearchConditionRuleByKeywords(ctx, req) + } + pageData, err := manager.PageListByIndexes[*meshresource.ConditionRouteResource]( ctx.ResourceManager(), meshresource.ConditionRouteKind, - []string{"name=" + req.Keywords}, - req.PageRequest()) + map[string]string{ + index.ByMeshIndex: req.Mesh, + }, + req.PageReq) if err != nil { - return nil, err + logger.Errorf("search condition route error: %v", err) + return nil, bizerror.New(bizerror.InternalError, "search condition route failed, please try again") } + respList := slice.FilterMap(pageData.Data, + func(index int, item *meshresource.ConditionRouteResource) (*model.ConditionRuleSearchResp, bool) { + resp := ToSearchConditionRuleResp(item) + return resp, resp != nil + }) + return &model.SearchPaginationResult{ + List: respList, + PageInfo: pageData.Pagination, + }, nil +} + +// SearchConditionRuleByKeywords for now, only accurate search is supported +func SearchConditionRuleByKeywords(ctx context.Context, req *model.SearchConditionRuleReq) (*model.SearchPaginationResult, error) { + resKey := coremodel.BuildResourceKey(req.Mesh, req.Keywords) + conditionRuleRes, exists, err := manager.GetByKey[*meshresource.ConditionRouteResource]( + ctx.ResourceManager(), meshresource.ConditionRouteKind, resKey) + if err != nil { + logger.Errorf("search condition rule error: %v", err) + return nil, bizerror.New(bizerror.InternalError, "search condition rule failed, please try again") + } + if !exists { + return &model.SearchPaginationResult{ + List: nil, + PageInfo: coremodel.Pagination{ + Total: 0, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil + } + return &model.SearchPaginationResult{ + List: []*model.ConditionRuleSearchResp{ToSearchConditionRuleResp(conditionRuleRes)}, + PageInfo: coremodel.Pagination{ + Total: 1, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil +} - var respList []model.ConditionRuleSearchResp - for _, item := range pageData.Data { - if v3 := item.Spec.ToConditionRouteV3(); v3 != nil { - respList = append(respList, model.ConditionRuleSearchResp{ - RuleName: item.Name, - Scope: v3.GetScope(), - CreateTime: item.CreationTimestamp.String(), - Enabled: v3.GetEnabled(), - }) - } else if v3x1 := item.Spec.ToConditionRouteV3x1(); v3x1 != nil { - respList = append(respList, model.ConditionRuleSearchResp{ - RuleName: item.Name, - Scope: v3x1.GetScope(), - CreateTime: item.CreationTimestamp.String(), - Enabled: v3x1.GetEnabled(), - }) - } else { - logger.Errorf("Invalid condition route %v", item) - } +func ToSearchConditionRuleResp(res *meshresource.ConditionRouteResource) *model.ConditionRuleSearchResp { + return &model.ConditionRuleSearchResp{ + RuleName: res.Name, + Scope: res.Spec.Scope, + CreateTime: res.CreationTimestamp.String(), + Enabled: res.Spec.Enabled, } - result := model.NewSearchPaginationResult() - result.List = respList - result.PageInfo = pageData.Pagination - return result, nil } func GetConditionRule(ctx context.Context, name string, mesh string) (*meshresource.ConditionRouteResource, error) { @@ -72,24 +105,24 @@ func GetConditionRule(ctx context.Context, name string, mesh string) (*meshresou return res, nil } -func UpdateConditionRule(ctx context.Context, name string, res *meshresource.ConditionRouteResource) error { +func UpdateConditionRule(ctx context.Context, res *meshresource.ConditionRouteResource) error { if err := ctx.ResourceManager().Update(res); err != nil { - logger.Warnf("update %s condition failed with error: %s", name, err.Error()) + logger.Warnf("update %s condition failed with error: %s", res.Name, err.Error()) return err } return nil } -func CreateConditionRule(ctx context.Context, name string, res *meshresource.ConditionRouteResource) error { +func CreateConditionRule(ctx context.Context, res *meshresource.ConditionRouteResource) error { if err := ctx.ResourceManager().Add(res); err != nil { - logger.Warnf("create %s condition failed with error: %s", name, err.Error()) + logger.Warnf("create %s condition failed with error: %s", res.Name, err.Error()) return err } return nil } func DeleteConditionRule(ctx context.Context, name string, mesh string) error { - if err := ctx.ResourceManager().DeleteByKey(meshresource.ConditionRouteKind, coremodel.BuildResourceKey(mesh, name)); err != nil { + if err := ctx.ResourceManager().DeleteByKey(meshresource.ConditionRouteKind, mesh, coremodel.BuildResourceKey(mesh, name)); err != nil { return err } return nil diff --git a/pkg/console/service/configurator_rule.go b/pkg/console/service/configurator_rule.go index 1e7b033a9..7bdf97943 100644 --- a/pkg/console/service/configurator_rule.go +++ b/pkg/console/service/configurator_rule.go @@ -18,13 +18,89 @@ package service import ( + "github.com/duke-git/lancet/v2/slice" + + "github.com/apache/dubbo-admin/pkg/common/bizerror" consolectx "github.com/apache/dubbo-admin/pkg/console/context" + "github.com/apache/dubbo-admin/pkg/console/model" "github.com/apache/dubbo-admin/pkg/core/logger" "github.com/apache/dubbo-admin/pkg/core/manager" meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" + "github.com/apache/dubbo-admin/pkg/core/store/index" ) +func PageListConfiguratorRule(ctx consolectx.Context, req *model.SearchReq) (*model.SearchPaginationResult, error) { + pageData, err := manager.PageListByIndexes[*meshresource.DynamicConfigResource]( + ctx.ResourceManager(), + meshresource.DynamicConfigKind, + map[string]string{ + index.ByMeshIndex: req.Mesh, + }, + req.PageReq) + if err != nil { + logger.Errorf("search dynamic config rule error: %v", err) + return nil, bizerror.New(bizerror.InternalError, "search dynamic config rule failed, please try again") + } + if pageData.Data == nil || len(pageData.Data) == 0 { + return &model.SearchPaginationResult{ + List: nil, + PageInfo: coremodel.Pagination{ + Total: 0, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil + } + respList := slice.Map(pageData.Data, func(_ int, item *meshresource.DynamicConfigResource) *model.ConfiguratorSearchResp { + return &model.ConfiguratorSearchResp{ + Scope: item.Spec.Scope, + CreateTime: "", + Enabled: item.Spec.Enabled, + RuleName: item.Name, + } + }) + return &model.SearchPaginationResult{ + List: respList, + PageInfo: pageData.Pagination, + }, nil +} + +// SearchConfiguratorRuleByKeywords for now, only accurate search is supported +func SearchConfiguratorRuleByKeywords(ctx consolectx.Context, req *model.SearchReq) (*model.SearchPaginationResult, error) { + resKey := coremodel.BuildResourceKey(req.Mesh, req.Keywords) + configuratorRuleRes, exists, err := manager.GetByKey[*meshresource.DynamicConfigResource]( + ctx.ResourceManager(), meshresource.DynamicConfigKind, resKey) + if err != nil { + logger.Errorf("search dynamic config rule error: %v", err) + return nil, bizerror.New(bizerror.InternalError, "search dynamic config rule failed, please try again") + } + if !exists { + return &model.SearchPaginationResult{ + List: nil, + PageInfo: coremodel.Pagination{ + Total: 0, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil + } + resp := &model.ConfiguratorSearchResp{ + Scope: configuratorRuleRes.Spec.Scope, + CreateTime: "", + Enabled: configuratorRuleRes.Spec.Enabled, + RuleName: configuratorRuleRes.Name, + } + return &model.SearchPaginationResult{ + List: []*model.ConfiguratorSearchResp{resp}, + PageInfo: coremodel.Pagination{ + Total: 1, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil +} + func GetConfigurator(ctx consolectx.Context, name string, mesh string) (*meshresource.DynamicConfigResource, error) { res, _, err := manager.GetByKey[*meshresource.DynamicConfigResource]( ctx.ResourceManager(), @@ -37,24 +113,24 @@ func GetConfigurator(ctx consolectx.Context, name string, mesh string) (*meshres return res, nil } -func UpdateConfigurator(ctx consolectx.Context, name string, res *meshresource.DynamicConfigResource) error { +func UpdateConfigurator(ctx consolectx.Context, res *meshresource.DynamicConfigResource) error { if err := ctx.ResourceManager().Update(res); err != nil { - logger.Warnf("update %s configurator failed with error: %s", name, err.Error()) + logger.Warnf("update %s configurator failed with error: %s", res.Name, err.Error()) return err } return nil } -func CreateConfigurator(ctx consolectx.Context, name string, res *meshresource.DynamicConfigResource) error { +func CreateConfigurator(ctx consolectx.Context, res *meshresource.DynamicConfigResource) error { if err := ctx.ResourceManager().Add(res); err != nil { - logger.Warnf("create %s configurator failed with error: %s", name, err.Error()) + logger.Warnf("create %s configurator failed with error: %s", res.Name, err.Error()) return err } return nil } func DeleteConfigurator(ctx consolectx.Context, name string, mesh string) error { - if err := ctx.ResourceManager().DeleteByKey(meshresource.DynamicConfigKind, coremodel.BuildResourceKey(mesh, name)); err != nil { + if err := ctx.ResourceManager().DeleteByKey(meshresource.DynamicConfigKind, mesh, coremodel.BuildResourceKey(mesh, name)); err != nil { logger.Warnf("delete %s configurator failed with error: %s", name, err.Error()) return err } diff --git a/pkg/console/service/instance.go b/pkg/console/service/instance.go index bf43a58a9..ccfe2f949 100644 --- a/pkg/console/service/instance.go +++ b/pkg/console/service/instance.go @@ -23,59 +23,108 @@ import ( "net/http" "strings" + "github.com/duke-git/lancet/v2/convertor" + "github.com/duke-git/lancet/v2/slice" "github.com/duke-git/lancet/v2/strutil" + meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1" + "github.com/apache/dubbo-admin/pkg/common/bizerror" + "github.com/apache/dubbo-admin/pkg/common/constants" consolectx "github.com/apache/dubbo-admin/pkg/console/context" "github.com/apache/dubbo-admin/pkg/console/model" + "github.com/apache/dubbo-admin/pkg/core/logger" "github.com/apache/dubbo-admin/pkg/core/manager" meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" "github.com/apache/dubbo-admin/pkg/core/store/index" ) -// BannerSearchIp -// TODO: implement me -func BannerSearchIp(ctx consolectx.Context, req *model.SearchReq) (*model.SearchPaginationResult, error) { - return nil, nil +// SearchInstanceByIp search instance by ip +func SearchInstanceByIp(ctx consolectx.Context, req *model.SearchReq) (*model.SearchPaginationResult, error) { + pageData, err := manager.PageListByIndexes[*meshresource.InstanceResource]( + ctx.ResourceManager(), + meshresource.InstanceKind, + map[string]string{ + index.ByMeshIndex: req.Mesh, + index.ByInstanceIpIndex: req.Keywords, + }, + req.PageReq) + if err != nil { + return nil, err + } + if pageData.Data == nil || len(pageData.Data) == 0 { + return &model.SearchPaginationResult{ + List: []*meshresource.ServiceProviderMetadataResourceList{}, + PageInfo: coremodel.Pagination{ + Total: 0, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil + } + return &model.SearchPaginationResult{ + List: slice.Map(pageData.Data, func(_ int, item *meshresource.InstanceResource) *model.AppInstanceInfoResp { + return buildAppInstanceInfoResp(item, ctx.Config()) + }), + PageInfo: pageData.Pagination, + }, nil } -// BannerSearchInstances -// TODO: implement me -func BannerSearchInstances(ctx consolectx.Context, req *model.SearchReq) (*model.SearchPaginationResult, error) { - // TODO: implement me - return nil, nil +// SearchInstanceByName search instance by name +func SearchInstanceByName(ctx consolectx.Context, req *model.SearchReq) (*model.SearchPaginationResult, error) { + pageData, err := manager.PageListByIndexes[*meshresource.InstanceResource]( + ctx.ResourceManager(), + meshresource.InstanceKind, + map[string]string{ + index.ByMeshIndex: req.Mesh, + index.ByInstanceNameIndex: req.Keywords, + }, + req.PageReq) + if err != nil { + return nil, err + } + if pageData.Data == nil || len(pageData.Data) == 0 { + return &model.SearchPaginationResult{ + List: []*meshresource.ServiceProviderMetadataResourceList{}, + PageInfo: coremodel.Pagination{ + Total: 0, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil + } + return &model.SearchPaginationResult{ + List: slice.Map(pageData.Data, func(_ int, item *meshresource.InstanceResource) *model.AppInstanceInfoResp { + return buildAppInstanceInfoResp(item, ctx.Config()) + }), + PageInfo: pageData.Pagination, + }, nil } func SearchInstances(ctx consolectx.Context, req *model.SearchInstanceReq) (*model.SearchPaginationResult, error) { - var pageData *coremodel.PageData[*meshresource.InstanceResource] - var err error - if strutil.IsBlank(req.Keywords) { - pageData, err = manager.PageListByIndexes[*meshresource.InstanceResource]( - ctx.ResourceManager(), - meshresource.InstanceKind, - map[string]string{ - index.ByMeshIndex: req.Mesh, - }, - req.PageReq) - if err != nil { - return nil, err - } - } else { - pageData, err = manager.PageSearchResourceByConditions[*meshresource.InstanceResource]( - ctx.ResourceManager(), - meshresource.InstanceKind, - []string{"ip=" + req.Keywords}, - req.PageReq, - ) - if err != nil { - return nil, err - } + if strutil.IsNotBlank(req.Keywords) { + return SearchInstanceByIp(ctx, &model.SearchReq{ + PageReq: req.PageReq, + SearchType: "ip", + Keywords: req.Keywords, + Mesh: req.Mesh, + }) + } + pageData, err := manager.PageListByIndexes[*meshresource.InstanceResource]( + ctx.ResourceManager(), + meshresource.InstanceKind, + map[string]string{ + index.ByMeshIndex: req.Mesh, + }, + req.PageReq) + if err != nil { + logger.Errorf("Failed to search instance,req: %s, cause: %v", convertor.ToString(req), err) + return nil, err } - resp := model.NewSearchPaginationResult() var list []*model.SearchInstanceResp for _, item := range pageData.Data { - list = append(list, model.NewSearchInstanceResp().FromInstanceResource(item)) + list = append(list, model.NewSearchInstanceResp().FromInstanceResource(item, ctx.Config())) } resp.List = list resp.PageInfo = pageData.Pagination @@ -91,8 +140,11 @@ func GetInstanceDetail(ctx consolectx.Context, req *model.InstanceDetailReq) (*m if err != nil { return nil, err } + if res == nil { + return nil, bizerror.New(bizerror.NotFoundError, fmt.Sprintf("instance %s not found", req.InstanceName)) + } - resp := model.FromInstanceResource(res) + resp := model.FromInstanceResource(res, ctx.Config()) return resp, nil } @@ -119,6 +171,234 @@ func GetInstanceMetrics(ctx consolectx.Context, req *model.MetricsReq) ([]*model return []*model.MetricsResp{metricsResp}, nil } +func UpdateInstanceTrafficStatus(ctx consolectx.Context, mesh string, appName string, instanceIP string, newDisabled bool) error { + conditionRuleRes, err := GetConditionRule(ctx, appName, mesh) + if err != nil { + logger.Errorf("get condition rule for %s failed, cause: %s", appName, err) + return err + } + // if no condition rule for application exists + if conditionRuleRes == nil || conditionRuleRes.Spec == nil { + // if disable traffic is false, and rule doesn't exist, directly return + if !newDisabled { + return nil + } + conditionRoute := generateDefaultConditionV3(true, true, true, appName, constants.ScopeApplication) + resName := appName + constants.ConditionRuleDotSuffix + conditionRuleRes = meshresource.NewConditionRouteResourceWithAttributes(resName, mesh) + conditionRuleRes.Spec = conditionRoute + err := CreateConditionRule(ctx, conditionRuleRes) + if err != nil { + logger.Errorf("create condition rule for app %s failed, cause: %s", appName, err) + return err + } + return nil + } + // otherwise, checkout condition one by one + for i, condition := range conditionRuleRes.Spec.Conditions { + oldDisabled := isInstanceTrafficDisabled(condition, instanceIP) + // if user needs to disable traffic and instance's traffic is already disabled, skip updating, directly return + if newDisabled && oldDisabled { + logger.Warnf("The instance %s has been disabled, skip updating condition rule", instanceIP) + return nil + } + // if user needs to enable traffic while instance's traffic is disabled, update condition rule + if !newDisabled && oldDisabled { + conditionRuleRes.Spec.Conditions = append(conditionRuleRes.Spec.Conditions[:i], conditionRuleRes.Spec.Conditions[i+1:]...) + if err = UpdateConditionRule(ctx, conditionRuleRes); err != nil { + logger.Errorf("update condition rule for app %s failed, cause: %s", appName, err) + return err + } + return nil + } + } + // if user needs to enable traffic while instance's traffic is already enabled, directly return + if !newDisabled { + logger.Warnf("the instance %s has been enabled, skip updating condition rule", instanceIP) + return nil + } + // else user needs ato disable traffic, add a disable condition + conditionRuleRes.Spec.Conditions = append(conditionRuleRes.Spec.Conditions, disableExpression(instanceIP)) + if err := UpdateConditionRule(ctx, conditionRuleRes); err != nil { + logger.Errorf("update condition rule for app %s failed, cause: %s", appName, err) + return err + } + return nil +} + +func GetInstanceTrafficStatus(ctx consolectx.Context, mesh string, appName string, instanceIP string) (bool, error) { + resName := appName + constants.ConditionRuleDotSuffix + res, err := GetConditionRule(ctx, resName, mesh) + if err != nil { + logger.Errorf("get condition rule for %s failed, cause: %s", appName, err) + return true, err + } + if res == nil { + return false, nil + } + disabled := false + slice.ForEachWithBreak(res.Spec.Conditions, func(_ int, condition string) bool { + disabled = isInstanceTrafficDisabled(condition, instanceIP) + return disabled + }) + return disabled, nil +} + +func disableExpression(instanceIP string) string { + return "=>host!=" + instanceIP +} + +func generateDefaultConditionV3(Enabled, Force, Runtime bool, Key, Scope string) *meshproto.ConditionRoute { + return &meshproto.ConditionRoute{ + ConfigVersion: constants.ConfiguratorVersionV3, + Priority: 0, + Enabled: true, + Force: Force, + Runtime: Runtime, + Key: Key, + Scope: Scope, + Conditions: make([]string, 0), + } +} + +// isInstanceTrafficDisabled judge if a condition is instance traffic disable expression or not. +// A condition include fromCondition and toCondition which is seperated by `=>`. +// return true if the instance traffic is disabled, otherwise return false. +func isInstanceTrafficDisabled(condition string, targetIP string) bool { + if len(condition) == 0 { + return false + } + condition = strings.ReplaceAll(condition, " ", "") + // only accept string start with `=>` + if !strings.HasPrefix(condition, "=>") { + return false + } + toCondition := strings.TrimPrefix(condition, "=>") + + if !strings.Contains(toCondition, targetIP) { + return false + } + targetExpression := "host!=" + targetIP + if targetExpression != toCondition { + return false + } + return true +} + +func GetInstanceAccessLogOpenStatus(ctx consolectx.Context, mesh string, applicationName string, instanceIP string) (bool, error) { + appConfiguratorName := applicationName + constants.ConfiguratorRuleDotSuffix + res, err := GetConfigurator(ctx, appConfiguratorName, mesh) + if err != nil { + logger.Errorf("get configurator for %s failed, cause: %s", appConfiguratorName, err) + return false, err + } + if res == nil || res.Spec == nil { + return false, nil + } + openAccessLog := false + if res.Spec.Enabled { + slice.ForEachWithBreak(res.Spec.Configs, func(_ int, conf *meshproto.OverrideConfig) bool { + openAccessLog = isInstanceAccessLogOpen(conf, instanceIP) + return openAccessLog + }) + } + return openAccessLog, nil +} + +func UpdateInstanceAccessLogOpenStatus( + ctx consolectx.Context, + mesh string, + appName string, + instanceIP string, + openStatus bool) error { + appConfiguratorName := appName + constants.ConfiguratorRuleDotSuffix + res, err := GetConfigurator(ctx, appConfiguratorName, mesh) + if err != nil { + logger.Errorf("get configurator for %s failed, cause: %s", appConfiguratorName, err) + return err + } + // if configurator doesn't exist + if res == nil || res.Spec == nil { + // if user needs to disable accesslog, directly return + if !openStatus { + logger.Warnf("the instance %s accesslog is disabled, skip updating configurator", instanceIP) + return nil + } + // otherwise create a new configurator with accesslog opened + res = meshresource.NewDynamicConfigResourceWithAttributes(appConfiguratorName, mesh) + res.Spec = &meshproto.DynamicConfig{ + Key: appName, + Scope: constants.ScopeApplication, + ConfigVersion: constants.ConfiguratorVersionV3, + Enabled: true, + Configs: []*meshproto.OverrideConfig{ + { + Side: constants.SideProvider, + Match: &meshproto.ConditionMatch{Address: &meshproto.AddressMatch{Wildcard: instanceIP + `:*`}}, + Parameters: map[string]string{`accesslog`: `true`}, + XGenerateByCp: true, + }, + }, + } + err := CreateConfigurator(ctx, res) + if err != nil { + logger.Errorf("create configurator for instance %s%s failed, cause: %s", appName, instanceIP, err) + return err + } + return nil + } + + // otherwise we need to match config one by one + for i, config := range res.Spec.Configs { + accessLogOpened := isInstanceAccessLogOpen(config, instanceIP) + // if user needs to open accesslog and accesslog is already opened, directly return + if openStatus && accessLogOpened { + logger.Warnf("the instance %s accesslog is already opened, skip updating configurator", instanceIP) + return nil + } + // if user needs to close accesslog and accesslog is opened, remove the config and return + if !openStatus && accessLogOpened { + res.Spec.Configs = slice.Concat(res.Spec.Configs[:i], res.Spec.Configs[i+1:]) + err := UpdateConfigurator(ctx, res) + if err != nil { + logger.Errorf("update configurator for instance %s%s failed, cause: %s", appName, instanceIP, err) + return err + } + return nil + } + } + // if user needs to close accesslog and accesslog is not opened, directly return + if !openStatus { + logger.Warnf("the instance %s accesslog is already disabled, skip updating configurator", instanceIP) + return nil + } + // otherwise we need to add a new config to open accesslog + res.Spec.Configs = append(res.Spec.Configs, &meshproto.OverrideConfig{ + Side: constants.SideProvider, + Match: &meshproto.ConditionMatch{Address: &meshproto.AddressMatch{Wildcard: instanceIP + `:*`}}, + Parameters: map[string]string{`accesslog`: `true`}, + XGenerateByCp: true, + }) + err = UpdateConfigurator(ctx, res) + if err != nil { + logger.Errorf("update configurator for instance %s%s failed, cause: %s", appName, instanceIP, err) + return err + } + return nil +} + +func isInstanceAccessLogOpen(conf *meshproto.OverrideConfig, IP string) bool { + if conf != nil && + conf.Match != nil && + conf.Match.Address != nil && + conf.Match.Address.Wildcard == IP+`:*` && + conf.Side == constants.SideProvider && + conf.Parameters != nil && + conf.Parameters[`accesslog`] == `true` { + return true + } + return false +} func fetchMetricsData(ip string, port int64) ([]model.Metric, error) { url := fmt.Sprintf("http://%s:%d/metrics", ip, port) response, err := http.Get(url) diff --git a/pkg/console/service/service.go b/pkg/console/service/service.go index 4ff11e885..be851266e 100644 --- a/pkg/console/service/service.go +++ b/pkg/console/service/service.go @@ -18,87 +18,123 @@ package service import ( - "sort" + "strconv" + "strings" - "github.com/duke-git/lancet/v2/maputil" "github.com/duke-git/lancet/v2/slice" "github.com/duke-git/lancet/v2/strutil" + meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1" + "github.com/apache/dubbo-admin/pkg/common/bizerror" + "github.com/apache/dubbo-admin/pkg/common/constants" + discoveryutil "github.com/apache/dubbo-admin/pkg/common/util/discovery" consolectx "github.com/apache/dubbo-admin/pkg/console/context" "github.com/apache/dubbo-admin/pkg/console/model" + "github.com/apache/dubbo-admin/pkg/core/logger" "github.com/apache/dubbo-admin/pkg/core/manager" meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" "github.com/apache/dubbo-admin/pkg/core/store/index" ) -// GetServiceTabDistribution TODO implement +// GetServiceTabDistribution get service distribution func GetServiceTabDistribution(ctx consolectx.Context, req *model.ServiceTabDistributionReq) (*model.SearchPaginationResult, error) { - return nil, nil + indexes := map[string]string{ + index.ByServiceConsumerServiceName: req.ServiceName, + } + // for now, only support accurate name match + if strutil.IsNotBlank(req.Keywords) { + indexes[index.ByServiceConsumerAppName] = req.Keywords + } + pageData, err := manager.PageListByIndexes[*meshresource.ServiceConsumerMetadataResource]( + ctx.ResourceManager(), + meshresource.ServiceConsumerMetadataKind, + indexes, + req.PageReq) + if err != nil { + logger.Errorf("get service consumer %s failed, cause: %v", req.ServiceName, err) + return nil, bizerror.New(bizerror.InternalError, "get service consumer failed, please try again") + } + if pageData.Data == nil || len(pageData.Data) == 0 { + return &model.SearchPaginationResult{ + List: []*meshresource.ServiceConsumerMetadataResourceList{}, + PageInfo: coremodel.Pagination{ + Total: 0, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil + } + appResKeys := slice.Map(pageData.Data, func(_ int, item *meshresource.ServiceConsumerMetadataResource) string { + return coremodel.BuildResourceKey(req.Mesh, item.Spec.ConsumerAppName) + }) + appResList, err := manager.GetByKeys[*meshresource.ApplicationResource]( + ctx.ResourceManager(), meshresource.ApplicationKind, appResKeys) + if err != nil { + logger.Errorf("get application list %v failed, cause: %s", appResKeys, err) + return nil, err + } + respList := slice.Map(appResList, func(_ int, item *meshresource.ApplicationResource) model.ApplicationSearchResp { + return model.ApplicationSearchResp{ + AppName: item.Spec.Name, + InstanceCount: item.Spec.InstanceCount, + DeployClusters: []string{ctx.Config().Engine.Name}, + RegistryClusters: []string{discoveryutil.GetOrDefaultRegistryName(ctx.Config(), item.Mesh)}, + } + }) + return &model.SearchPaginationResult{ + List: respList, + PageInfo: pageData.Pagination, + }, nil } +// SearchServices search services pageably func SearchServices(ctx consolectx.Context, req *model.ServiceSearchReq) (*model.SearchPaginationResult, error) { - if req.Keywords != "" { - return BannerSearchServices(ctx, req) - } - var pageData *coremodel.PageData[*meshresource.ServiceResource] - var err error - if strutil.IsBlank(req.Keywords) { - pageData, err = manager.PageListByIndexes[*meshresource.ServiceResource]( - ctx.ResourceManager(), - meshresource.ServiceKind, - map[string]string{ - index.ByMeshIndex: req.Mesh, - }, - req.PageReq, - ) - if err != nil { - return nil, err - } - } else { - pageData, err = manager.PageSearchResourceByConditions[*meshresource.ServiceResource]( - ctx.ResourceManager(), - meshresource.ServiceKind, - []string{"serviceName=" + req.Keywords}, - req.PageReq, - ) - if err != nil { - return nil, err - } + if strutil.IsNotBlank(req.Keywords) { + return SearchServicesByKeywords(ctx, req) } - - serviceMap := make(map[string]*model.ServiceSearchResp) - for _, serviceRes := range pageData.Data { - service := serviceRes.Spec - if _, exists := serviceMap[service.Name]; !exists { - serviceMap[service.Name] = toServiceSearchResp(serviceRes) - } else { - vgs := serviceMap[service.Name].VersionGroups - serviceMap[service.Name].VersionGroups = append(vgs, model.VersionGroup{ - Version: service.Version, - Group: service.Group, - }) - } + pageData, err := manager.PageListByIndexes[*meshresource.ServiceProviderMetadataResource]( + ctx.ResourceManager(), + meshresource.ServiceProviderMetadataKind, + map[string]string{ + index.ByMeshIndex: req.Mesh, + }, + req.PageReq, + ) + if err != nil { + logger.Errorf("get service provider failed, cause: %v", err) + return nil, err + } + if pageData.Data == nil || len(pageData.Data) == 0 { + return nil, nil } + serviceSearchResps := slice.Map(pageData.Data, + func(_ int, item *meshresource.ServiceProviderMetadataResource) *model.ServiceSearchResp { + return ToServiceSearchRespByProvider(item) + }) return &model.SearchPaginationResult{ - List: maputil.Values(serviceMap), + List: serviceSearchResps, PageInfo: pageData.Pagination, }, nil } -func BannerSearchServices(ctx consolectx.Context, req *model.ServiceSearchReq) (*model.SearchPaginationResult, error) { - pageData, err := manager.PageSearchResourceByConditions[*meshresource.ServiceResource]( +// SearchServicesByKeywords search services by keywords, for now only support accurate search +func SearchServicesByKeywords(ctx consolectx.Context, req *model.ServiceSearchReq) (*model.SearchPaginationResult, error) { + pageData, err := manager.PageListByIndexes[*meshresource.ServiceProviderMetadataResource]( ctx.ResourceManager(), - meshresource.ServiceKind, - []string{"serviceName=" + req.Keywords}, + meshresource.ServiceProviderMetadataKind, + map[string]string{ + index.ByMeshIndex: req.Mesh, + index.ByServiceProviderServiceName: req.Keywords, + }, req.PageReq, ) if err != nil { return nil, err } - searchRespList := slice.Map[*meshresource.ServiceResource, *model.ServiceSearchResp](pageData.Data, - func(_ int, item *meshresource.ServiceResource) *model.ServiceSearchResp { - return toServiceSearchResp(item) + searchRespList := slice.Map(pageData.Data, + func(_ int, item *meshresource.ServiceProviderMetadataResource) *model.ServiceSearchResp { + return ToServiceSearchRespByProvider(item) }) return &model.SearchPaginationResult{ List: searchRespList, @@ -106,51 +142,383 @@ func BannerSearchServices(ctx consolectx.Context, req *model.ServiceSearchReq) ( }, nil } -func toServiceSearchResp(res *meshresource.ServiceResource) *model.ServiceSearchResp { - service := res.Spec +func ToServiceSearchRespByProvider(res *meshresource.ServiceProviderMetadataResource) *model.ServiceSearchResp { return &model.ServiceSearchResp{ - ServiceName: service.Name, - VersionGroups: []model.VersionGroup{ - { - Version: service.Version, - Group: service.Group, + ServiceName: res.Spec.ServiceName, + Group: res.Spec.Group, + Version: res.Spec.Version, + ProviderAppName: res.Spec.ProviderAppName, + } +} + +func ToServiceSearchRespByConsumer(res *meshresource.ServiceConsumerMetadataResource) *model.ServiceSearchResp { + return &model.ServiceSearchResp{ + ServiceName: res.Spec.ServiceName, + Group: res.Spec.Group, + Version: res.Spec.Version, + ConsumerAppName: res.Spec.ConsumerAppName, + } +} + +func GetServiceTimeoutConfig(ctx consolectx.Context, req model.BaseServiceReq) (int32, error) { + serviceConfiguratorName := req.ServiceKey() + constants.ConfiguratorRuleDotSuffix + res, err := GetConfigurator(ctx, serviceConfiguratorName, req.Mesh) + if err != nil { + logger.Errorf("get service configurator %s failed, cause: %v", serviceConfiguratorName, err) + return 0, err + } + if res == nil || res.Spec == nil { + logger.Infof("service configurator %s not found, return default timeout", serviceConfiguratorName) + return constants.ServiceDefaultTimeout, nil + } + timeout := constants.ServiceDefaultTimeout + slice.ForEachWithBreak(res.Spec.Configs, func(_ int, conf *meshproto.OverrideConfig) bool { + t, found := getServiceTimeout(conf) + if found { + timeout = t + return true + } + return found + }) + return timeout, nil +} + +func UpInsertServiceConfigTimeoutConfig(ctx consolectx.Context, req model.BaseServiceReq, timeout int32) error { + serviceConfiguratorName := req.ServiceKey() + constants.ConfiguratorRuleDotSuffix + res, err := GetConfigurator(ctx, serviceConfiguratorName, req.Mesh) + if err != nil { + logger.Errorf("get service configurator %s failed, cause: %v", serviceConfiguratorName, err) + return err + } + // if configurator doesn't exist + if res == nil || res.Spec == nil { + // if timeout config is default value, skip updating + if timeout == constants.ServiceDefaultTimeout { + logger.Infof("service configurator %s not found, timeout config is default value, "+ + "skip updating configurator", serviceConfiguratorName) + return nil + } + // otherwise create a new configurator with timeout config + res = meshresource.NewDynamicConfigResourceWithAttributes(serviceConfiguratorName, req.Mesh) + res.Spec = &meshproto.DynamicConfig{ + Key: req.ServiceName, + Scope: constants.ScopeService, + ConfigVersion: constants.ConfiguratorVersionV3, + Enabled: true, + Configs: []*meshproto.OverrideConfig{ + { + Side: constants.SideProvider, + Parameters: map[string]string{`timeout`: strconv.Itoa(int(timeout))}, + XGenerateByCp: true, + }, }, - }, + } + err = CreateConfigurator(ctx, res) + if err != nil { + logger.Errorf("create service configurator %s failed, cause: %v", serviceConfiguratorName, err) + return err + } + return nil + } + // if configurator exists, match config one by one + for _, conf := range res.Spec.Configs { + oldTimeout, found := getServiceTimeout(conf) + if !found { + continue + } + // if timeout config is same as input, skip updating + if oldTimeout == timeout { + logger.Infof("service configurator %s already exists, timeout config is same as input, "+ + "skip updating configurator", serviceConfiguratorName) + return nil + } + // if timeout config is different from input, update + conf.Parameters[`timeout`] = strconv.Itoa(int(timeout)) + err := UpdateConfigurator(ctx, res) + if err != nil { + logger.Errorf("update service configurator %s failed, cause: %v", serviceConfiguratorName, err) + return err + } + return nil + } + // if timeout config is not found, create a new one + res.Spec.Configs = append(res.Spec.Configs, &meshproto.OverrideConfig{ + Side: constants.SideProvider, + Parameters: map[string]string{`timeout`: strconv.Itoa(int(timeout))}, + XGenerateByCp: true, + }) + err = UpdateConfigurator(ctx, res) + if err != nil { + logger.Errorf("update service configurator %s failed, cause: %v", serviceConfiguratorName, err) + return err } + return nil +} +func getServiceTimeout(conf *meshproto.OverrideConfig) (int32, bool) { + if conf.Side == constants.SideProvider && conf.Parameters != nil && conf.Parameters[`timeout`] != "" { + timeout, err := strconv.Atoi(conf.Parameters[`timeout`]) + if err == nil { + return int32(timeout), true + } + } + return 0, false } -func ToSearchPaginationResult[T any](services []T, data sort.Interface, req coremodel.PageReq) *model.SearchPaginationResult { - res := model.NewSearchPaginationResult() - list := make([]T, 0) +func GetServiceRetryConfig(ctx consolectx.Context, req model.BaseServiceReq) (int32, error) { + serviceConfiguratorName := req.ServiceKey() + constants.ConfiguratorRuleDotSuffix + res, err := GetConfigurator(ctx, serviceConfiguratorName, req.Mesh) + if err != nil { + logger.Errorf("get service configurator %s failed, cause: %v", serviceConfiguratorName, err) + return 0, err + } + if res == nil || res.Spec == nil { + logger.Infof("service configurator %s not found, return default retries", serviceConfiguratorName) + return constants.ServiceDefaultRetries, nil + } + retries := constants.ServiceDefaultRetries + slice.ForEachWithBreak(res.Spec.Configs, func(_ int, conf *meshproto.OverrideConfig) bool { + t, found := getServiceRetryTimes(conf) + if found { + retries = t + return true + } + return found + }) + return retries, nil +} - sort.Sort(data) - lenFilteredItems := len(services) - pageSize := lenFilteredItems - offset := 0 - paginationEnabled := req.PageSize != 0 - if paginationEnabled { - pageSize = req.PageSize - offset = req.PageOffset +func UpInsertServiceRetryConfig(ctx consolectx.Context, req model.BaseServiceReq, retries int32) error { + serviceConfiguratorName := req.ServiceKey() + constants.ConfiguratorRuleDotSuffix + res, err := GetConfigurator(ctx, serviceConfiguratorName, req.Mesh) + if err != nil { + logger.Errorf("get service configurator %s failed, cause: %v", serviceConfiguratorName, err) + return err + } + // if configurator doesn't exist + if res == nil || res.Spec == nil { + // if retries config is default value, skip updating + if retries == constants.ServiceDefaultRetries { + logger.Infof("service configurator %s not found, retries config is default value, "+ + "skip updating configurator", serviceConfiguratorName) + return nil + } + // otherwise create a new configurator with retries config + res = meshresource.NewDynamicConfigResourceWithAttributes(serviceConfiguratorName, req.Mesh) + res.Spec = &meshproto.DynamicConfig{ + Key: req.ServiceName, + Scope: constants.ScopeService, + ConfigVersion: constants.ConfiguratorVersionV3, + Enabled: true, + Configs: []*meshproto.OverrideConfig{ + { + Side: constants.SideConsumer, + Parameters: map[string]string{`retries`: strconv.Itoa(int(retries))}, + XGenerateByCp: true, + }, + }, + } + if err := CreateConfigurator(ctx, res); err != nil { + logger.Errorf("create service configurator %s failed, cause: %v", serviceConfiguratorName, err) + return err + } + return nil + } + // if configurator exists, match config one by one + for _, conf := range res.Spec.Configs { + retryTimes, found := getServiceRetryTimes(conf) + if !found { + continue + } + // if retries config is same as input, skip updating + if retryTimes == retries { + logger.Infof("service configurator %s already exists, retries config is same as input, "+ + "skip updating configurator", serviceConfiguratorName) + return nil + } + // if retries config is different from input, update + conf.Parameters[`retries`] = strconv.Itoa(int(retries)) + if err := UpdateConfigurator(ctx, res); err != nil { + logger.Errorf("update service configurator %s failed, cause: %v", serviceConfiguratorName, err) + return err + } + } + // no retry config found and retries is default value, skip updating + if retries == constants.ServiceDefaultRetries { + logger.Infof("service configurator %s already exists, retries config is default value, "+ + "skip updating configurator", serviceConfiguratorName) + return nil + } + // otherwise create a new one + res.Spec.Configs = append(res.Spec.Configs, &meshproto.OverrideConfig{ + Side: constants.SideConsumer, + Parameters: map[string]string{`retries`: strconv.Itoa(int(retries))}, + XGenerateByCp: true, + }) + if err = UpdateConfigurator(ctx, res); err != nil { + logger.Errorf("update service configurator %s failed, cause: %v", serviceConfiguratorName, err) + return err } + return nil +} - for i := offset; i < offset+pageSize && i < lenFilteredItems; i++ { - list = append(list, services[i]) +func getServiceRetryTimes(conf *meshproto.OverrideConfig) (int32, bool) { + if conf.Side == constants.SideConsumer && conf.Parameters != nil && conf.Parameters[`retries`] != "" { + retries, err := strconv.Atoi(conf.Parameters[`retries`]) + if err == nil { + return int32(retries), true + } } + return 0, false +} - nextOffset := 0 - if paginationEnabled { - if offset+pageSize < lenFilteredItems { // set new offset only if we did not reach the end of the collection - nextOffset = offset + req.PageSize +func GetServiceRegionPriorityConfig(ctx consolectx.Context, req model.BaseServiceReq) (bool, error) { + serviceConditionRuleName := req.ServiceKey() + constants.ConditionRuleDotSuffix + res, err := GetConditionRule(ctx, serviceConditionRuleName, req.Mesh) + if err != nil { + logger.Errorf("get service condition rule %s failed, cause: %v", serviceConditionRuleName, err) + return true, err + } + if res == nil { + return false, nil + } + openSameRegionPrior := false + slice.ForEachWithBreak(res.Spec.Conditions, func(_ int, condition string) bool { + openSameRegionPrior = isServiceSameRegion(condition) + return openSameRegionPrior + }) + return openSameRegionPrior, nil +} + +func UpInsertServiceRegionPriorityConfig(ctx consolectx.Context, req model.BaseServiceReq, enabled bool) error { + serviceConditionRuleName := req.ServiceKey() + constants.ConditionRuleSuffix + res, err := GetConditionRule(ctx, serviceConditionRuleName, req.Mesh) + if err != nil { + logger.Errorf("get service condition rule %s failed, cause: %v", serviceConditionRuleName, err) + return err + } + // if condition rule doesn't exist + if res == nil || res.Spec == nil { + // if same region priority is needed to disable, skip updating + if !enabled { + logger.Infof("service condition rule %s not found, and same region priority is needed to disable, "+ + "skip updating condition rule", serviceConditionRuleName) + return nil + } + // otherwise create a new condition rule + res := meshresource.NewConditionRouteResourceWithAttributes(serviceConditionRuleName, req.Mesh) + res.Spec = &meshproto.ConditionRoute{ + ConfigVersion: "v3.0", + Priority: 0, + Enabled: true, + Force: false, + Runtime: true, + Key: req.ServiceName, + Scope: constants.ScopeService, + Conditions: []string{"=>region=$region"}, + } + if err := CreateConditionRule(ctx, res); err != nil { + logger.Errorf("create service condition rule %s failed, cause: %v", serviceConditionRuleName, err) + return err + } + return nil + } + // if condition rule exists, match condition one by one + for i, condition := range res.Spec.Conditions { + isSameRegion := isServiceSameRegion(condition) + if !isSameRegion { + continue } + // if same region priority is needed to enable, and condition is already enabled, skip updating + if enabled { + logger.Infof("same region prior is already opened, skip updating service condition rule %s", serviceConditionRuleName) + return nil + } + // otherwise we need to remove the condition and update condition rule + res.Spec.Conditions = slice.Concat(res.Spec.Conditions[:i], res.Spec.Conditions[i+1:]) + if err := UpdateConditionRule(ctx, res); err != nil { + logger.Errorf("update service condition rule %s failed, cause: %v", serviceConditionRuleName, err) + return err + } + return nil + } + // no same region priority found and region priority is needed to disable, skip updating + if !enabled { + logger.Infof("enabled is false and same region prior config is not exists, "+ + "skip updating service condition rule %s", serviceConditionRuleName) + return nil + } + // otherwise create a new condition + res.Spec.Conditions = append(res.Spec.Conditions, "=>region=$region") + if err := UpdateConditionRule(ctx, res); err != nil { + logger.Errorf("update service condition rule %s failed, cause: %v", serviceConditionRuleName, err) + return err } + return nil +} + +func isServiceSameRegion(condition string) bool { + c := strings.TrimSpace(condition) + return strings.Contains(c, "=>region=$region") +} - res.List = list - res.PageInfo = coremodel.Pagination{ - Total: lenFilteredItems, - PageSize: req.PageSize, - PageOffset: nextOffset, +func GetServiceArgumentRouteConfig(ctx consolectx.Context, req model.BaseServiceReq) (*model.ServiceArgumentRoute, error) { + serviceConditionRuleName := req.ServiceKey() + constants.ConditionRuleDotSuffix + rawRes, err := GetConditionRule(ctx, serviceConditionRuleName, req.Mesh) + if err != nil { + logger.Errorf("get service condition rule %s failed, cause: %v", serviceConditionRuleName, err) + return nil, err } + if rawRes == nil || rawRes.Spec == nil { + return nil, nil + } + argumentRoutes := slice.Map(rawRes.Spec.Conditions, func(index int, condition string) model.ServiceArgument { + return model.ParseConditionExpression(condition) + }) + return &model.ServiceArgumentRoute{ + Routes: argumentRoutes, + }, nil +} - return res +func UpInsertServiceArgumentRouteConfig(ctx consolectx.Context, req model.BaseServiceReq, route model.ServiceArgumentRoute) error { + serviceConditionRuleName := req.ServiceKey() + constants.ConditionRuleDotSuffix + conditionRouteRes, err := GetConditionRule(ctx, serviceConditionRuleName, req.Mesh) + if err != nil { + logger.Errorf("get service condition rule %s failed, cause: %v", serviceConditionRuleName, err) + return err + } + if conditionRouteRes == nil { + conditionRouteRes = meshresource.NewConditionRouteResourceWithAttributes(serviceConditionRuleName, req.Mesh) + conditionRouteRes.Spec.Conditions = make([]string, 0) + } + conditions := slice.Filter(conditionRouteRes.Spec.Conditions, func(index int, condition string) bool { + return !isArgumentRoute(condition) + }) + conditions = slice.Concat(conditions, + slice.Map(route.Routes, func(index int, item model.ServiceArgument) string { + return item.ToExpression() + })) + conditionRouteRes.Spec = &meshproto.ConditionRoute{ + ConfigVersion: "v3.0", + Priority: 0, + Enabled: true, + Force: false, + Runtime: true, + Key: req.ServiceName, + Scope: constants.ScopeService, + Conditions: conditions, + } + if err = UpdateConditionRule(ctx, conditionRouteRes); err != nil { + logger.Errorf("create service condition rule %s failed, cause: %v", serviceConditionRuleName, err) + return err + } + return nil +} + +// isArgumentRoute judge whether the condition is argument route +func isArgumentRoute(condition string) bool { + if strings.Contains(condition, "method") { + return true + } + return false } diff --git a/pkg/console/service/tag_rule.go b/pkg/console/service/tag_rule.go index 7c4048feb..0ab9e81e5 100644 --- a/pkg/console/service/tag_rule.go +++ b/pkg/console/service/tag_rule.go @@ -18,13 +18,87 @@ package service import ( + "github.com/duke-git/lancet/v2/slice" + + "github.com/apache/dubbo-admin/pkg/common/bizerror" consolectx "github.com/apache/dubbo-admin/pkg/console/context" + "github.com/apache/dubbo-admin/pkg/console/model" "github.com/apache/dubbo-admin/pkg/core/logger" "github.com/apache/dubbo-admin/pkg/core/manager" meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" + "github.com/apache/dubbo-admin/pkg/core/store/index" ) +func PageListTagRule(ctx consolectx.Context, req *model.SearchReq) (*model.SearchPaginationResult, error) { + pageData, err := manager.PageListByIndexes[*meshresource.TagRouteResource]( + ctx.ResourceManager(), + meshresource.TagRouteKind, + map[string]string{ + index.ByMeshIndex: req.Mesh, + }, + req.PageReq) + if err != nil { + logger.Errorf("search tag rule error: %v", err) + return nil, bizerror.New(bizerror.InternalError, "search tag rule failed, please try again") + } + if pageData.Data == nil || len(pageData.Data) == 0 { + return &model.SearchPaginationResult{ + List: nil, + PageInfo: coremodel.Pagination{ + Total: 0, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil + } + respList := slice.Map(pageData.Data, func(_ int, item *meshresource.TagRouteResource) *model.TagRuleSearchResp { + return &model.TagRuleSearchResp{ + CreateTime: "", + Enabled: item.Spec.Enabled, + RuleName: item.Name, + } + }) + return &model.SearchPaginationResult{ + List: respList, + PageInfo: pageData.Pagination, + }, nil +} + +// SearchTagRuleByKeywords for now, only accurate search is supported +func SearchTagRuleByKeywords(ctx consolectx.Context, req *model.SearchReq) (*model.SearchPaginationResult, error) { + resKey := coremodel.BuildResourceKey(req.Mesh, req.Keywords) + tagRuleRes, exists, err := manager.GetByKey[*meshresource.TagRouteResource](ctx.ResourceManager(), meshresource.TagRouteKind, resKey) + if err != nil { + logger.Errorf("search tag rule error: %v", err) + return nil, bizerror.New(bizerror.InternalError, "search tag rule failed, please try again") + } + if !exists { + return &model.SearchPaginationResult{ + List: nil, + PageInfo: coremodel.Pagination{ + Total: 0, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil + } + return &model.SearchPaginationResult{ + List: []*model.TagRuleSearchResp{ + { + CreateTime: "", + Enabled: tagRuleRes.Spec.Enabled, + RuleName: tagRuleRes.Name, + }, + }, + PageInfo: coremodel.Pagination{ + Total: 1, + PageSize: req.PageReq.PageSize, + PageOffset: req.PageReq.PageOffset, + }, + }, nil +} + func GetTagRule(ctx consolectx.Context, name string, mesh string) (*meshresource.TagRouteResource, error) { res, _, err := manager.GetByKey[*meshresource.TagRouteResource]( ctx.ResourceManager(), @@ -56,7 +130,7 @@ func CreateTagRule(ctx consolectx.Context, res *meshresource.TagRouteResource) e } func DeleteTagRule(ctx consolectx.Context, name string, mesh string) error { - err := ctx.ResourceManager().DeleteByKey(meshresource.TagRouteKind, coremodel.BuildResourceKey(mesh, name)) + err := ctx.ResourceManager().DeleteByKey(meshresource.TagRouteKind, mesh, coremodel.BuildResourceKey(mesh, name)) if err != nil { logger.Warnf("delete tag rule %s error: %v", name, err) return err diff --git a/pkg/console/util/error.go b/pkg/console/util/error.go index a4dcab169..9bbda2cf5 100644 --- a/pkg/console/util/error.go +++ b/pkg/console/util/error.go @@ -19,6 +19,7 @@ package util import ( "errors" + "fmt" "net/http" "github.com/gin-gonic/gin" @@ -39,3 +40,8 @@ func HandleArgumentError(ctx *gin.Context, err error) { e := bizerror.New(bizerror.InvalidArgument, err.Error()) ctx.JSON(http.StatusOK, model.NewBizErrorResp(e)) } + +func HandleNotFoundError(ctx *gin.Context, resName string) { + e := bizerror.New(bizerror.NotFoundError, fmt.Sprintf("%s not found", resName)) + ctx.JSON(http.StatusOK, model.NewBizErrorResp(e)) +} diff --git a/pkg/core/bootstrap/bootstrap.go b/pkg/core/bootstrap/bootstrap.go index f3ef6f0d4..b07376edb 100644 --- a/pkg/core/bootstrap/bootstrap.go +++ b/pkg/core/bootstrap/bootstrap.go @@ -110,6 +110,7 @@ func (sb *SmartBootstrapper) gatherComponents() ([]runtime.Component, error) { {"ResourceEngine", runtime.ComponentRegistry().ResourceEngine}, {"ResourceManager", runtime.ComponentRegistry().ResourceManager}, {"Console", runtime.ComponentRegistry().Console}, + {"RuleGovernor", runtime.ComponentRegistry().RuleGovernor}, } for _, comp := range coreComps { diff --git a/pkg/core/bootstrap/init.go b/pkg/core/bootstrap/init.go index 3ed089434..21bf1133a 100644 --- a/pkg/core/bootstrap/init.go +++ b/pkg/core/bootstrap/init.go @@ -24,6 +24,7 @@ import ( _ "github.com/apache/dubbo-admin/pkg/core/discovery" _ "github.com/apache/dubbo-admin/pkg/core/engine" _ "github.com/apache/dubbo-admin/pkg/core/events" + _ "github.com/apache/dubbo-admin/pkg/core/governor" _ "github.com/apache/dubbo-admin/pkg/core/manager" _ "github.com/apache/dubbo-admin/pkg/core/store" _ "github.com/apache/dubbo-admin/pkg/discovery/mock" @@ -31,6 +32,8 @@ import ( _ "github.com/apache/dubbo-admin/pkg/discovery/zk" _ "github.com/apache/dubbo-admin/pkg/engine/kubernetes" _ "github.com/apache/dubbo-admin/pkg/engine/mock" + _ "github.com/apache/dubbo-admin/pkg/governor/nacos2" + _ "github.com/apache/dubbo-admin/pkg/governor/zk" _ "github.com/apache/dubbo-admin/pkg/store/memory" _ "github.com/apache/dubbo-admin/pkg/store/mysql" _ "github.com/apache/dubbo-admin/pkg/store/postgres" diff --git a/pkg/core/clients/nacos.go b/pkg/core/clients/nacos.go new file mode 100644 index 000000000..44b84d1b4 --- /dev/null +++ b/pkg/core/clients/nacos.go @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 clients + +import ( + "fmt" + + dubbogocom "dubbo.apache.org/dubbo-go/v3/common" + dubbogoconstant "dubbo.apache.org/dubbo-go/v3/common/constant" + dubbogonacos "dubbo.apache.org/dubbo-go/v3/remoting/nacos" + nacosconfigclient "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client" + nacosnamingclient "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client" + + "github.com/apache/dubbo-admin/pkg/common/bizerror" +) + +// CreateNacosClients creates nacos clients by url +// format: nacos://host:port?username=${uname}&password=${pwd} +func CreateNacosClients(url string) (nacosconfigclient.IConfigClient, nacosnamingclient.INamingClient, error) { + nacosUrl, err := dubbogocom.NewURL(url) + if err != nil { + return nil, nil, err + } + nacosUrl.AddParam(dubbogoconstant.ClientNameKey, url) + configClient, err := dubbogonacos.NewNacosConfigClientByUrl(nacosUrl) + if err != nil { + return nil, nil, bizerror.Wrap(err, bizerror.NacosError, + fmt.Sprintf("cannot create nacos config client for %s", url)) + } + namingClient, err := dubbogonacos.NewNacosClientByURL(nacosUrl) + if err != nil { + return nil, nil, bizerror.Wrap(err, bizerror.NacosError, + fmt.Sprintf("cannot create nacos naming client for %s", url)) + } + return configClient.Client(), namingClient.Client(), nil +} diff --git a/pkg/core/clients/zk.go b/pkg/core/clients/zk.go new file mode 100644 index 000000000..866d2b517 --- /dev/null +++ b/pkg/core/clients/zk.go @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 clients + +import ( + "fmt" + "net/url" + "time" + + "github.com/dubbogo/go-zookeeper/zk" + + "github.com/apache/dubbo-admin/pkg/common/bizerror" + "github.com/apache/dubbo-admin/pkg/core/logger" +) + +type ZKLogger struct{} + +func (z *ZKLogger) Printf(s string, i ...interface{}) { + logger.Debugf(s, i...) +} + +// NewZKConnection creates a new zookeeper connection +// address format: zookeeper://host:port +func NewZKConnection(address string) (*zk.Conn, error) { + zkUrl, err := url.Parse(address) + if err != nil { + return nil, bizerror.Wrap(err, bizerror.ZKError, + fmt.Sprintf("cannot parse url for zookeeper, url: %s", address)) + } + conn, _, err := zk.Connect([]string{zkUrl.Host}, time.Second*1, func(c *zk.Conn) { + c.SetLogger(&ZKLogger{}) + }) + if err != nil { + logger.Errorf("cannot connect to zookeeper, url: %s", address) + return nil, bizerror.Wrap(err, bizerror.ZKError, + fmt.Sprintf("cannot connect to zookeeper, url: %s", address)) + } + return conn, nil +} diff --git a/pkg/core/controller/informer.go b/pkg/core/controller/informer.go index 79a12c2ca..92cdb6938 100644 --- a/pkg/core/controller/informer.go +++ b/pkg/core/controller/informer.go @@ -66,6 +66,13 @@ type Options struct { ResyncPeriod time.Duration } +func ResourceKeyFunc(obj interface{}) (string, error) { + if r, ok := obj.(model.Resource); ok { + return r.ResourceKey(), nil + } + return "", bizerror.NewAssertionError("Resource", reflect.TypeOf(obj).Name()) +} + // informer implements Informer and has three // main components. One is the cache.Indexer which provides curd operations for objects. // The second main component is a cache.Controller that pulls diff --git a/pkg/core/discovery/component.go b/pkg/core/discovery/component.go index 68358e724..6cd27203f 100644 --- a/pkg/core/discovery/component.go +++ b/pkg/core/discovery/component.go @@ -132,21 +132,6 @@ func (d *discoveryComponent) Start(_ runtime.Runtime, ch <-chan struct{}) error return nil } -func (d *discoveryComponent) Add(resource coremodel.Resource) error { - //TODO implement me - panic("implement me") -} - -func (d *discoveryComponent) Update(resource coremodel.Resource) error { - //TODO implement me - panic("implement me") -} - -func (d *discoveryComponent) Delete(resource coremodel.Resource) error { - //TODO implement me - panic("implement me") -} - func (d *discoveryComponent) initInformers(cfg *discovery.Config, storeRouter store.Router, eventBus events.EventBus) (Informers, error) { factory, err := ListWatcherFactoryRegistry().GetListWatcherFactory(cfg.Type) if err != nil { @@ -194,8 +179,8 @@ func (d *discoveryComponent) initSubscribes(storeRouter store.Router, emitter ev } serviceConsumerMetadataSub := subscriber.NewServiceConsumerMetadataEventSubscriber(appStore, emitter) serviceProviderMetadataSub := subscriber.NewServiceProviderMetadataEventSubscriber(appStore, emitter) - - d.subscribers = append(d.subscribers, rpcInstanceSub, serviceConsumerMetadataSub, serviceProviderMetadataSub) + instanceSub := subscriber.NewInstanceEventSubscriber(appStore, instanceStore, emitter) + d.subscribers = append(d.subscribers, rpcInstanceSub, serviceConsumerMetadataSub, serviceProviderMetadataSub, instanceSub) // if there is a nacos discovery, a NacosServiceEventSubscriber is needed _, hasNacosDiscovery := slice.FindBy(d.configs, func(index int, item *discovery.Config) bool { diff --git a/pkg/core/discovery/discovery.go b/pkg/core/discovery/discovery.go index 172b27d87..95971d4d7 100644 --- a/pkg/core/discovery/discovery.go +++ b/pkg/core/discovery/discovery.go @@ -17,21 +17,5 @@ package discovery -import ( - "github.com/apache/dubbo-admin/pkg/core/resource/model" -) - -// Operations are the operations which can be called by other components` -type Operations interface { - // Add adds a resource to the registry - Add(model.Resource) error - // Update updates a resource in the registry - Update(model.Resource) error - // Delete deletes a resource from the registry - Delete(model.Resource) error -} - // ResourceDiscovery is the component which discovers the rpc services and save them into store.ResourceStore -type ResourceDiscovery interface { - Operations -} +type ResourceDiscovery interface{} diff --git a/pkg/core/discovery/subscriber/instance.go b/pkg/core/discovery/subscriber/instance.go new file mode 100644 index 000000000..997144039 --- /dev/null +++ b/pkg/core/discovery/subscriber/instance.go @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 subscriber + +import ( + "k8s.io/client-go/tools/cache" + + meshproto "github.com/apache/dubbo-admin/api/mesh/v1alpha1" + "github.com/apache/dubbo-admin/pkg/common/bizerror" + "github.com/apache/dubbo-admin/pkg/core/events" + "github.com/apache/dubbo-admin/pkg/core/logger" + meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" + coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" + "github.com/apache/dubbo-admin/pkg/core/store" + "github.com/apache/dubbo-admin/pkg/core/store/index" +) + +type InstanceEventSubscriber struct { + appStore store.ResourceStore + instanceStore store.ResourceStore + emitter events.Emitter +} + +func NewInstanceEventSubscriber( + appStore store.ResourceStore, + instanceStore store.ResourceStore, + emitter events.Emitter) *InstanceEventSubscriber { + return &InstanceEventSubscriber{ + appStore: appStore, + instanceStore: instanceStore, + emitter: emitter, + } +} + +func (s *InstanceEventSubscriber) ResourceKind() coremodel.ResourceKind { + return meshresource.InstanceKind +} + +func (s *InstanceEventSubscriber) Name() string { + return "Discovery-" + s.ResourceKind().ToString() +} + +func (s *InstanceEventSubscriber) ProcessEvent(event events.Event) error { + newObj, ok := event.NewObj().(*meshresource.InstanceResource) + if !ok && event.NewObj() != nil { + return bizerror.NewAssertionError(meshresource.InstanceKind, event.NewObj()) + } + oldObj, ok := event.OldObj().(*meshresource.InstanceResource) + if !ok && event.OldObj() != nil { + return bizerror.NewAssertionError(meshresource.InstanceKind, event.OldObj()) + } + var instanceRes *meshresource.InstanceResource + if newObj != nil { + instanceRes = newObj + } else { + instanceRes = oldObj + } + instanceResList, err := s.instanceStore.ListByIndexes(map[string]string{ + index.ByInstanceAppNameIndex: instanceRes.Spec.AppName, + }) + + appResKey := coremodel.BuildResourceKey(instanceRes.Mesh, instanceRes.Spec.AppName) + res, exists, err := s.appStore.GetByKey(appResKey) + if err != nil { + return err + } + if !exists { + appRes := BuildAppResource(instanceRes.Mesh, instanceRes.Spec.AppName, int64(len(instanceResList))) + err := s.appStore.Add(appRes) + if err != nil { + logger.Errorf("add app resource failed, app: %s, err: %s", appResKey, err.Error()) + return err + } + s.emitter.Send(events.NewResourceChangedEvent(cache.Added, nil, appRes)) + return nil + } + appRes, ok := res.(*meshresource.ApplicationResource) + if !ok { + return bizerror.NewAssertionError(meshresource.ApplicationKind, res) + } + appRes.Spec.InstanceCount = int64(len(instanceResList)) + err = s.appStore.Update(appRes) + if err != nil { + logger.Errorf("update app resource failed, app: %s, err: %s", appResKey, err.Error()) + return err + } + return nil +} + +func BuildAppResource(mesh string, name string, instanceCount int64) *meshresource.ApplicationResource { + appRes := meshresource.NewApplicationResourceWithAttributes(name, mesh) + appRes.Spec = &meshproto.Application{ + Name: name, + InstanceCount: instanceCount, + } + return appRes +} diff --git a/pkg/core/discovery/subscriber/nacos_service.go b/pkg/core/discovery/subscriber/nacos_service.go index e6776d3f5..6a83771d3 100644 --- a/pkg/core/discovery/subscriber/nacos_service.go +++ b/pkg/core/discovery/subscriber/nacos_service.go @@ -163,8 +163,13 @@ func (n *NacosServiceEventSubscriber) processConsumerMetadataUpsert(serviceRes * n.emitter.Send(events.NewResourceChangedEvent(cache.Added, nil, item)) }) // Find consumers need to update and update them - updateConsumers := maputil.Intersect(newConsumers, oldConsumers) - slice.ForEach(maputil.Values(updateConsumers), func(_ int, item *meshresource.ServiceConsumerMetadataResource) { + updateConsumers := make([]*meshresource.ServiceConsumerMetadataResource, 0) + for k, consumer := range newConsumers { + if _, exists := oldConsumers[k]; exists { + updateConsumers = append(updateConsumers, consumer) + } + } + slice.ForEach(updateConsumers, func(_ int, item *meshresource.ServiceConsumerMetadataResource) { err := st.Update(item) if err != nil { logger.Errorf("update service consumer metadata %s failed, cause: %v", item.ResourceKey(), err) @@ -242,8 +247,13 @@ func (n *NacosServiceEventSubscriber) processRPCInstanceUpsert(serviceRes *meshr n.emitter.Send(events.NewResourceChangedEvent(cache.Added, nil, item)) }) // find instances need to update and update them - updateInstances := maputil.Intersect(newInstances, oldInstances) - slice.ForEach(maputil.Values(updateInstances), func(_ int, item *meshresource.RPCInstanceResource) { + updateInstances := make([]*meshresource.RPCInstanceResource, 0) + for key, instance := range newInstances { + if _, exists := oldInstances[key]; exists { + updateInstances = append(updateInstances, instance) + } + } + slice.ForEach(updateInstances, func(_ int, item *meshresource.RPCInstanceResource) { err := st.Update(item) if err != nil { logger.Errorf("update rpc instance %s failed, cause: %v", item.ResourceKey(), err) @@ -252,7 +262,7 @@ func (n *NacosServiceEventSubscriber) processRPCInstanceUpsert(serviceRes *meshr n.emitter.Send(events.NewResourceChangedEvent(cache.Updated, item, item)) }) logger.Debugf("process rpc instance upsert event, oldInstances: %s, newInstances: %s, offlineInstances: %s, addInstances: %s, updateInstances: %s", - maputil.Keys(oldInstances), maputil.Keys(newInstances), maputil.Keys(offlineInstances), maputil.Keys(addInstances), maputil.Keys(updateInstances)) + maputil.Keys(oldInstances), maputil.Keys(newInstances), maputil.Keys(offlineInstances), maputil.Keys(addInstances), updateInstances) return nil } diff --git a/pkg/core/engine/component.go b/pkg/core/engine/component.go index 818fa8d11..992eec8ac 100644 --- a/pkg/core/engine/component.go +++ b/pkg/core/engine/component.go @@ -78,10 +78,10 @@ func (e *engineComponent) Order() int { func (e *engineComponent) Init(ctx runtime.BuilderContext) error { cfg := ctx.Config().Engine - e.name = cfg.Name + e.name = cfg.ID eventBusComponent, err := ctx.GetActivatedComponent(runtime.EventBus) if err != nil { - return fmt.Errorf("can not retrieve event bus from runtime in engine %s, %w", cfg.Name, err) + return fmt.Errorf("can not retrieve event bus from runtime in engine %s, %w", e.name, err) } eventBus, ok := eventBusComponent.(events.EventBus) if !ok { diff --git a/pkg/core/engine/subscriber/runtime_instance.go b/pkg/core/engine/subscriber/runtime_instance.go index 8c0528ecc..6038462c0 100644 --- a/pkg/core/engine/subscriber/runtime_instance.go +++ b/pkg/core/engine/subscriber/runtime_instance.go @@ -144,7 +144,7 @@ func (s *RuntimeInstanceEventSubscriber) processDelete(rtInstanceRes *meshresour logger.Warnf("cannot find instance resource by runtime instance %s, skipped deleting instance", rtInstanceRes.ResourceKey()) return nil } - if err = s.instanceStore.Delete(instanceResource.ResourceKey()); err != nil { + if err = s.instanceStore.Delete(instanceResource); err != nil { logger.Errorf("delete instance resource failed, instance: %s, err: %s", instanceResource.ResourceKey(), err.Error()) return err } diff --git a/pkg/core/governor/component.go b/pkg/core/governor/component.go new file mode 100644 index 000000000..323cba04b --- /dev/null +++ b/pkg/core/governor/component.go @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 governor + +import ( + "fmt" + "math" + "reflect" + + "github.com/apache/dubbo-admin/pkg/common/bizerror" + "github.com/apache/dubbo-admin/pkg/config/discovery" + "github.com/apache/dubbo-admin/pkg/core/events" + coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" + "github.com/apache/dubbo-admin/pkg/core/runtime" + "github.com/apache/dubbo-admin/pkg/core/store" +) + +func init() { + runtime.RegisterComponent(newGovernorComponent()) +} + +type Router interface { + ResourceRoute(r coremodel.Resource) (RuleGovernor, error) + ResourceMeshRoute(mesh string) (RuleGovernor, error) +} + +type Component interface { + runtime.Component + Router +} + +var _ Component = &ruleGovernorComponent{} + +type ruleGovernorComponent struct { + configs []*discovery.Config + governors map[string]RuleGovernor +} + +func newGovernorComponent() Component { + return &ruleGovernorComponent{ + governors: make(map[string]RuleGovernor), + } +} + +func (g *ruleGovernorComponent) Type() runtime.ComponentType { + return runtime.RuleGovernor +} + +func (g *ruleGovernorComponent) Order() int { + return math.MaxInt - 2 +} + +func (g *ruleGovernorComponent) RequiredDependencies() []runtime.ComponentType { + return []runtime.ComponentType{ + runtime.EventBus, + runtime.ResourceStore, + } +} +func (g *ruleGovernorComponent) Init(ctx runtime.BuilderContext) error { + eventBusComponent, err := ctx.GetActivatedComponent(runtime.EventBus) + if err != nil { + return bizerror.New(bizerror.UnknownError, + fmt.Sprintf("can not retrieve event bus from runtime in discovery, %s", err)) + } + eventBus, ok := eventBusComponent.(events.EventBus) + if !ok { + return bizerror.NewAssertionError("EventBus", reflect.TypeOf(eventBusComponent).Name()) + } + + storeComponent, err := ctx.GetActivatedComponent(runtime.ResourceStore) + if err != nil { + return bizerror.New(bizerror.UnknownError, + fmt.Sprintf("can not retrieve store from runtime in discovery, %s", err)) + } + storeRouter, ok := storeComponent.(store.Router) + if !ok { + return bizerror.NewAssertionError("store.Router", reflect.TypeOf(storeComponent).Name()) + } + g.configs = ctx.Config().Discovery + for _, cfg := range g.configs { + factory, err := FactoryRegistry().GetGovernorFactory(cfg.Type) + if err != nil { + return err + } + governor, err := factory.New(cfg.ID, cfg, storeRouter, eventBus) + if err != nil { + return err + } + g.governors[cfg.ID] = governor + } + return nil +} + +func (g *ruleGovernorComponent) Start(_ runtime.Runtime, _ <-chan struct{}) error { + return nil +} + +func (g *ruleGovernorComponent) ResourceRoute(r coremodel.Resource) (RuleGovernor, error) { + return g.ResourceMeshRoute(r.ResourceMesh()) +} + +func (g *ruleGovernorComponent) ResourceMeshRoute(mesh string) (RuleGovernor, error) { + if governor, exists := g.governors[mesh]; exists { + return governor, nil + } + return nil, bizerror.New(bizerror.GovernorError, fmt.Sprintf("mesh %s not found for governor", mesh)) +} diff --git a/pkg/core/governor/factory.go b/pkg/core/governor/factory.go new file mode 100644 index 000000000..75364546e --- /dev/null +++ b/pkg/core/governor/factory.go @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 governor + +import ( + "fmt" + + discoverycfg "github.com/apache/dubbo-admin/pkg/config/discovery" + "github.com/apache/dubbo-admin/pkg/core/events" + "github.com/apache/dubbo-admin/pkg/core/store" +) + +var factoryRegistry = newGovernorFactoryRegistry() + +func RegisterFactory(f Factory) { + factoryRegistry.Register(f) +} + +func FactoryRegistry() Registry { + return factoryRegistry +} + +// Factory is the interface for create a specific type of RuleGovernor +type Factory interface { + // Support returns true if the factory supports the given type in config + Support(t discoverycfg.Type) bool + // New returns a new RuleGovernor for the mesh using the given config and other components + New(mesh string, config *discoverycfg.Config, router store.Router, emitter events.Emitter) (RuleGovernor, error) +} + +type Registry interface { + GetGovernorFactory(discoverycfg.Type) (Factory, error) +} + +type RegistryMutator interface { + // Register registers a new factory + Register(Factory) +} + +type MutableRegistry interface { + Registry + RegistryMutator +} + +var _ MutableRegistry = &governorFactoryRegistry{} + +type governorFactoryRegistry struct { + factories []Factory +} + +func newGovernorFactoryRegistry() MutableRegistry { + return &governorFactoryRegistry{ + factories: make([]Factory, 0), + } +} + +func (g *governorFactoryRegistry) GetGovernorFactory(t discoverycfg.Type) (Factory, error) { + for _, factory := range g.factories { + if factory.Support(t) { + return factory, nil + } + } + return nil, fmt.Errorf("governor type %s not supported", t) +} + +func (g *governorFactoryRegistry) Register(factory Factory) { + g.factories = append(g.factories, factory) +} diff --git a/pkg/core/governor/governor.go b/pkg/core/governor/governor.go new file mode 100644 index 000000000..d29fd156a --- /dev/null +++ b/pkg/core/governor/governor.go @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 governor + +import ( + set "github.com/duke-git/lancet/v2/datastructure/set" + + meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" + "github.com/apache/dubbo-admin/pkg/core/resource/model" +) + +var RuleResourceKinds = set.New(meshresource.DynamicConfigKind, meshresource.ConditionRouteKind, meshresource.TagRouteKind) + +// RuleGovernor makes the rule operations effective +type RuleGovernor interface { + // CreateRule creates a resource in the registry + CreateRule(model.Resource) error + // UpdateRule updates a resource in the registry + UpdateRule(model.Resource) error + // DeleteRule deletes a resource from the registry + DeleteRule(model.Resource) error +} diff --git a/pkg/core/manager/component.go b/pkg/core/manager/component.go index ae7d70069..661ef9fae 100644 --- a/pkg/core/manager/component.go +++ b/pkg/core/manager/component.go @@ -18,10 +18,10 @@ package manager import ( + "fmt" "math" - "github.com/pkg/errors" - + "github.com/apache/dubbo-admin/pkg/core/governor" "github.com/apache/dubbo-admin/pkg/core/runtime" "github.com/apache/dubbo-admin/pkg/core/store" ) @@ -43,7 +43,8 @@ type resourceManagerComponent struct { func (r *resourceManagerComponent) RequiredDependencies() []runtime.ComponentType { return []runtime.ComponentType{ - runtime.ResourceStore, // Manager needs Store to be initialized first + runtime.ResourceStore, + runtime.RuleGovernor, } } @@ -58,9 +59,13 @@ func (r *resourceManagerComponent) Order() int { func (r *resourceManagerComponent) Init(ctx runtime.BuilderContext) error { rsc, err := ctx.GetActivatedComponent(runtime.ResourceStore) if err != nil { - return errors.Wrap(err, "failed to init resource manager") + return fmt.Errorf("failed to init resource manager, cause: %s", err) + } + rgc, err := ctx.GetActivatedComponent(runtime.RuleGovernor) + if err != nil { + return fmt.Errorf("failed to init resource manager, cause: %w", err) } - r.rm = NewResourceManager(rsc.(store.Router)) + r.rm = NewResourceManager(rsc.(store.Router), rgc.(governor.Router)) return nil } diff --git a/pkg/core/manager/manager.go b/pkg/core/manager/manager.go index 2ed0a11fa..44861ca7f 100644 --- a/pkg/core/manager/manager.go +++ b/pkg/core/manager/manager.go @@ -19,7 +19,10 @@ package manager import ( "fmt" + "reflect" + "github.com/apache/dubbo-admin/pkg/common/bizerror" + "github.com/apache/dubbo-admin/pkg/core/governor" "github.com/apache/dubbo-admin/pkg/core/resource/model" "github.com/apache/dubbo-admin/pkg/core/store" ) @@ -27,6 +30,8 @@ import ( type ReadOnlyResourceManager interface { // GetByKey returns the resource with the given resource key GetByKey(rk model.ResourceKind, key string) (r model.Resource, exist bool, err error) + // GetByKeys returns the resources with the given resource keys + GetByKeys(rk model.ResourceKind, keys []string) ([]model.Resource, error) // ListByIndexes returns the resources with the given indexes, indexes is a map of index name and index value ListByIndexes(rk model.ResourceKind, indexes map[string]string) ([]model.Resource, error) // PageListByIndexes page list the resources with the given indexes, indexes is a map of index name and index value @@ -44,7 +49,7 @@ type WriteOnlyResourceManager interface { // Upsert upserts the resource Upsert(r model.Resource) error // DeleteByKey deletes the resource with the given resource key - DeleteByKey(rk model.ResourceKind, key string) error + DeleteByKey(rk model.ResourceKind, mesh string, key string) error } type ResourceManager interface { @@ -53,29 +58,50 @@ type ResourceManager interface { WriteOnlyResourceManager } -func NewResourceManager(router store.Router) ResourceManager { - return &resourcesManager{ - StoreRouter: router, - } -} - var _ ResourceManager = &resourcesManager{} type resourcesManager struct { - StoreRouter store.Router + storeRouter store.Router + governorRouter governor.Router +} + +func NewResourceManager(router store.Router, governorRouter governor.Router) ResourceManager { + return &resourcesManager{ + storeRouter: router, + governorRouter: governorRouter, + } } func (rm *resourcesManager) GetByKey(rk model.ResourceKind, key string) (r model.Resource, exist bool, err error) { - rs, err := rm.StoreRouter.ResourceKindRoute(rk) + rs, err := rm.storeRouter.ResourceKindRoute(rk) if err != nil { return nil, false, err } item, exist, err := rs.GetByKey(key) - return item.(model.Resource), exist, err + if !exist { + return nil, false, nil + } + res, ok := item.(model.Resource) + if !ok { + return nil, false, bizerror.NewAssertionError("Resource", reflect.TypeOf(res).Name()) + } + return res, exist, err +} + +func (rm *resourcesManager) GetByKeys(rk model.ResourceKind, keys []string) ([]model.Resource, error) { + rs, err := rm.storeRouter.ResourceKindRoute(rk) + if err != nil { + return nil, err + } + resources, err := rs.GetByKeys(keys) + if err != nil { + return nil, err + } + return resources, nil } func (rm *resourcesManager) ListByIndexes(rk model.ResourceKind, indexes map[string]string) ([]model.Resource, error) { - rs, err := rm.StoreRouter.ResourceKindRoute(rk) + rs, err := rm.storeRouter.ResourceKindRoute(rk) if err != nil { return nil, err } @@ -91,7 +117,7 @@ func (rm *resourcesManager) PageListByIndexes( indexes map[string]string, pr model.PageReq) (*model.PageData[model.Resource], error) { - rs, err := rm.StoreRouter.ResourceKindRoute(rk) + rs, err := rm.storeRouter.ResourceKindRoute(rk) if err != nil { return nil, err } @@ -108,22 +134,31 @@ func (rm *resourcesManager) PageSearchResourceByConditions(rk model.ResourceKind } func (rm *resourcesManager) Add(r model.Resource) error { - rs, err := rm.StoreRouter.ResourceRoute(r) + if !governor.RuleResourceKinds.Contain(r.ResourceKind()) { + return bizerror.New(bizerror.InvalidArgument, "invalid resource kind") + } + rs, err := rm.governorRouter.ResourceRoute(r) if err != nil { return err } - return rs.Add(r) + return rs.CreateRule(r) } func (rm *resourcesManager) Update(r model.Resource) error { - rs, err := rm.StoreRouter.ResourceRoute(r) + if !governor.RuleResourceKinds.Contain(r.ResourceKind()) { + return bizerror.New(bizerror.InvalidArgument, "invalid resource kind") + } + rs, err := rm.governorRouter.ResourceRoute(r) if err != nil { return err } - return rs.Update(r) + return rs.UpdateRule(r) } func (rm *resourcesManager) Upsert(r model.Resource) error { + if !governor.RuleResourceKinds.Contain(r.ResourceKind()) { + return bizerror.New(bizerror.InvalidArgument, "invalid resource kind") + } if _, exists, _ := rm.GetByKey(r.ResourceKind(), r.ResourceKey()); exists { return rm.Update(r) } else { @@ -131,8 +166,11 @@ func (rm *resourcesManager) Upsert(r model.Resource) error { } } -func (rm *resourcesManager) DeleteByKey(rk model.ResourceKind, key string) error { - rs, err := rm.StoreRouter.ResourceKindRoute(rk) +func (rm *resourcesManager) DeleteByKey(rk model.ResourceKind, mesh string, key string) error { + if !governor.RuleResourceKinds.Contain(rk) { + return bizerror.New(bizerror.InvalidArgument, "invalid resource kind") + } + gov, err := rm.governorRouter.ResourceMeshRoute(mesh) if err != nil { return err } @@ -143,5 +181,5 @@ func (rm *resourcesManager) DeleteByKey(rk model.ResourceKind, key string) error if !exists { return fmt.Errorf("%s %s does not exist", rk, key) } - return rs.Delete(r) + return gov.DeleteRule(r) } diff --git a/pkg/core/manager/manager_helper.go b/pkg/core/manager/manager_helper.go index ff32ce2a3..80e40ba80 100644 --- a/pkg/core/manager/manager_helper.go +++ b/pkg/core/manager/manager_helper.go @@ -40,6 +40,24 @@ func GetByKey[T model.Resource](rm ReadOnlyResourceManager, rk model.ResourceKin return typedResource, true, nil } +func GetByKeys[T model.Resource](rm ReadOnlyResourceManager, rk model.ResourceKind, keys []string) ([]T, error) { + resources, err := rm.GetByKeys(rk, keys) + if err != nil { + return nil, err + } + + typedResources := make([]T, len(resources)) + for i, resource := range resources { + typedResource, ok := resource.(T) + if !ok { + return nil, bizerror.NewAssertionError(rk, reflect.TypeOf(typedResource).Name()) + } + typedResources[i] = typedResource + } + + return typedResources, nil +} + // ListByIndexes is a helper function of ResourceManager.ListByIndexes func ListByIndexes[T model.Resource](rm ReadOnlyResourceManager, rk model.ResourceKind, indexes map[string]string) ([]T, error) { resources, err := rm.ListByIndexes(rk, indexes) diff --git a/pkg/core/resource/apis/mesh/v1alpha1/affinityroute_types.go b/pkg/core/resource/apis/mesh/v1alpha1/affinityroute_types.go index b9a3a2a92..93847a715 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/affinityroute_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/affinityroute_types.go @@ -62,7 +62,7 @@ func (r *AffinityRouteResource) ResourceKind() coremodel.ResourceKind { return AffinityRouteKind } -func (r *AffinityRouteResource) MeshName() string { +func (r *AffinityRouteResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/application_types.go b/pkg/core/resource/apis/mesh/v1alpha1/application_types.go index f7eb45d48..7fe4b414b 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/application_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/application_types.go @@ -62,7 +62,7 @@ func (r *ApplicationResource) ResourceKind() coremodel.ResourceKind { return ApplicationKind } -func (r *ApplicationResource) MeshName() string { +func (r *ApplicationResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/conditionroute_helper.go b/pkg/core/resource/apis/mesh/v1alpha1/conditionroute_helper.go index 90a197ffe..6a79c60c8 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/conditionroute_helper.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/conditionroute_helper.go @@ -20,10 +20,15 @@ package v1alpha1 import ( "sigs.k8s.io/yaml" + "github.com/apache/dubbo-admin/pkg/common/constants" "github.com/apache/dubbo-admin/pkg/core/logger" coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" ) +func BuildConditionRouteResName(scopeEntity string) string { + return scopeEntity + constants.ConditionRuleDotSuffix +} + func ToConditionRouteResource(mesh, name, data string) coremodel.Resource { res := NewConditionRouteResourceWithAttributes(name, mesh) err := yaml.Unmarshal([]byte(data), res.Spec) diff --git a/pkg/core/resource/apis/mesh/v1alpha1/conditionroute_types.go b/pkg/core/resource/apis/mesh/v1alpha1/conditionroute_types.go index 2fe080400..499a01317 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/conditionroute_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/conditionroute_types.go @@ -62,7 +62,7 @@ func (r *ConditionRouteResource) ResourceKind() coremodel.ResourceKind { return ConditionRouteKind } -func (r *ConditionRouteResource) MeshName() string { +func (r *ConditionRouteResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/dynamicconfig_types.go b/pkg/core/resource/apis/mesh/v1alpha1/dynamicconfig_types.go index 26199fbca..a76c5afc9 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/dynamicconfig_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/dynamicconfig_types.go @@ -62,7 +62,7 @@ func (r *DynamicConfigResource) ResourceKind() coremodel.ResourceKind { return DynamicConfigKind } -func (r *DynamicConfigResource) MeshName() string { +func (r *DynamicConfigResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/instance_helper.go b/pkg/core/resource/apis/mesh/v1alpha1/instance_helper.go index 7fa7f0e06..ffc72babe 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/instance_helper.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/instance_helper.go @@ -77,4 +77,5 @@ func MergeRuntimeInstanceIntoInstance( instanceRes.Spec.Node = rtInstanceRes.Spec.Node instanceRes.Spec.Probes = rtInstanceRes.Spec.Probes instanceRes.Spec.Conditions = rtInstanceRes.Spec.Conditions + instanceRes.Spec.SourceEngine = rtInstanceRes.Spec.SourceEngine } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/instance_types.go b/pkg/core/resource/apis/mesh/v1alpha1/instance_types.go index 956fa8c2b..7556b4706 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/instance_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/instance_types.go @@ -62,7 +62,7 @@ func (r *InstanceResource) ResourceKind() coremodel.ResourceKind { return InstanceKind } -func (r *InstanceResource) MeshName() string { +func (r *InstanceResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/nacosconfig_types.go b/pkg/core/resource/apis/mesh/v1alpha1/nacosconfig_types.go index 5df3a5baf..b0d28cf50 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/nacosconfig_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/nacosconfig_types.go @@ -62,7 +62,7 @@ func (r *NacosConfigResource) ResourceKind() coremodel.ResourceKind { return NacosConfigKind } -func (r *NacosConfigResource) MeshName() string { +func (r *NacosConfigResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/nacosservice_types.go b/pkg/core/resource/apis/mesh/v1alpha1/nacosservice_types.go index d5d21ba50..ec4b07730 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/nacosservice_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/nacosservice_types.go @@ -62,7 +62,7 @@ func (r *NacosServiceResource) ResourceKind() coremodel.ResourceKind { return NacosServiceKind } -func (r *NacosServiceResource) MeshName() string { +func (r *NacosServiceResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/rpc_instance_helper.go b/pkg/core/resource/apis/mesh/v1alpha1/rpc_instance_helper.go index 8cca4de6e..b3232cf91 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/rpc_instance_helper.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/rpc_instance_helper.go @@ -53,7 +53,7 @@ func ToRPCInstance( logger.Warnf("parse url params failed, raw url params string: %s, cause: %v", urlParams, err) } releaseVersion = paramsMap[constants.ReleaseKey] - protocol = paramsMap[constants.DubboVersionKey] + protocol = paramsMap[constants.ProtocolKey] serialization = paramsMap[constants.SerializationKey] preferSerialization = paramsMap[constants.PreferSerializationKey] } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/rpcinstance_types.go b/pkg/core/resource/apis/mesh/v1alpha1/rpcinstance_types.go index 57a2af11a..b9eef5930 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/rpcinstance_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/rpcinstance_types.go @@ -62,7 +62,7 @@ func (r *RPCInstanceResource) ResourceKind() coremodel.ResourceKind { return RPCInstanceKind } -func (r *RPCInstanceResource) MeshName() string { +func (r *RPCInstanceResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/rpcinstancemetadata_types.go b/pkg/core/resource/apis/mesh/v1alpha1/rpcinstancemetadata_types.go index 51903c1ac..1f17e9eb3 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/rpcinstancemetadata_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/rpcinstancemetadata_types.go @@ -62,7 +62,7 @@ func (r *RPCInstanceMetadataResource) ResourceKind() coremodel.ResourceKind { return RPCInstanceMetadataKind } -func (r *RPCInstanceMetadataResource) MeshName() string { +func (r *RPCInstanceMetadataResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/runtimeinstance_types.go b/pkg/core/resource/apis/mesh/v1alpha1/runtimeinstance_types.go index c9e72a523..1fbc31ed3 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/runtimeinstance_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/runtimeinstance_types.go @@ -62,7 +62,7 @@ func (r *RuntimeInstanceResource) ResourceKind() coremodel.ResourceKind { return RuntimeInstanceKind } -func (r *RuntimeInstanceResource) MeshName() string { +func (r *RuntimeInstanceResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/service_types.go b/pkg/core/resource/apis/mesh/v1alpha1/service_types.go index b78e1d1a0..74ba69a4b 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/service_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/service_types.go @@ -62,7 +62,7 @@ func (r *ServiceResource) ResourceKind() coremodel.ResourceKind { return ServiceKind } -func (r *ServiceResource) MeshName() string { +func (r *ServiceResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/serviceconsumermetadata_types.go b/pkg/core/resource/apis/mesh/v1alpha1/serviceconsumermetadata_types.go index c041d7afe..44996d896 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/serviceconsumermetadata_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/serviceconsumermetadata_types.go @@ -62,7 +62,7 @@ func (r *ServiceConsumerMetadataResource) ResourceKind() coremodel.ResourceKind return ServiceConsumerMetadataKind } -func (r *ServiceConsumerMetadataResource) MeshName() string { +func (r *ServiceConsumerMetadataResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/serviceprovidermapping_types.go b/pkg/core/resource/apis/mesh/v1alpha1/serviceprovidermapping_types.go index e43c7e623..903b0d98d 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/serviceprovidermapping_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/serviceprovidermapping_types.go @@ -62,7 +62,7 @@ func (r *ServiceProviderMappingResource) ResourceKind() coremodel.ResourceKind { return ServiceProviderMappingKind } -func (r *ServiceProviderMappingResource) MeshName() string { +func (r *ServiceProviderMappingResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/serviceprovidermetadata_types.go b/pkg/core/resource/apis/mesh/v1alpha1/serviceprovidermetadata_types.go index a791d1034..840bcd52d 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/serviceprovidermetadata_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/serviceprovidermetadata_types.go @@ -62,7 +62,7 @@ func (r *ServiceProviderMetadataResource) ResourceKind() coremodel.ResourceKind return ServiceProviderMetadataKind } -func (r *ServiceProviderMetadataResource) MeshName() string { +func (r *ServiceProviderMetadataResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/tagroute_types.go b/pkg/core/resource/apis/mesh/v1alpha1/tagroute_types.go index adcd6439d..90c82be3d 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/tagroute_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/tagroute_types.go @@ -62,7 +62,7 @@ func (r *TagRouteResource) ResourceKind() coremodel.ResourceKind { return TagRouteKind } -func (r *TagRouteResource) MeshName() string { +func (r *TagRouteResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/zkconfig_types.go b/pkg/core/resource/apis/mesh/v1alpha1/zkconfig_types.go index 84490d361..700f2883a 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/zkconfig_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/zkconfig_types.go @@ -62,7 +62,7 @@ func (r *ZKConfigResource) ResourceKind() coremodel.ResourceKind { return ZKConfigKind } -func (r *ZKConfigResource) MeshName() string { +func (r *ZKConfigResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/apis/mesh/v1alpha1/zkmetadata_types.go b/pkg/core/resource/apis/mesh/v1alpha1/zkmetadata_types.go index 9581ffda6..5190de16e 100644 --- a/pkg/core/resource/apis/mesh/v1alpha1/zkmetadata_types.go +++ b/pkg/core/resource/apis/mesh/v1alpha1/zkmetadata_types.go @@ -62,7 +62,7 @@ func (r *ZKMetadataResource) ResourceKind() coremodel.ResourceKind { return ZKMetadataKind } -func (r *ZKMetadataResource) MeshName() string { +func (r *ZKMetadataResource) ResourceMesh() string { return r.Mesh } diff --git a/pkg/core/resource/model/resource.go b/pkg/core/resource/model/resource.go index f4af8f69f..1ff3485b8 100644 --- a/pkg/core/resource/model/resource.go +++ b/pkg/core/resource/model/resource.go @@ -21,25 +21,8 @@ import ( k8smeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8sruntime "k8s.io/apimachinery/pkg/runtime" -) - -const ( - DefaultMesh = "default" - // NoMesh defines a marker that resource is not bound to a Mesh. - // Resources not bound to a mesh (ScopeGlobal) should have an empty string in Mesh field. - NoMesh = "" -) - -const separator = "/" -const ( - ExtensionsImageKey = "image" - ExtensionsPodPhaseKey = "podPhase" - ExtensionsPodStatusKey = "podStatus" - ExtensionsContainerStatusReasonKey = "containerStatus" - ExtensionApplicationNameKey = "applicationName" // For universial mode - ExtensionsWorkLoadKey = "workLoad" - ExtensionsNodeNameKey = "nodeName" + "github.com/apache/dubbo-admin/pkg/common/constants" ) type ResourceSpec interface{} @@ -57,8 +40,8 @@ type Resource interface { ResourceKind() ResourceKind // ResourceKey returns the unique resource key ResourceKey() string - // MeshName returns the mesh which the resource belongs to - MeshName() string + // ResourceMesh returns the mesh which the resource belongs to + ResourceMesh() string // ResourceMeta returns the resource metadata ResourceMeta() metav1.ObjectMeta // ResourceSpec returns the resource spec @@ -76,5 +59,5 @@ type ResourceList interface { // BuildResourceKey build a unique identifier for a resource, usually is `mesh/name` func BuildResourceKey(mesh string, name string) string { - return mesh + separator + name + return mesh + constants.PathSeparator + name } diff --git a/pkg/core/runtime/component.go b/pkg/core/runtime/component.go index a0362c0e4..ec90648f2 100644 --- a/pkg/core/runtime/component.go +++ b/pkg/core/runtime/component.go @@ -26,9 +26,10 @@ const ( ResourceEngine ComponentType = "resource engine" ResourceDiscovery ComponentType = "resource discovery" EventBus ComponentType = "event bus" + RuleGovernor ComponentType = "rule governor" ) -var CoreComponentTypes = []ComponentType{Console, ResourceManager, ResourceStore, ResourceEngine, ResourceDiscovery} +var CoreComponentTypes = []ComponentType{Console, ResourceManager, ResourceStore, ResourceEngine, ResourceDiscovery, RuleGovernor} type Lifecycle interface { // Init initializes the component diff --git a/pkg/core/runtime/registry.go b/pkg/core/runtime/registry.go index c5c058e4e..244301e7d 100644 --- a/pkg/core/runtime/registry.go +++ b/pkg/core/runtime/registry.go @@ -41,6 +41,7 @@ type Registry interface { ResourceManager() (Component, error) ResourceDiscovery() (Component, error) ResourceEngine() (Component, error) + RuleGovernor() (Component, error) } type RegistryMutator interface { @@ -88,6 +89,9 @@ func (r *componentRegistry) ResourceEngine() (Component, error) { return r.Get(ResourceEngine) } +func (r *componentRegistry) RuleGovernor() (Component, error) { + return r.Get(RuleGovernor) +} func (r *componentRegistry) Register(component Component) error { _, ok := r.directory[component.Type()] if ok { diff --git a/pkg/core/store/index/common.go b/pkg/core/store/index/common.go index f2b69f964..1312db0e5 100644 --- a/pkg/core/store/index/common.go +++ b/pkg/core/store/index/common.go @@ -43,5 +43,5 @@ func ByMesh(obj interface{}) ([]string, error) { if !ok { return nil, bizerror.NewAssertionError("Resource", reflect.TypeOf(obj).Name()) } - return []string{r.MeshName()}, nil + return []string{r.ResourceMesh()}, nil } diff --git a/pkg/core/store/index/service_provider_metadata.go b/pkg/core/store/index/service_provider_metadata.go index 53f9b8921..fe3d7fe28 100644 --- a/pkg/core/store/index/service_provider_metadata.go +++ b/pkg/core/store/index/service_provider_metadata.go @@ -27,12 +27,14 @@ import ( ) const ( - ByServiceProviderAppName = "idx_service_provider_app_name" + ByServiceProviderAppName = "idx_service_provider_app_name" + ByServiceProviderServiceName = "idx_service_provider_service_name" ) func init() { RegisterIndexers(meshresource.ServiceProviderMetadataKind, map[string]cache.IndexFunc{ - ByServiceProviderAppName: byServiceProviderAppName, + ByServiceProviderAppName: byServiceProviderAppName, + ByServiceProviderServiceName: byServiceProviderServiceName, }) } @@ -46,3 +48,14 @@ func byServiceProviderAppName(obj interface{}) ([]string, error) { } return []string{metadata.Spec.ProviderAppName}, nil } + +func byServiceProviderServiceName(obj interface{}) ([]string, error) { + metadata, ok := obj.(*meshresource.ServiceProviderMetadataResource) + if !ok { + return nil, bizerror.NewAssertionError(meshresource.ServiceProviderMetadataKind, reflect.TypeOf(obj).Name()) + } + if metadata.Spec == nil { + return []string{}, nil + } + return []string{metadata.Spec.ServiceName}, nil +} diff --git a/pkg/discovery/zk/factory.go b/pkg/discovery/zk/factory.go index 6a340fdd6..7518af8f0 100644 --- a/pkg/discovery/zk/factory.go +++ b/pkg/discovery/zk/factory.go @@ -47,13 +47,11 @@ func (f *Factory) Support(typ discoverycfg.Type) bool { } func (f *Factory) NewListWatchers(config *discoverycfg.Config) ([]controller.ResourceListerWatcher, error) { - zkLog := &zkLogger{} mappingLw, err := listerwatcher.NewListerWatcher( meshresource.ServiceProviderMappingKind, toUpsertMappingResource, toDeleteMappingResource, "/dubbo/mapping", - zkLog, config, ) if err != nil { @@ -64,7 +62,6 @@ func (f *Factory) NewListWatchers(config *discoverycfg.Config) ([]controller.Res toUpsertRPCInstanceResource, toDeleteRPCInstanceResource, "/services", - zkLog, config, ) if err != nil { @@ -75,7 +72,6 @@ func (f *Factory) NewListWatchers(config *discoverycfg.Config) ([]controller.Res toUpsertZKConfigResource, toDeleteZKConfigResource, "/dubbo/config", - zkLog, config, ) if err != nil { @@ -86,7 +82,6 @@ func (f *Factory) NewListWatchers(config *discoverycfg.Config) ([]controller.Res toUpsertZKMetadataResource, toDeleteZKMetadataResource, "/dubbo/metadata", - zkLog, config, ) if err != nil { @@ -235,9 +230,3 @@ func toDeleteRPCInstanceResource(mesh, nodePath string) coremodel.Resource { resName := meshresource.BuildInstanceResName(appName, ip, port) return meshresource.NewRPCInstanceResourceWithAttributes(resName, mesh) } - -type zkLogger struct{} - -func (z *zkLogger) Printf(s string, i ...interface{}) { - logger.Debugf(s, i...) -} diff --git a/pkg/discovery/zk/listerwatcher/listerwatcher.go b/pkg/discovery/zk/listerwatcher/listerwatcher.go index ce62adc41..61b0f1581 100644 --- a/pkg/discovery/zk/listerwatcher/listerwatcher.go +++ b/pkg/discovery/zk/listerwatcher/listerwatcher.go @@ -19,8 +19,6 @@ package listerwatcher import ( "fmt" - "net/url" - "time" "github.com/dubbogo/go-zookeeper/zk" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -31,6 +29,7 @@ import ( "github.com/apache/dubbo-admin/pkg/common/bizerror" "github.com/apache/dubbo-admin/pkg/common/constants" discoverycfg "github.com/apache/dubbo-admin/pkg/config/discovery" + "github.com/apache/dubbo-admin/pkg/core/clients" "github.com/apache/dubbo-admin/pkg/core/logger" coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" "github.com/apache/dubbo-admin/pkg/discovery/zk/zkwatcher" @@ -59,7 +58,6 @@ func NewListerWatcher( toResourceFunc ToUpsertResourceFunc, toDeleteResourceFunc ToDeleteResourceFunc, basePath string, - zkLogger zk.Logger, cfg *discoverycfg.Config) (*ListerWatcher[coremodel.Resource], error) { newResourceFunc, err := coremodel.ResourceSchemaRegistry().NewResourceFunc(rk) if err != nil { @@ -69,18 +67,10 @@ func NewListerWatcher( if err != nil { return nil, err } - address := cfg.Address.Registry - zkUrl, err := url.Parse(address) + conn, err := clients.NewZKConnection(cfg.Address.Registry) if err != nil { return nil, err } - conn, _, err := zk.Connect([]string{zkUrl.Host}, time.Second*1, func(c *zk.Conn) { - c.SetLogger(zkLogger) - }) - if err != nil { - logger.Errorf("connect to %s failed in %s lister watcher", rk, address) - return nil, bizerror.Wrap(err, bizerror.ZKError, "connect to zookeeper failed, addr: "+address) - } return &ListerWatcher[coremodel.Resource]{ rk: rk, toUpsertResourceFunc: toResourceFunc, diff --git a/pkg/engine/kubernetes/listerwatcher/runtime_instance.go b/pkg/engine/kubernetes/listerwatcher/runtime_instance.go index 4b3140278..c6f497815 100644 --- a/pkg/engine/kubernetes/listerwatcher/runtime_instance.go +++ b/pkg/engine/kubernetes/listerwatcher/runtime_instance.go @@ -152,20 +152,22 @@ func (p *PodListerWatcher) TransformFunc() cache.TransformFunc { } res := meshresource.NewRuntimeInstanceResourceWithAttributes(pod.Name, p.getDubboMesh(pod)) res.Spec = &meshproto.RuntimeInstance{ - Name: pod.Name, - Ip: pod.Status.PodIP, - RpcPort: rpcPort, - Image: image, - AppName: appName, - CreateTime: createTime, - StartTime: startTime, - ReadyTime: readyTime, - Phase: phase, - WorkloadName: workloadName, - WorkloadType: workloadType, - Node: pod.Spec.NodeName, - Probes: probes, - Conditions: conditions, + Name: pod.Name, + Ip: pod.Status.PodIP, + RpcPort: rpcPort, + Image: image, + AppName: appName, + CreateTime: createTime, + StartTime: startTime, + ReadyTime: readyTime, + Phase: phase, + WorkloadName: workloadName, + WorkloadType: workloadType, + Node: pod.Spec.NodeName, + Probes: probes, + Conditions: conditions, + SourceEngine: p.cfg.ID, + SourceEngineType: string(p.cfg.Type), } return res, nil } diff --git a/pkg/governor/nacos2/factory.go b/pkg/governor/nacos2/factory.go new file mode 100644 index 000000000..3b82b1616 --- /dev/null +++ b/pkg/governor/nacos2/factory.go @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 nacos2 + +import ( + discoverycfg "github.com/apache/dubbo-admin/pkg/config/discovery" + "github.com/apache/dubbo-admin/pkg/core/events" + "github.com/apache/dubbo-admin/pkg/core/governor" + "github.com/apache/dubbo-admin/pkg/core/store" +) + +func init() { + governor.RegisterFactory(&Factory{}) +} + +type Factory struct{} + +func (f *Factory) Support(t discoverycfg.Type) bool { + return t == discoverycfg.Nacos2 +} + +func (f *Factory) New(mesh string, config *discoverycfg.Config, router store.Router, emitter events.Emitter) (governor.RuleGovernor, error) { + return NewNacos2Governor(config, router, emitter) +} diff --git a/pkg/governor/nacos2/governor.go b/pkg/governor/nacos2/governor.go new file mode 100644 index 000000000..c05405493 --- /dev/null +++ b/pkg/governor/nacos2/governor.go @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 nacos2 + +import ( + "fmt" + "reflect" + + nacosconfigclient "github.com/nacos-group/nacos-sdk-go/v2/clients/config_client" + nacosnamingclient "github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client" + nacosvo "github.com/nacos-group/nacos-sdk-go/v2/vo" + "k8s.io/client-go/tools/cache" + "sigs.k8s.io/yaml" + + "github.com/apache/dubbo-admin/pkg/common/bizerror" + "github.com/apache/dubbo-admin/pkg/common/constants" + discoverycfg "github.com/apache/dubbo-admin/pkg/config/discovery" + "github.com/apache/dubbo-admin/pkg/core/clients" + "github.com/apache/dubbo-admin/pkg/core/events" + "github.com/apache/dubbo-admin/pkg/core/logger" + meshresource "github.com/apache/dubbo-admin/pkg/core/resource/apis/mesh/v1alpha1" + coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" + "github.com/apache/dubbo-admin/pkg/core/store" +) + +type RuleGovernor struct { + cfg *discoverycfg.Config + storeRouter store.Router + emitter events.Emitter + configClient nacosconfigclient.IConfigClient + namingClient nacosnamingclient.INamingClient +} + +func NewNacos2Governor( + cfg *discoverycfg.Config, + storeRouter store.Router, + emitter events.Emitter) (*RuleGovernor, error) { + configClient, namingClient, err := clients.CreateNacosClients(cfg.Address.ConfigCenter) + if err != nil { + return nil, err + } + return &RuleGovernor{ + cfg: cfg, + storeRouter: storeRouter, + emitter: emitter, + configClient: configClient, + namingClient: namingClient, + }, nil +} + +func (g *RuleGovernor) CreateRule(r coremodel.Resource) error { + rawContent, err := yaml.Marshal(r.ResourceSpec()) + if err != nil { + return bizerror.Wrap(err, bizerror.NacosError, + fmt.Sprintf("failed to marshal resource spec, res: %s", r.String())) + } + ok, err := g.configClient.PublishConfig(nacosvo.ConfigParam{ + DataId: r.ResourceMeta().Name, + Group: constants.NacosConfigGroup, + Content: string(rawContent), + }) + if err != nil || !ok { + logger.Errorf("failed to publish config in %s, res: %s", r.String(), r.ResourceMesh()) + return bizerror.Wrap(err, bizerror.NacosError, + fmt.Sprintf("failed to publish config, res: %s", r.String())) + } + //g.GetConfigAndUpdateStore(r) + return nil +} + +func (g *RuleGovernor) UpdateRule(r coremodel.Resource) error { + return g.CreateRule(r) +} + +func (g *RuleGovernor) DeleteRule(r coremodel.Resource) error { + ok, err := g.configClient.DeleteConfig(nacosvo.ConfigParam{ + DataId: r.ResourceMeta().Name, + Group: constants.NacosConfigGroup, + }) + if err != nil || !ok { + logger.Errorf("failed to delete config in %s, res: %s", r.String(), r.ResourceMesh()) + return bizerror.Wrap(err, bizerror.NacosError, + fmt.Sprintf("failed to delete config, res: %s", r.String())) + } + // delete resource in store, if delete failed, just log an error message + // the lister-watcher will sync the deleted event finally + st, err := g.storeRouter.ResourceKindRoute(r.ResourceKind()) + if err != nil { + logger.Errorf("failed to get store in %s, res: %s, cause: %s", r.String(), r.ResourceMesh(), err) + return nil + } + if err := st.Delete(r); err != nil { + logger.Errorf("failed to delete resource in %s, res: %s, cause: %s", r.String(), r.ResourceMesh(), err) + return nil + } + return nil +} + +// GetConfigAndUpdateStore get resource from nacos, and update resource in store, if failed, just log an error message, +// the lister-watcher will sync the event finally +func (g *RuleGovernor) GetConfigAndUpdateStore(r coremodel.Resource) { + content, err := g.configClient.GetConfig(nacosvo.ConfigParam{ + DataId: r.ResourceMeta().Name, + Group: constants.NacosConfigGroup, + }) + if err != nil { + logger.Errorf("failed to get config in %s, res: %s, cause: %s", r.String(), r.ResourceMesh(), err) + return + } + st, err := g.storeRouter.ResourceKindRoute(r.ResourceKind()) + if err != nil { + logger.Errorf("failed to get store in %s, res: %s, cause: %s", r.String(), r.ResourceMesh(), err) + return + } + var res coremodel.Resource + switch r.ResourceKind() { + case meshresource.DynamicConfigKind: + res = meshresource.ToDynamicConfigResource(r.ResourceMesh(), r.ResourceMeta().Name, content) + case meshresource.ConditionRouteKind: + res = meshresource.ToConditionRouteResource(r.ResourceMesh(), r.ResourceMeta().Name, content) + case meshresource.TagRouteKind: + res = meshresource.ToTagRouteResource(r.ResourceMesh(), r.ResourceMeta().Name, content) + } + obj, exists, err := st.GetByKey(r.ResourceKey()) + if err != nil { + logger.Errorf("failed to get resource in %s, res: %s, cause: %s", r.String(), r.ResourceMesh(), err) + return + } + // if exists in store, update it + if exists { + if err := st.Update(res); err != nil { + logger.Errorf("failed to update resource in %s, res: %s, cause: %s", r.String(), r.ResourceMesh(), err) + return + } + oldRes, ok := obj.(coremodel.Resource) + if !ok { + logger.Errorf("type assertion failed in nacos2 discovery %s, expected Resource, got %s", + g.mesh(), reflect.TypeOf(obj).Name()) + } + g.emitter.Send(events.NewResourceChangedEvent(cache.Updated, oldRes, res)) + return + } + + // otherwise add it + err = st.Add(res) + if err != nil { + logger.Errorf("failed to add resource in %s, res: %s, cause: %s", r.String(), r.ResourceMesh(), err) + return + } + g.emitter.Send(events.NewResourceChangedEvent(cache.Added, nil, res)) +} + +func (g *RuleGovernor) mesh() string { + return g.cfg.ID +} diff --git a/pkg/governor/zk/factory.go b/pkg/governor/zk/factory.go new file mode 100644 index 000000000..1d6819d41 --- /dev/null +++ b/pkg/governor/zk/factory.go @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 zk + +import ( + discoverycfg "github.com/apache/dubbo-admin/pkg/config/discovery" + "github.com/apache/dubbo-admin/pkg/core/events" + "github.com/apache/dubbo-admin/pkg/core/governor" + "github.com/apache/dubbo-admin/pkg/core/store" +) + +func init() { + governor.RegisterFactory(&Factory{}) +} + +type Factory struct{} + +func (f *Factory) Support(t discoverycfg.Type) bool { + return t == discoverycfg.Zookeeper +} + +func (f *Factory) New(_ string, config *discoverycfg.Config, router store.Router, emitter events.Emitter) (governor.RuleGovernor, error) { + return NewZKRuleGovernor(config, router, emitter) +} diff --git a/pkg/governor/zk/governor.go b/pkg/governor/zk/governor.go new file mode 100644 index 000000000..7c7a9ce3d --- /dev/null +++ b/pkg/governor/zk/governor.go @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 zk + +import ( + "fmt" + + "github.com/dubbogo/go-zookeeper/zk" + "sigs.k8s.io/yaml" + + "github.com/apache/dubbo-admin/pkg/common/bizerror" + discoverycfg "github.com/apache/dubbo-admin/pkg/config/discovery" + "github.com/apache/dubbo-admin/pkg/core/clients" + "github.com/apache/dubbo-admin/pkg/core/events" + "github.com/apache/dubbo-admin/pkg/core/logger" + coremodel "github.com/apache/dubbo-admin/pkg/core/resource/model" + "github.com/apache/dubbo-admin/pkg/core/store" +) + +type RuleGovernor struct { + cfg *discoverycfg.Config + storeRouter store.Router + emitter events.Emitter + conn *zk.Conn +} + +func NewZKRuleGovernor(cfg *discoverycfg.Config, router store.Router, emitter events.Emitter) (*RuleGovernor, error) { + address := cfg.Address.Registry + conn, err := clients.NewZKConnection(address) + if err != nil { + return nil, err + } + return &RuleGovernor{ + cfg: cfg, + storeRouter: router, + emitter: emitter, + conn: conn, + }, nil +} + +func (g *RuleGovernor) CreateRule(r coremodel.Resource) error { + path := "/dubbo/config/" + r.ResourceMeta().Name + content, err := yaml.Marshal(r.ResourceSpec()) + if err != nil { + return bizerror.Wrap(err, bizerror.YamlError, + fmt.Sprintf("failed to marshal resource spec, res: %s", r.String())) + } + _, err = g.conn.Create(path, content, zk.FlagPersistent, zk.WorldACL(zk.PermAll)) + if err != nil { + return bizerror.Wrap(err, bizerror.ZKError, + fmt.Sprintf("failed to create zk node, path: %s", path)) + } + // save to store once znode is created in zk to insure local store is consistent to zk timely. + // if save to store failed, the discovery will watch and update the store finally. + st, err := g.storeRouter.ResourceRoute(r) + if err != nil { + logger.Warnf("cannot find store for rk: %s, cause: %v", r.ResourceKind(), err) + return nil + } + err = st.Add(r) + if err != nil { + logger.Warnf("add resource to store failed, res: %s, cause: %v", r.String(), err) + return nil + } + return nil +} + +func (g *RuleGovernor) UpdateRule(r coremodel.Resource) error { + path := "/dubbo/config/" + r.ResourceMeta().Name + content, err := yaml.Marshal(r.ResourceSpec()) + if err != nil { + return bizerror.Wrap(err, bizerror.YamlError, + fmt.Sprintf("failed to marshal resource spec, res: %s", r.String())) + } + _, err = g.conn.Set(path, content, -1) + if err != nil { + return bizerror.Wrap(err, bizerror.ZKError, + fmt.Sprintf("failed to update zk node, path: %s", path)) + } + st, err := g.storeRouter.ResourceRoute(r) + if err != nil { + logger.Warnf("cannot find store for rk: %s, cause: %v", r.ResourceKind(), err) + return nil + } + err = st.Update(r) + if err != nil { + logger.Warnf("update resource in store failed, res: %s, cause: %v", r.String(), err) + } + return nil +} + +func (g *RuleGovernor) DeleteRule(r coremodel.Resource) error { + path := "/dubbo/config/" + r.ResourceMeta().Name + err := g.conn.Delete(path, -1) + if err != nil { + return bizerror.Wrap(err, bizerror.ZKError, + fmt.Sprintf("failed to delete zk node, path: %s", path)) + } + st, err := g.storeRouter.ResourceRoute(r) + if err != nil { + logger.Warnf("cannot find store for rk: %s, cause: %v", r.ResourceKind(), err) + } + err = st.Delete(r) + if err != nil { + logger.Warnf("delete resource from store failed, res: %s, cause: %v", r.String(), err) + } + return nil +} diff --git a/pkg/store/dbcommon/gorm_store.go b/pkg/store/dbcommon/gorm_store.go index bd52c8935..e823189b5 100644 --- a/pkg/store/dbcommon/gorm_store.go +++ b/pkg/store/dbcommon/gorm_store.go @@ -129,7 +129,7 @@ func (gs *GormStore) Add(obj interface{}) error { return store.ErrorResourceAlreadyExists( resource.ResourceKind().ToString(), resource.ResourceMeta().Name, - resource.MeshName(), + resource.ResourceMesh(), ) } @@ -168,7 +168,7 @@ func (gs *GormStore) Update(obj interface{}) error { return store.ErrorResourceNotFound( resource.ResourceKind().ToString(), resource.ResourceMeta().Name, - resource.MeshName(), + resource.ResourceMesh(), ) } @@ -194,7 +194,7 @@ func (gs *GormStore) Update(obj interface{}) error { return store.ErrorResourceNotFound( resource.ResourceKind().ToString(), resource.ResourceMeta().Name, - resource.MeshName(), + resource.ResourceMesh(), ) } @@ -224,7 +224,7 @@ func (gs *GormStore) Delete(obj interface{}) error { return store.ErrorResourceNotFound( resource.ResourceKind().ToString(), resource.ResourceMeta().Name, - resource.MeshName(), + resource.ResourceMesh(), ) } diff --git a/pkg/store/dbcommon/gorm_store_test.go b/pkg/store/dbcommon/gorm_store_test.go index e5f445d76..da9718ed2 100644 --- a/pkg/store/dbcommon/gorm_store_test.go +++ b/pkg/store/dbcommon/gorm_store_test.go @@ -73,6 +73,10 @@ func (mr *mockResource) ResourceSpec() model.ResourceSpec { return mr.Spec } +func (mr *mockResource) ResourceMesh() string { + return mr.Mesh +} + func (mr *mockResource) String() string { b, err := json.Marshal(mr) if err != nil { @@ -601,7 +605,7 @@ func TestGormStore_AddIndexers(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) @@ -624,7 +628,7 @@ func TestGormStore_IndexKeys(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) @@ -734,7 +738,7 @@ func TestGormStore_ListByIndexes(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) @@ -814,7 +818,7 @@ func TestGormStore_PageListByIndexes(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) @@ -897,7 +901,7 @@ func TestGormStore_PageListByIndexesOffsetBeyondTotal(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) @@ -936,7 +940,7 @@ func TestGormStore_MultipleIndexes(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, "by-namespace": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) @@ -1023,7 +1027,7 @@ func TestGormStore_ListIndexFuncValues(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) @@ -1104,7 +1108,7 @@ func TestGormStore_UpdateIndices(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) @@ -1156,7 +1160,7 @@ func TestGormStore_DeleteFromIndices(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) @@ -1198,7 +1202,7 @@ func TestGormStore_ReplaceIndices(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) @@ -1249,7 +1253,7 @@ func TestGormStore_InitRebuildIndices(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) @@ -1376,7 +1380,7 @@ func TestGormStore_ConcurrentOperations(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) diff --git a/pkg/store/dbcommon/model.go b/pkg/store/dbcommon/model.go index 96277a12e..5cf66c695 100644 --- a/pkg/store/dbcommon/model.go +++ b/pkg/store/dbcommon/model.go @@ -119,7 +119,7 @@ func FromResource(resource model.Resource) (*ResourceModel, error) { ResourceKey: resource.ResourceKey(), ResourceKind: resource.ResourceKind().ToString(), Name: resource.ResourceMeta().Name, - Mesh: resource.MeshName(), + Mesh: resource.ResourceMesh(), Data: data, }, nil } diff --git a/pkg/store/memory/store_test.go b/pkg/store/memory/store_test.go index 8f4250007..8d1f1104a 100644 --- a/pkg/store/memory/store_test.go +++ b/pkg/store/memory/store_test.go @@ -40,6 +40,10 @@ type mockResource struct { objectRef runtime.Object } +func (mr *mockResource) ResourceMesh() string { + return mr.mesh +} + func (mr *mockResource) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } @@ -56,10 +60,6 @@ func (mr *mockResource) ResourceKey() string { return mr.key } -func (mr *mockResource) MeshName() string { - return mr.mesh -} - func (mr *mockResource) ResourceMeta() metav1.ObjectMeta { return mr.meta } @@ -67,6 +67,7 @@ func (mr *mockResource) ResourceMeta() metav1.ObjectMeta { func (mr *mockResource) ResourceSpec() model.ResourceSpec { return mr.spec } + func (mr *mockResource) String() string { b, err := json.Marshal(mr) if err != nil { @@ -361,7 +362,7 @@ func TestResourceStore_ListByIndexes(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) @@ -416,7 +417,7 @@ func TestResourceStore_PageListByIndexes(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, } err = store.AddIndexers(indexers) @@ -518,7 +519,7 @@ func TestResourceStore_MultipleIndexes(t *testing.T) { indexers := map[string]cache.IndexFunc{ "by-mesh": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) - return []string{resource.MeshName()}, nil + return []string{resource.ResourceMesh()}, nil }, "by-version": func(obj interface{}) ([]string, error) { resource := obj.(model.Resource) diff --git a/scripts/resourcegen/gen.go b/scripts/resourcegen/gen.go index d2882b4e1..0c3e25cfc 100644 --- a/scripts/resourcegen/gen.go +++ b/scripts/resourcegen/gen.go @@ -106,7 +106,7 @@ func (r *{{.Name}}Resource) ResourceKind() coremodel.ResourceKind { return {{.Name}}Kind } -func (r *{{.Name}}Resource) MeshName() string { +func (r *{{.Name}}Resource) ResourceMesh() string { return r.Mesh } diff --git a/ui-vue3/yarn.lock b/ui-vue3/yarn.lock index 2c6c10bfa..70fc77b98 100644 --- a/ui-vue3/yarn.lock +++ b/ui-vue3/yarn.lock @@ -2405,6 +2405,11 @@ defined@~1.0.1: resolved "https://registry.npmmirror.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf" integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q== +defu@^6.1.4: + version "6.1.4" + resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" + integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -3688,6 +3693,11 @@ js-beautify@^1.14.9: glob "^10.3.3" nopt "^7.2.0" +js-cookie@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" + integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -4435,6 +4445,13 @@ pify@^4.0.1: resolved "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pinia-plugin-persistedstate@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.7.1.tgz#d13880e0b7efdafd1b73fca3d73cd64ae34bde84" + integrity sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ== + dependencies: + defu "^6.1.4" + pinia@^2.1.7: version "2.1.7" resolved "https://registry.npmmirror.com/pinia/-/pinia-2.1.7.tgz#4cf5420d9324ca00b7b4984d3fbf693222115bbc"