From 80798745e3148998f3688df99b741d58ae454abb Mon Sep 17 00:00:00 2001 From: cywang1905 Date: Thu, 31 Mar 2022 14:37:43 +0800 Subject: [PATCH 1/2] Fence.spec.host content should be full host --- controllers/servicefence_controller.go | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/controllers/servicefence_controller.go b/controllers/servicefence_controller.go index 840f570..25d1c4c 100644 --- a/controllers/servicefence_controller.go +++ b/controllers/servicefence_controller.go @@ -375,7 +375,7 @@ func addDomainsWithHost(domains map[string]*lazyloadv1alpha1.Destinations, sf *l // handle namespace level host, like 'default/*' handleNsHost(h, domains, nsSvcCache) } else { - // handle service level host, like 'a.default.svc.cluster.local' or 'a' or 'a.default' + // handle service level host, like 'a.default.svc.cluster.local' or 'www.netease.com' handleSvcHost(h, strategy, checkStatus, domains, sf) } } @@ -424,27 +424,12 @@ func handleNsHost(h string, domains map[string]*lazyloadv1alpha1.Destinations, n } } -func handleSvcHost(h string, strategy *lazyloadv1alpha1.RecyclingStrategy, +func handleSvcHost(fullHost string, strategy *lazyloadv1alpha1.RecyclingStrategy, checkStatus func(now int64, strategy *lazyloadv1alpha1.RecyclingStrategy) lazyloadv1alpha1.Destinations_Status, domains map[string]*lazyloadv1alpha1.Destinations, sf *lazyloadv1alpha1.ServiceFence, ) { now := time.Now().Unix() - fullHost := h - hostParts := strings.Split(h, ".") - switch len(hostParts) { - // full service name, like "reviews.default.svc.cluster.local", needs no action - case 5: - // short service name without namespace, like "reviews", needs to add namespace of servicefence and "svc.cluster.local" - case 1: - fullHost = fmt.Sprintf("%s.%s.svc.cluster.local", hostParts[0], sf.Namespace) - // short service name with namespace, like "reviews.default", needs to add "svc.cluster.local" - case 2: - fullHost = fmt.Sprintf("%s.%s.svc.cluster.local", hostParts[0], hostParts[1]) - default: - log.Errorf("%s is invalid host, skip", h) - return - } if !isValidHost(fullHost) { return } From 353dc89cc2a8bfc6cd8cf405add69608206e51b6 Mon Sep 17 00:00:00 2001 From: cywang1905 Date: Fri, 1 Apr 2022 15:30:28 +0800 Subject: [PATCH 2/2] Support domain alias in fence module config --- README.md | 1 + README_zh.md | 1 + api/v1alpha1/fence_module.pb.go | 110 +++++++++++++++++++----- api/v1alpha1/fence_module.proto | 14 +++ controllers/domain.go | 81 ++++++++++++++++++ controllers/model.go | 7 ++ controllers/servicefence_controller.go | 114 ++++++++++++------------- lazyload_tutorials.md | 87 +++++++++++++++++++ lazyload_tutorials_zh.md | 87 +++++++++++++++++++ 9 files changed, 422 insertions(+), 80 deletions(-) create mode 100644 controllers/domain.go diff --git a/README.md b/README.md index b834c54..94f8ecd 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ Details at [Install&Use](./lazyload_tutorials.md#install-and-use) - Automatic ServiceFence generation based on namespace/service label - Custom undefined traffic dispatch - Support for adding static service dependencies +- Support for custom service dependency aliases - Log output to local file and rotate Details at [Introduction of features](./lazyload_tutorials.md#Introduction-of-features) diff --git a/README_zh.md b/README_zh.md index 6bb7b3a..3ac27c6 100644 --- a/README_zh.md +++ b/README_zh.md @@ -84,6 +84,7 @@ - 基于namespace/service label自动生成ServiceFence - 支持自定义兜底流量分派 - 支持添加静态服务依赖关系 +- 支持自定义服务依赖别名 - 日志输出到本地并轮转 详见 [特性介绍](./lazyload_tutorials_zh.md#%E7%89%B9%E6%80%A7%E4%BB%8B%E7%BB%8D) diff --git a/api/v1alpha1/fence_module.pb.go b/api/v1alpha1/fence_module.pb.go index c891806..33c6ae2 100644 --- a/api/v1alpha1/fence_module.pb.go +++ b/api/v1alpha1/fence_module.pb.go @@ -29,10 +29,12 @@ type Fence struct { // the namespace list which enable lazyload Namespace []string `protobuf:"bytes,3,rep,name=namespace,proto3" json:"namespace,omitempty"` // custom outside dispatch traffic rules - Dispatches []*Dispatch `protobuf:"bytes,4,rep,name=dispatches,proto3" json:"dispatches,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Dispatches []*Dispatch `protobuf:"bytes,4,rep,name=dispatches,proto3" json:"dispatches,omitempty"` + // can convert to one or many domain alias rules + DomainAliases []*DomainAlias `protobuf:"bytes,5,rep,name=domainAliases,proto3" json:"domainAliases,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Fence) Reset() { *m = Fence{} } @@ -87,6 +89,13 @@ func (m *Fence) GetDispatches() []*Dispatch { return nil } +func (m *Fence) GetDomainAliases() []*DomainAlias { + if m != nil { + return m.DomainAliases + } + return nil +} + // The general idea is to assign different default traffic to different targets // for correct processing by means of domain matching. type Dispatch struct { @@ -146,30 +155,87 @@ func (m *Dispatch) GetCluster() string { return "" } +// DomainAlias regexp expression, which is alias for target domain +// default value is empty +// example: +// domainAliases: +// - pattern: (?P[^\.]+)\.(?P[^\.]+)\.svc\.cluster\.local$ +// template: +// - $namespace.$service.service.mailsaas +type DomainAlias struct { + Pattern string `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"` + Templates []string `protobuf:"bytes,2,rep,name=templates,proto3" json:"templates,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *DomainAlias) Reset() { *m = DomainAlias{} } +func (m *DomainAlias) String() string { return proto.CompactTextString(m) } +func (*DomainAlias) ProtoMessage() {} +func (*DomainAlias) Descriptor() ([]byte, []int) { + return fileDescriptor_8eebc4b237a55c9b, []int{2} +} +func (m *DomainAlias) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_DomainAlias.Unmarshal(m, b) +} +func (m *DomainAlias) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_DomainAlias.Marshal(b, m, deterministic) +} +func (m *DomainAlias) XXX_Merge(src proto.Message) { + xxx_messageInfo_DomainAlias.Merge(m, src) +} +func (m *DomainAlias) XXX_Size() int { + return xxx_messageInfo_DomainAlias.Size(m) +} +func (m *DomainAlias) XXX_DiscardUnknown() { + xxx_messageInfo_DomainAlias.DiscardUnknown(m) +} + +var xxx_messageInfo_DomainAlias proto.InternalMessageInfo + +func (m *DomainAlias) GetPattern() string { + if m != nil { + return m.Pattern + } + return "" +} + +func (m *DomainAlias) GetTemplates() []string { + if m != nil { + return m.Templates + } + return nil +} + func init() { proto.RegisterType((*Fence)(nil), "slime.microservice.lazyload.v1alpha1.Fence") proto.RegisterType((*Dispatch)(nil), "slime.microservice.lazyload.v1alpha1.Dispatch") + proto.RegisterType((*DomainAlias)(nil), "slime.microservice.lazyload.v1alpha1.DomainAlias") } func init() { proto.RegisterFile("fence_module.proto", fileDescriptor_8eebc4b237a55c9b) } var fileDescriptor_8eebc4b237a55c9b = []byte{ - // 261 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x90, 0xc1, 0x4a, 0xc4, 0x30, - 0x10, 0x86, 0xe9, 0x76, 0xd5, 0xdd, 0xd1, 0x83, 0xe4, 0x94, 0x83, 0x87, 0x52, 0x3c, 0x14, 0x91, - 0x94, 0xd5, 0x27, 0x50, 0xc4, 0xa3, 0x48, 0x8f, 0x5e, 0x64, 0x36, 0x1d, 0x69, 0x20, 0xe9, 0x94, - 0xa4, 0x5d, 0xd1, 0xf7, 0xf3, 0xbd, 0xa4, 0x59, 0x83, 0x8a, 0x17, 0x6f, 0x33, 0x7f, 0xf8, 0x26, - 0x1f, 0x3f, 0x88, 0x17, 0xea, 0x35, 0x3d, 0x3b, 0x6e, 0x27, 0x4b, 0x6a, 0xf0, 0x3c, 0xb2, 0x38, - 0x0f, 0xd6, 0x38, 0x52, 0xce, 0x68, 0xcf, 0x81, 0xfc, 0xce, 0x68, 0x52, 0x16, 0xdf, 0xdf, 0x2c, - 0x63, 0xab, 0x76, 0x1b, 0xb4, 0x43, 0x87, 0x9b, 0xf2, 0x23, 0x83, 0x83, 0xfb, 0x19, 0x16, 0x25, - 0x9c, 0xbc, 0xb2, 0x77, 0x1d, 0x5b, 0x7a, 0x64, 0x3f, 0xca, 0xac, 0xc8, 0xab, 0x75, 0xf3, 0x2b, - 0x13, 0x17, 0x70, 0xda, 0x9a, 0x80, 0x5b, 0x4b, 0x37, 0xd3, 0xc8, 0x91, 0x93, 0x8b, 0x22, 0xab, - 0x56, 0xcd, 0x9f, 0x5c, 0x9c, 0xc1, 0xba, 0x47, 0x47, 0x61, 0x40, 0x4d, 0x32, 0x8f, 0xc7, 0xbe, - 0x03, 0xf1, 0x00, 0xd0, 0x9a, 0x30, 0xe0, 0xa8, 0x3b, 0x0a, 0x72, 0x59, 0xe4, 0xd5, 0xf1, 0x95, - 0x52, 0xff, 0x51, 0x56, 0x77, 0x5f, 0x5c, 0xf3, 0xe3, 0x42, 0xd9, 0xc0, 0x2a, 0xe5, 0x42, 0xc0, - 0x72, 0xfe, 0x48, 0x66, 0x45, 0x56, 0xad, 0x9b, 0x38, 0x0b, 0x09, 0x47, 0x2d, 0x3b, 0x34, 0x7d, - 0x90, 0x8b, 0xe8, 0x92, 0xd6, 0xf9, 0x45, 0xdb, 0x29, 0x8c, 0xe4, 0x65, 0x1e, 0x81, 0xb4, 0xde, - 0xaa, 0xa7, 0xcb, 0xbd, 0x90, 0xe1, 0x3a, 0x0e, 0xf5, 0xbe, 0xe0, 0x50, 0x27, 0xa9, 0x1a, 0x07, - 0x53, 0x27, 0xb1, 0xed, 0x61, 0x2c, 0xfe, 0xfa, 0x33, 0x00, 0x00, 0xff, 0xff, 0x54, 0xb0, 0xad, - 0x38, 0x8e, 0x01, 0x00, 0x00, + // 313 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xc1, 0x4a, 0x3b, 0x31, + 0x10, 0xc6, 0xd9, 0x6e, 0xfb, 0xff, 0xb7, 0xa9, 0x82, 0xe4, 0x94, 0x83, 0x87, 0x65, 0xf1, 0xb0, + 0x88, 0x64, 0xa9, 0x3e, 0x41, 0x45, 0x3d, 0x8a, 0xec, 0x45, 0xf0, 0x22, 0xd3, 0xec, 0x48, 0x03, + 0xc9, 0x26, 0x24, 0x69, 0x45, 0x1f, 0xc6, 0x67, 0x95, 0x4d, 0x1b, 0xb6, 0xc5, 0x4b, 0x6f, 0x33, + 0x5f, 0xf2, 0xfd, 0x26, 0x5f, 0x18, 0x42, 0x3f, 0xb0, 0x13, 0xf8, 0xae, 0x4d, 0xbb, 0x51, 0xc8, + 0xad, 0x33, 0xc1, 0xd0, 0x2b, 0xaf, 0xa4, 0x46, 0xae, 0xa5, 0x70, 0xc6, 0xa3, 0xdb, 0x4a, 0x81, + 0x5c, 0xc1, 0xf7, 0x97, 0x32, 0xd0, 0xf2, 0xed, 0x02, 0x94, 0x5d, 0xc3, 0xa2, 0xfc, 0x19, 0x91, + 0xc9, 0x53, 0x6f, 0xa6, 0x25, 0x39, 0xfb, 0x34, 0x4e, 0xaf, 0x8d, 0xc2, 0x17, 0xe3, 0x02, 0xcb, + 0x8a, 0xbc, 0x9a, 0x35, 0x47, 0x1a, 0xbd, 0x26, 0x17, 0xad, 0xf4, 0xb0, 0x52, 0xb8, 0xdc, 0x04, + 0x13, 0x7d, 0x6c, 0x54, 0x64, 0xd5, 0xb4, 0xf9, 0xa3, 0xd3, 0x4b, 0x32, 0xeb, 0x40, 0xa3, 0xb7, + 0x20, 0x90, 0xe5, 0x11, 0x36, 0x08, 0xf4, 0x99, 0x90, 0x56, 0x7a, 0x0b, 0x41, 0xac, 0xd1, 0xb3, + 0x71, 0x91, 0x57, 0xf3, 0x5b, 0xce, 0x4f, 0x79, 0x32, 0x7f, 0xd8, 0xfb, 0x9a, 0x03, 0x02, 0x7d, + 0x25, 0xe7, 0xad, 0xd1, 0x20, 0xbb, 0xa5, 0x92, 0xe0, 0xd1, 0xb3, 0x49, 0x44, 0x2e, 0x4e, 0x44, + 0x0e, 0xd6, 0xe6, 0x98, 0x53, 0x36, 0x64, 0x9a, 0x06, 0x52, 0x4a, 0xc6, 0x7d, 0x02, 0x96, 0x15, + 0x59, 0x35, 0x6b, 0x62, 0x4d, 0x19, 0xf9, 0xbf, 0x33, 0x78, 0x36, 0x8a, 0x21, 0x53, 0xdb, 0x9f, + 0x08, 0xb5, 0xf1, 0x01, 0x1d, 0xcb, 0xa3, 0x21, 0xb5, 0xe5, 0x23, 0x99, 0x1f, 0x4c, 0xec, 0x2f, + 0x5a, 0x08, 0x01, 0x5d, 0xb7, 0x27, 0xa7, 0xb6, 0xff, 0xc3, 0x80, 0xda, 0x2a, 0x08, 0x98, 0xf0, + 0x83, 0x70, 0xcf, 0xdf, 0x6e, 0x76, 0xe9, 0xa4, 0xa9, 0x63, 0x51, 0xef, 0x16, 0xc0, 0xd7, 0x29, + 0x61, 0x0d, 0x56, 0xd6, 0x29, 0xe5, 0xea, 0x5f, 0x5c, 0x8c, 0xbb, 0xdf, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x70, 0x6e, 0xcc, 0x00, 0x2e, 0x02, 0x00, 0x00, } diff --git a/api/v1alpha1/fence_module.proto b/api/v1alpha1/fence_module.proto index de4dea0..09c1977 100644 --- a/api/v1alpha1/fence_module.proto +++ b/api/v1alpha1/fence_module.proto @@ -18,6 +18,8 @@ message Fence { repeated string namespace = 3; // custom outside dispatch traffic rules repeated Dispatch dispatches = 4; + // can convert to one or many domain alias rules + repeated DomainAlias domainAliases = 5; } // The general idea is to assign different default traffic to different targets @@ -29,4 +31,16 @@ message Dispatch { repeated string domains = 2; // target cluster string cluster = 3; +} + +// DomainAlias regexp expression, which is alias for target domain +// default value is empty +// example: +// domainAliases: +// - pattern: (?P[^\.]+)\.(?P[^\.]+)\.svc\.cluster\.local$ +// template: +// - $namespace.$service.service.mailsaas +message DomainAlias { + string pattern = 1; + repeated string templates = 2; } \ No newline at end of file diff --git a/controllers/domain.go b/controllers/domain.go new file mode 100644 index 0000000..5e78d7b --- /dev/null +++ b/controllers/domain.go @@ -0,0 +1,81 @@ +package controllers + +import ( + "regexp" + lazyloadv1alpha1 "slime.io/slime/modules/lazyload/api/v1alpha1" +) + +func newDomainAliasRules(domainAlias []*lazyloadv1alpha1.DomainAlias) []*domainAliasRule { + var rules []*domainAliasRule + if domainAlias == nil { + return nil + } + + for _, da := range domainAlias { + log.Infof("lazyload domainAlias: get pattern %s, get templates %v", da.Pattern, da.Templates) + + pattern := da.Pattern + re, err := regexp.Compile(pattern) + if err != nil { + log.Errorf("compile domainAlias pattern %s err: %+v", pattern, err) + return nil + } + templates := da.Templates + if len(templates) == 0 { + log.Errorf("domainAlias template is empty") + return nil + } + rule := &domainAliasRule{ + pattern: pattern, + templates: templates, + re: re, + } + rules = append(rules, rule) + } + return rules +} + +func domainAddAlias(src string, rules []*domainAliasRule) []string { + dest := []string{src} + if rules == nil { + return dest + } + + for _, rule := range rules { + allIndexes := rule.re.FindAllSubmatchIndex([]byte(src), -1) + if idxNum := len(allIndexes); idxNum != 1 { + if idxNum > 1 { + log.Warnf("domain %s matches more than once on pattern %s", src, rule.pattern) + } + continue + } + for _, template := range rule.templates { + var domain []byte + // expand the template according allIndexes + domain = rule.re.ExpandString(domain, template, src, allIndexes[0]) + if len(domain) == 0 { + continue + } + dest = append(dest, string(domain)) + } + } + + log.Debugf("domainAddAlias src: %s, dest: %v", src, dest) + return dest +} + +func addToDomains(domains map[string]*lazyloadv1alpha1.Destinations, fh string) { + if domains[fh] != nil { + return + } + + allHost := []string{fh} + if hs := getDestination(fh); len(hs) > 0 { + allHost = append(allHost, hs...) + } + + domains[fh] = &lazyloadv1alpha1.Destinations{ + Hosts: allHost, + Status: lazyloadv1alpha1.Destinations_ACTIVE, + } +} diff --git a/controllers/model.go b/controllers/model.go index 8412f26..8c6eb66 100644 --- a/controllers/model.go +++ b/controllers/model.go @@ -6,6 +6,7 @@ package controllers import ( + "regexp" "sync" "slime.io/slime/framework/model" @@ -33,3 +34,9 @@ type LabelSvcCache struct { Data map[LabelItem]map[string]struct{} sync.RWMutex } + +type domainAliasRule struct { + pattern string + templates []string + re *regexp.Regexp +} diff --git a/controllers/servicefence_controller.go b/controllers/servicefence_controller.go index 25d1c4c..5319895 100644 --- a/controllers/servicefence_controller.go +++ b/controllers/servicefence_controller.go @@ -65,6 +65,7 @@ type ServicefenceReconciler struct { nsSvcCache *NsSvcCache labelSvcCache *LabelSvcCache defaultAddNamespaces []string + doAliasRules []*domainAliasRule } // NewReconciler returns a new reconcile.Reconciler @@ -89,6 +90,7 @@ func NewReconciler(cfg *lazyloadv1alpha1.Fence, mgr manager.Manager, env bootstr staleNamespaces: map[string]bool{}, enabledNamespaces: map[string]bool{}, defaultAddNamespaces: []string{env.Config.Global.IstioNamespace, env.Config.Global.SlimeNamespace}, + doAliasRules: newDomainAliasRules(cfg.DomainAliases), } // start service related cache @@ -308,7 +310,7 @@ func parseHost(sourceNs, h string) *types.NamespacedName { } func (r *ServicefenceReconciler) updateVisitedHostStatus(sf *lazyloadv1alpha1.ServiceFence) Diff { - domains := r.genDomains(sf) + domains := r.genDomains(sf, r.doAliasRules) delta := Diff{ Deleted: make([]string, 0), @@ -340,18 +342,20 @@ func (r *ServicefenceReconciler) updateVisitedHostStatus(sf *lazyloadv1alpha1.Se return delta } -func (r *ServicefenceReconciler) genDomains(sf *lazyloadv1alpha1.ServiceFence) map[string]*lazyloadv1alpha1.Destinations { +func (r *ServicefenceReconciler) genDomains(sf *lazyloadv1alpha1.ServiceFence, rules []*domainAliasRule) map[string]*lazyloadv1alpha1.Destinations { domains := make(map[string]*lazyloadv1alpha1.Destinations) - addDomainsWithHost(domains, sf, r.nsSvcCache) - addDomainsWithLabelSelector(domains, sf, r.labelSvcCache) - addDomainsWithMetricStatus(domains, sf) + addDomainsWithHost(domains, sf, r.nsSvcCache, rules) + addDomainsWithLabelSelector(domains, sf, r.labelSvcCache, rules) + addDomainsWithMetricStatus(domains, sf, rules) return domains } // update domains with spec.host -func addDomainsWithHost(domains map[string]*lazyloadv1alpha1.Destinations, sf *lazyloadv1alpha1.ServiceFence, nsSvcCache *NsSvcCache) { +func addDomainsWithHost(domains map[string]*lazyloadv1alpha1.Destinations, sf *lazyloadv1alpha1.ServiceFence, nsSvcCache *NsSvcCache, + rules []*domainAliasRule, +) { checkStatus := func(now int64, strategy *lazyloadv1alpha1.RecyclingStrategy) lazyloadv1alpha1.Destinations_Status { switch { case strategy.Stable != nil: @@ -373,15 +377,15 @@ func addDomainsWithHost(domains map[string]*lazyloadv1alpha1.Destinations, sf *l for h, strategy := range sf.Spec.Host { if strings.HasSuffix(h, "/*") { // handle namespace level host, like 'default/*' - handleNsHost(h, domains, nsSvcCache) + handleNsHost(h, domains, nsSvcCache, rules) } else { // handle service level host, like 'a.default.svc.cluster.local' or 'www.netease.com' - handleSvcHost(h, strategy, checkStatus, domains, sf) + handleSvcHost(h, strategy, checkStatus, domains, sf, rules) } } } -func handleNsHost(h string, domains map[string]*lazyloadv1alpha1.Destinations, nsSvcCache *NsSvcCache) { +func handleNsHost(h string, domains map[string]*lazyloadv1alpha1.Destinations, nsSvcCache *NsSvcCache, rules []*domainAliasRule) { hostParts := strings.Split(h, "/") if len(hostParts) != 2 { log.Errorf("%s is invalid host, skip", h) @@ -399,23 +403,27 @@ func handleNsHost(h string, domains map[string]*lazyloadv1alpha1.Destinations, n if !isValidHost(fullHost) { continue } - if domains[fullHost] != nil { - continue - } - // service relates to other services - if hs := getDestination(fullHost); len(hs) > 0 { - for i := 0; i < len(hs); { - hParts := strings.Split(hs[i], ".") - // ignore destSvc that in the same namespace - if hParts[1] == hostParts[0] { - hs[i], hs[len(hs)-1] = hs[len(hs)-1], hs[i] - hs = hs[:len(hs)-1] - } else { - i++ - } + + fullHosts := domainAddAlias(fullHost, rules) + for _, fh := range fullHosts { + if domains[fh] != nil { + continue } + // service relates to other services + if hs := getDestination(fh); len(hs) > 0 { + for i := 0; i < len(hs); { + hParts := strings.Split(hs[i], ".") + // ignore destSvc that in the same namespace + if hParts[1] == hostParts[0] { + hs[i], hs[len(hs)-1] = hs[len(hs)-1], hs[i] + hs = hs[:len(hs)-1] + } else { + i++ + } + } - allHost = append(allHost, hs...) + allHost = append(allHost, hs...) + } } } domains[h] = &lazyloadv1alpha1.Destinations{ @@ -426,31 +434,35 @@ func handleNsHost(h string, domains map[string]*lazyloadv1alpha1.Destinations, n func handleSvcHost(fullHost string, strategy *lazyloadv1alpha1.RecyclingStrategy, checkStatus func(now int64, strategy *lazyloadv1alpha1.RecyclingStrategy) lazyloadv1alpha1.Destinations_Status, - domains map[string]*lazyloadv1alpha1.Destinations, sf *lazyloadv1alpha1.ServiceFence, + domains map[string]*lazyloadv1alpha1.Destinations, sf *lazyloadv1alpha1.ServiceFence, rules []*domainAliasRule, ) { now := time.Now().Unix() if !isValidHost(fullHost) { return } - if domains[fullHost] != nil { - return - } - allHost := []string{fullHost} - if hs := getDestination(fullHost); len(hs) > 0 { - allHost = append(allHost, hs...) - } + fullHosts := domainAddAlias(fullHost, rules) + for _, fh := range fullHosts { + if domains[fh] != nil { + return + } - domains[fullHost] = &lazyloadv1alpha1.Destinations{ - Hosts: allHost, - Status: checkStatus(now, strategy), + allHost := []string{fh} + if hs := getDestination(fh); len(hs) > 0 { + allHost = append(allHost, hs...) + } + + domains[fh] = &lazyloadv1alpha1.Destinations{ + Hosts: allHost, + Status: checkStatus(now, strategy), + } } } // update domains with spec.labelSelector func addDomainsWithLabelSelector(domains map[string]*lazyloadv1alpha1.Destinations, sf *lazyloadv1alpha1.ServiceFence, - labelSvcCache *LabelSvcCache, + labelSvcCache *LabelSvcCache, rules []*domainAliasRule, ) { labelSvcCache.RLock() defer labelSvcCache.RUnlock() @@ -493,16 +505,10 @@ func addDomainsWithLabelSelector(domains map[string]*lazyloadv1alpha1.Destinatio if !isValidHost(fullHost) { continue } - if domains[fullHost] != nil { - continue - } - allHost := []string{fullHost} - if hs := getDestination(fullHost); len(hs) > 0 { - allHost = append(allHost, hs...) - } - domains[fullHost] = &lazyloadv1alpha1.Destinations{ - Hosts: allHost, - Status: lazyloadv1alpha1.Destinations_ACTIVE, + + fullHosts := domainAddAlias(fullHost, rules) + for _, fh := range fullHosts { + addToDomains(domains, fh) } } @@ -510,7 +516,7 @@ func addDomainsWithLabelSelector(domains map[string]*lazyloadv1alpha1.Destinatio } // update domains with Status.MetricStatus -func addDomainsWithMetricStatus(domains map[string]*lazyloadv1alpha1.Destinations, sf *lazyloadv1alpha1.ServiceFence) { +func addDomainsWithMetricStatus(domains map[string]*lazyloadv1alpha1.Destinations, sf *lazyloadv1alpha1.ServiceFence, rules []*domainAliasRule) { for metricName := range sf.Status.MetricStatus { metricName = strings.Trim(metricName, "{}") if !strings.HasPrefix(metricName, "destination_service") && !strings.HasPrefix(metricName, "request_host") { @@ -530,18 +536,10 @@ func addDomainsWithMetricStatus(domains map[string]*lazyloadv1alpha1.Destination if !isValidHost(fullHost) { continue } - if domains[fullHost] != nil { // XXX merge with status from config - continue - } - allHost := []string{fullHost} - if hs := getDestination(fullHost); len(hs) > 0 { - allHost = append(allHost, hs...) - } - - domains[fullHost] = &lazyloadv1alpha1.Destinations{ - Hosts: allHost, - Status: lazyloadv1alpha1.Destinations_ACTIVE, + fullHosts := domainAddAlias(fullHost, rules) + for _, fh := range fullHosts { + addToDomains(domains, fh) } } } diff --git a/lazyload_tutorials.md b/lazyload_tutorials.md index c15ae4a..8d248a6 100644 --- a/lazyload_tutorials.md +++ b/lazyload_tutorials.md @@ -15,6 +15,7 @@ - [Dependency on specific services](#dependency-on-specific-services) - [Dependency on all services in specific namespaces](#dependency-on-all-services-in--specific-namespaces) - [Dependency on all services with specific labels](#dependency-on-all-services-with-specific-labels) + - [Support for custom service dependency aliases](#Support for custom service dependency aliases) - [Logs output to local file and rotate](#logs-output-to-local-file-and-rotate) - [Creating Storage Volumes](#creating-storage-volumes) - [Declaring mount information in SlimeBoot](#declaring-mount-information-in-slimeboot) @@ -603,6 +604,92 @@ spec: +### Support for custom service dependency aliases + +In some scenarios, we want Lazyload to add some additional dependent services in based on the known dependent service. + +Users can configure `general.domainAliases` to provide custom conversion relationships to achieve the requirements. `general.domainAliases` consists of one or many `domainAlias`. `domainAlias` consists of a matching rule `pattern` and a transformation rule `templates`. `pattern` contains only one matching rule, while `templates` can contain multiple conversion rules. + +For example, we want to add `. .svc.cluster.local` with an additional `. .mailsaas` service dependency, you can configure it like this + +```yaml +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + module: + - name: lazyload-test + kind: lazyload + enable: true + general: + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "9080" + domainAliases: + - pattern: '(?P[^\.]+)\.(?P[^\.]+)\.svc\.cluster\.local$' + templates: + - "$namespace.$service.mailsaas" + #... +``` + +Servicefence will look like this + +```yaml +apiVersion: microservice.slime.io/v1alpha1 +kind: ServiceFence +metadata: + name: ratings + namespace: default +spec: + enable: true + host: + details.default.svc.cluster.local: # static dependent service + stable: {} +status: + domains: + default.details.mailsaas: # static dependent service converted result + hosts: + - default.details.mailsaas + default.productpage.mailsaas: # dynamic dependent service converted result + hosts: + - default.productpage.mailsaas + details.default.svc.cluster.local: + hosts: + - details.default.svc.cluster.local + productpage.default.svc.cluster.local: + hosts: + - productpage.default.svc.cluster.local + metricStatus: + '{destination_service="productpage.default.svc.cluster.local"}': "1" # dynamic dependent service +``` + +Sidecar will look like this + +```yaml +apiVersion: networking.istio.io/v1beta1 +kind: Sidecar +metadata: + name: ratings + namespace: default +spec: + egress: + - hosts: + - '*/default.details.mailsaas' # static dependent service converted result + - '*/default.productpage.mailsaas' # dynamic dependent service converted result + - '*/details.default.svc.cluster.local' + - '*/productpage.default.svc.cluster.local' + - istio-system/* + - mesh-operator/* + workloadSelector: + labels: + app: ratings +``` + + + + + ### Logs output to local file and rotate slime logs are output to stdout by default, specifying `spec.module.global.log.logRotate` equal to `true` in the SlimeBoot CR resource will output the logs locally and start the log rotation, no longer to standard output. diff --git a/lazyload_tutorials_zh.md b/lazyload_tutorials_zh.md index d7e6b70..59e3e6d 100644 --- a/lazyload_tutorials_zh.md +++ b/lazyload_tutorials_zh.md @@ -15,6 +15,7 @@ - [依赖某个服务](#依赖某个服务) - [依赖某个namespace所有服务](#依赖某个namespace所有服务) - [依赖具有某个label的所有服务](#依赖具有某个label的所有服务) + - [支持自定义服务依赖别名](#支持自定义服务依赖别名) - [日志输出到本地并轮转](#日志输出到本地并轮转) - [创建存储卷](#创建存储卷) - [在SlimeBoot中声明挂载信息](#在slimeboot中声明挂载信息) @@ -597,6 +598,92 @@ spec: +### 支持自定义服务依赖别名 + +在某些场景,我们希望懒加载根据已知的服务依赖,添加一些额外的服务依赖进去。 + +用户可以通过配置`general.domainAliases`,提供自定义的转换关系,实现需求。`general.domainAliases`包含多个`domainAlias`,每个`domainAlias`由匹配规则`pattern`和转换规则`templates`组成。`pattern`只包含一个匹配规则,`templates`则可以包含多个转换规则。 + +举个例子,我们希望添加`..svc.cluster.local`时,额外添加`..mailsaas`的服务依赖,则可以这么配置 + +```yaml +apiVersion: config.netease.com/v1alpha1 +kind: SlimeBoot +metadata: + name: lazyload + namespace: mesh-operator +spec: + module: + - name: lazyload-test + kind: lazyload + enable: true + general: + wormholePort: # replace to your application service ports, and extend the list in case of multi ports + - "9080" + domainAliases: + - pattern: '(?P[^\.]+)\.(?P[^\.]+)\.svc\.cluster\.local$' + templates: + - "$namespace.$service.mailsaas" + #... +``` + +对应的servicefence会这样 + +```yaml +apiVersion: microservice.slime.io/v1alpha1 +kind: ServiceFence +metadata: + name: ratings + namespace: default +spec: + enable: true + host: + details.default.svc.cluster.local: # static dependent service + stable: {} +status: + domains: + default.details.mailsaas: # static dependent service converted result + hosts: + - default.details.mailsaas + default.productpage.mailsaas: # dynamic dependent service converted result + hosts: + - default.productpage.mailsaas + details.default.svc.cluster.local: + hosts: + - details.default.svc.cluster.local + productpage.default.svc.cluster.local: + hosts: + - productpage.default.svc.cluster.local + metricStatus: + '{destination_service="productpage.default.svc.cluster.local"}': "1" # dynamic dependent service +``` + +sidecar则是这样 + +```yaml +apiVersion: networking.istio.io/v1beta1 +kind: Sidecar +metadata: + name: ratings + namespace: default +spec: + egress: + - hosts: + - '*/default.details.mailsaas' # static dependent service converted result + - '*/default.productpage.mailsaas' # dynamic dependent service converted result + - '*/details.default.svc.cluster.local' + - '*/productpage.default.svc.cluster.local' + - istio-system/* + - mesh-operator/* + workloadSelector: + labels: + app: ratings +``` + + + + + ### 日志输出到本地并轮转 slime的日志默认输出到标准输出,指定SlimeBoot CR资源中`spec.module.global.log.logRotate`等于`true`会将日志输出到本地并启动日志轮转,不再输出到标准输出。