delete all rules
[msb/service-mesh.git] / msb2pilot / src / msb2pilot / pilot / msb.go
1 /**
2  * Copyright (c) 2018 ZTE Corporation.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * and the Apache License 2.0 which both accompany this distribution,
6  * and are available at http://www.eclipse.org/legal/epl-v10.html
7  * and http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Contributors:
10  *     ZTE - initial Project
11  */
12 package pilot
13
14 import (
15         "bytes"
16         "msb2pilot/log"
17         "msb2pilot/models"
18         "msb2pilot/msb"
19         "os"
20         "regexp"
21         "strings"
22
23         istioModel "istio.io/istio/pilot/pkg/model"
24 )
25
26 var (
27         cachedServices []*models.MsbService
28 )
29
30 const (
31         routerulePrefix = "msbcustom."
32 )
33
34 func SyncMsbData(newServices []*models.MsbService) {
35         if len(cachedServices) == 0 {
36                 deleteAllMsbRules()
37         }
38         log.Log.Debug("sync msb rewrite rule to pilot")
39         createServices, updateServices, deleteServices := compareServices(cachedServices, newServices)
40
41         saveService(OperationCreate, createServices)
42         saveService(OperationUpdate, updateServices)
43         saveService(OperationDelete, deleteServices)
44
45         cachedServices = newServices
46 }
47
48 func saveService(operation Operation, services []*models.MsbService) {
49         if len(services) == 0 {
50                 return
51         }
52         configs, err := parseServiceToConfig(services)
53         if err != nil {
54                 log.Log.Error("param parse error", err)
55                 return
56         }
57         fails := Save(operation, configs)
58         log.Log.Debug("%d services need to %s, %d fails. \n", len(services), operation, len(fails))
59 }
60
61 func deleteAllMsbRules() {
62         log.Log.Informational("delete all msb rules")
63         configs, err := List("route-rule", "")
64
65         if err != nil {
66                 return
67         }
68
69         deleteList := msbRuleFilter(configs)
70
71         Save(OperationDelete, deleteList)
72 }
73
74 func msbRuleFilter(configs []istioModel.Config) []istioModel.Config {
75         res := make([]istioModel.Config, 0, len(configs))
76
77         for _, config := range configs {
78                 if strings.HasPrefix(config.Name, routerulePrefix) {
79                         res = append(res, config)
80                 }
81         }
82
83         return res
84 }
85
86 func compareServices(oldServices, newServices []*models.MsbService) (createServices, updateServices, deleteServices []*models.MsbService) {
87         oldServiceMap := toServiceMap(oldServices)
88         newServiceMap := toServiceMap(newServices)
89
90         for key, newService := range newServiceMap {
91                 if oldService, exist := oldServiceMap[key]; exist {
92                         // service exist: check whether need to update
93                         if oldService.ModifyIndex != newService.ModifyIndex {
94                                 updateServices = append(updateServices, newService)
95                         }
96                 } else {
97                         // service not exist: add
98                         createServices = append(createServices, newService)
99                 }
100
101                 delete(oldServiceMap, key)
102         }
103
104         for _, service := range oldServiceMap {
105                 deleteServices = append(deleteServices, service)
106         }
107
108         return
109 }
110
111 func toServiceMap(services []*models.MsbService) map[string]*models.MsbService {
112         serviceMap := make(map[string]*models.MsbService)
113
114         for _, service := range services {
115                 serviceMap[service.ServiceName] = service
116         }
117
118         return serviceMap
119 }
120
121 func parseServiceToConfig(services []*models.MsbService) ([]istioModel.Config, error) {
122         publishServices := getPublishServiceMap()
123         apiGateway := os.Getenv(models.EnvApiGatewayName)
124         var buf bytes.Buffer
125         for _, service := range services {
126                 if publishService, exist := publishServices[getPublishServiceKey(service)]; exist {
127
128                         if service.ConsulLabels.BaseInfo != nil {
129                                 rule := createRouteRule(apiGateway, publishService.PublishUrl, service.ServiceName, service.ConsulLabels.BaseInfo.Url)
130                                 buf.WriteString(rule)
131                         }
132                 }
133         }
134         return ParseParam(buf.String())
135 }
136
137 func getPublishServiceKey(svc *models.MsbService) string {
138         res := svc.ServiceName
139
140         if svc.ConsulLabels.BaseInfo != nil {
141                 res += svc.ConsulLabels.BaseInfo.Version
142         }
143
144         if svc.ConsulLabels.NameSpace != nil {
145                 res += svc.ConsulLabels.NameSpace.NameSpace
146         }
147
148         return res
149 }
150
151 func getPublishServiceMap() map[string]*models.PublishService {
152         publishServices := msb.GetAllPublishServices()
153
154         res := make(map[string]*models.PublishService)
155
156         for _, svc := range publishServices {
157                 key := svc.ServiceName + svc.Version + svc.NameSpace
158                 res[key] = svc
159         }
160
161         return res
162 }
163
164 func createRouteRule(sourceService, sourcePath, targetService, targetPath string) string {
165         if sourcePath == "" {
166                 sourcePath = "/"
167         }
168         if targetPath == "" {
169                 targetPath = "/"
170         }
171         // rule name must consist of lower case alphanuberic charactoers, '-' or '.'. and must start and end with an alphanumberic charactore
172         r := regexp.MustCompile("[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*")
173         strs := r.FindAllString(targetService, -1)
174         name := routerulePrefix + strings.Join(strs, "")
175         name = strings.ToLower(name)
176
177         rule := `{
178 "apiVersion": "config.istio.io/v1alpha2",
179 "kind": "RouteRule",
180 "metadata": {
181   "name": "` + name + `"
182 },
183 "spec": {
184   "destination":{
185     "name":"` + sourceService + `"
186   },
187   "match":{
188     "request":{
189       "headers": {
190         "uri": {
191           "prefix": "` + sourcePath + `"
192         }
193       }
194     }
195   },
196   "rewrite": {
197     "uri": "` + targetPath + `"
198   },
199   "route":[
200     {
201       "destination":{
202         "name":"` + targetService + `"
203       }
204     }
205   ]
206 }
207 }
208
209 `
210         return rule
211 }