Egress Implementation (#18)
* Egress Implementation
* Addressing Review Comments
* egress changes
* Added istio pilot support
* Added Doc for egress
* Addressing review comments
* Addressed Review comments
* update go.mod
* Addressing review comments
* addressing review comment
* unit test
diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go
index 2574fdb..819929e 100644
--- a/bootstrap/bootstrap.go
+++ b/bootstrap/bootstrap.go
@@ -30,9 +30,12 @@
"github.com/go-mesh/mesher/resolver"
"github.com/go-chassis/go-chassis"
+ "github.com/go-chassis/go-chassis/core/handler"
chassisHandler "github.com/go-chassis/go-chassis/core/handler"
"github.com/go-chassis/go-chassis/core/lager"
"github.com/go-chassis/go-chassis/core/metadata"
+ "github.com/go-mesh/mesher/control"
+ "github.com/go-mesh/mesher/pkg/egress"
"github.com/go-mesh/mesher/pkg/metrics"
"github.com/go-mesh/mesher/pkg/runtime"
"github.com/go-mesh/openlogging"
@@ -64,6 +67,14 @@
} else {
lager.Logger.Infof("local service ports is [%v]", cmd.Configs.PortsMap)
}
+ err := egress.Init()
+ if err != nil {
+ return err
+ }
+
+ if err := control.Init(); err != nil {
+ return err
+ }
return nil
@@ -120,3 +131,18 @@
chassis.SetDefaultConsumerChains(consumerChainMap)
chassis.SetDefaultProviderChains(providerChainMap)
}
+
+//InitEgressChain init the egress handler chain
+func InitEgressChain() error {
+ egresschain := strings.Join([]string{
+ handler.RatelimiterConsumer,
+ handler.BizkeeperConsumer,
+ handler.Transport,
+ }, ",")
+
+ egressChainMap := map[string]string{
+ common.ChainConsumerEgress: egresschain,
+ }
+
+ return handler.CreateChains(common.ConsumerEgress, egressChainMap)
+}
diff --git a/common/common.go b/common/common.go
index 6eb7cff..5c1ba00 100644
--- a/common/common.go
+++ b/common/common.go
@@ -52,4 +52,8 @@
const (
ChainConsumerOutgoing = "outgoing"
ChainProviderIncoming = "incoming"
+ ChainConsumerEgress = "egress"
)
+
+//ConsumerEgress constant for egress
+const ConsumerEgress = "ConsumerEgress"
diff --git a/conf/egress.yaml b/conf/egress.yaml
new file mode 100644
index 0000000..de36a85
--- /dev/null
+++ b/conf/egress.yaml
@@ -0,0 +1,17 @@
+#egress:
+# infra: cse # pilot or cse
+# address: http://istio-pilot.istio-system:15010
+#egressRule:
+# google-ext:
+# - hosts:
+# - "google.com"
+# - "*.yahoo.com"
+# ports:
+# - port: 80
+# protocol: HTTP
+# facebook-ext:
+# - hosts:
+# - "www.facebook.com"
+# ports:
+# - port: 80
+# protocol: HTTP
diff --git a/config/config.go b/config/config.go
index db7ea4d..3062cb2 100644
--- a/config/config.go
+++ b/config/config.go
@@ -35,10 +35,12 @@
//Constant for mesher conf file
const (
- ConfFile = "mesher.yaml"
+ ConfFile = "mesher.yaml"
+ EgressConfFile = "egress.yaml"
)
var mesherConfig *MesherConfig
+var egressConfig *EgressConfig
//GetConfig returns mesher config
func GetConfig() *MesherConfig {
@@ -53,14 +55,19 @@
*mesherConfig = *nc
}
+//GetEgressConfig returns Egress config
+func GetEgressConfig() *EgressConfig {
+ return egressConfig
+}
+
//GetConfigFilePath returns config file path
-func GetConfigFilePath() (string, error) {
- if cmd.Configs.ConfigFile == "" {
+func GetConfigFilePath(key string) (string, error) {
+ if cmd.Configs.ConfigFile == "" || key == EgressConfFile {
wd, err := fileutil.GetWorkDir()
if err != nil {
return "", err
}
- return filepath.Join(wd, "conf", ConfFile), nil
+ return filepath.Join(wd, "conf", key), nil
}
return cmd.Configs.ConfigFile, nil
}
@@ -85,12 +92,24 @@
if err != nil {
return err
}
- return yaml.Unmarshal([]byte(contents), mesherConfig)
+ if err := yaml.Unmarshal([]byte(contents), mesherConfig); err != nil {
+ return err
+ }
+
+ egressConfig = &EgressConfig{}
+ egressContents, err := GetConfigContents(EgressConfFile)
+ if err != nil {
+ return err
+ }
+ if err := yaml.Unmarshal([]byte(egressContents), egressConfig); err != nil {
+ return err
+ }
+ return nil
}
//GetConfigContents returns config contents
func GetConfigContents(key string) (string, error) {
- f, err := GetConfigFilePath()
+ f, err := GetConfigFilePath(key)
if err != nil {
return "", err
}
@@ -113,10 +132,15 @@
}
b, err := ioutil.ReadFile(f)
if err != nil {
- lager.Logger.Error("Can not read mesher.yaml: " + err.Error())
+ lager.Logger.Error("Can not read yaml file" + err.Error())
return ""
}
contents = string(b)
archaius.AddKeyValue(key, contents)
return contents
}
+
+// GetEgressEndpoints returns the pilot address
+func GetEgressEndpoints() string {
+ return egressConfig.Egress.Address
+}
diff --git a/config/config_test.go b/config/config_test.go
index 7913229..140f57b 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -15,9 +15,10 @@
)
func TestGetConfigFilePath(t *testing.T) {
+ var key = "mesher.yaml"
cmd.Init()
- f, _ := config.GetConfigFilePath()
- assert.Contains(t, f, "mesher.yaml")
+ f, _ := config.GetConfigFilePath(key)
+ assert.Contains(t, f, key)
}
var file = []byte(`
diff --git a/config/egress.go b/config/egress.go
new file mode 100644
index 0000000..53b44fd
--- /dev/null
+++ b/config/egress.go
@@ -0,0 +1,42 @@
+/*
+ * 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 config
+
+//EgressConfig is the struct having info about egress rule destinations
+type EgressConfig struct {
+ Egress Egress `yaml:"egress"`
+ Destinations map[string][]*EgressRule `yaml:"egressRule"`
+}
+
+// Egress define where rule comes from
+type Egress struct {
+ Infra string `yaml:"infra"`
+ Address string `yaml:"address"`
+}
+
+//EgressRule has hosts and ports information
+type EgressRule struct {
+ Hosts []string `yaml:"hosts"`
+ Ports []*EgressPort `yaml:"ports"`
+}
+
+//EgressPort protocol and the corresponding port
+type EgressPort struct {
+ Port int32 `yaml:"port"`
+ Protocol string `yaml:"protocol"`
+}
diff --git a/control/istio/cache.go b/control/istio/cache.go
new file mode 100644
index 0000000..48ef996
--- /dev/null
+++ b/control/istio/cache.go
@@ -0,0 +1,9 @@
+package istio
+
+import "github.com/patrickmn/go-cache"
+
+//save configs
+var (
+ //key is service name
+ EgressConfigCache = cache.New(0, 0)
+)
diff --git a/control/istio/panel.go b/control/istio/panel.go
new file mode 100644
index 0000000..72f849c
--- /dev/null
+++ b/control/istio/panel.go
@@ -0,0 +1,55 @@
+package istio
+
+import (
+ "github.com/go-chassis/go-chassis/control"
+ "github.com/go-chassis/go-chassis/core/config/model"
+ "github.com/go-chassis/go-chassis/core/invocation"
+ "github.com/go-chassis/go-chassis/third_party/forked/afex/hystrix-go/hystrix"
+ meshercontrol "github.com/go-mesh/mesher/control"
+ "github.com/go-mesh/mesher/pkg/egress"
+)
+
+func init() {
+ meshercontrol.InstallPlugin("pilot", newPilotPanel)
+}
+
+//PilotPanel pull configs from istio pilot
+type PilotPanel struct {
+}
+
+func newPilotPanel(options meshercontrol.Options) control.Panel {
+ SaveToEgressCache(egress.DefaultEgress.FetchEgressRule())
+ return &PilotPanel{}
+}
+
+//GetEgressRule get egress config
+func (p *PilotPanel) GetEgressRule() []control.EgressConfig {
+ c, ok := EgressConfigCache.Get("")
+ if !ok {
+
+ return nil
+ }
+ return c.([]control.EgressConfig)
+}
+
+//GetCircuitBreaker return command , and circuit breaker settings
+func (p *PilotPanel) GetCircuitBreaker(inv invocation.Invocation, serviceType string) (string, hystrix.CommandConfig) {
+ return "", hystrix.CommandConfig{}
+
+}
+
+//GetLoadBalancing get load balancing config
+func (p *PilotPanel) GetLoadBalancing(inv invocation.Invocation) control.LoadBalancingConfig {
+ return control.LoadBalancingConfig{}
+
+}
+
+//GetRateLimiting get rate limiting config
+func (p *PilotPanel) GetRateLimiting(inv invocation.Invocation, serviceType string) control.RateLimitingConfig {
+ return control.RateLimitingConfig{}
+}
+
+//GetFaultInjection get Fault injection config
+func (p *PilotPanel) GetFaultInjection(inv invocation.Invocation) model.Fault {
+ return model.Fault{}
+}
diff --git a/control/istio/transfer.go b/control/istio/transfer.go
new file mode 100644
index 0000000..064e149
--- /dev/null
+++ b/control/istio/transfer.go
@@ -0,0 +1,32 @@
+package istio
+
+import (
+ "github.com/go-chassis/go-chassis/control"
+ "github.com/go-mesh/mesher/config"
+)
+
+//SaveToEgressCache save the egress rules in the cache
+func SaveToEgressCache(egressConfigFromPilot map[string][]*config.EgressRule) {
+ {
+ var egressconfig []control.EgressConfig
+ for _, v := range egressConfigFromPilot {
+ for _, v1 := range v {
+ var Ports []*control.EgressPort
+ for _, v2 := range v1.Ports {
+ p := control.EgressPort{
+ Port: (*v2).Port,
+ Protocol: (*v2).Protocol,
+ }
+ Ports = append(Ports, &p)
+ }
+ c := control.EgressConfig{
+ Hosts: v1.Hosts,
+ Ports: Ports,
+ }
+
+ egressconfig = append(egressconfig, c)
+ }
+ }
+ EgressConfigCache.Set("", egressconfig, 0)
+ }
+}
diff --git a/control/panel.go b/control/panel.go
new file mode 100644
index 0000000..40e43d5
--- /dev/null
+++ b/control/panel.go
@@ -0,0 +1,40 @@
+package control
+
+import (
+ "fmt"
+ "github.com/go-chassis/go-chassis/control"
+ "github.com/go-chassis/go-chassis/core/config"
+)
+
+var panelPlugin = make(map[string]func(options Options) control.Panel)
+
+//DefaultPanelEgress get fetch config
+var DefaultPanelEgress control.Panel
+
+//InstallPlugin install implementation
+func InstallPlugin(name string, f func(options Options) control.Panel) {
+ panelPlugin[name] = f
+}
+
+//Options is options
+type Options struct {
+ Address string
+}
+
+//Init initialize DefaultPanel
+func Init() error {
+ infra := config.GlobalDefinition.Panel.Infra
+ if infra == "" || infra == "archaius" {
+ return nil
+ }
+
+ f, ok := panelPlugin[infra]
+ if !ok {
+ return fmt.Errorf("do not support [%s] panel", infra)
+ }
+
+ DefaultPanelEgress = f(Options{
+ Address: config.GlobalDefinition.Panel.Settings["address"],
+ })
+ return nil
+}
diff --git a/control/struct.go b/control/struct.go
new file mode 100644
index 0000000..3f11b4a
--- /dev/null
+++ b/control/struct.go
@@ -0,0 +1,13 @@
+package control
+
+//EgressConfig is a standardized model
+type EgressConfig struct {
+ Hosts []string
+ Ports []*EgressPort
+}
+
+//EgressPort protocol and the corresponding port
+type EgressPort struct {
+ Port int32
+ Protocol string
+}
diff --git a/docs/egress.md b/docs/egress.md
new file mode 100644
index 0000000..5a4ce8a
--- /dev/null
+++ b/docs/egress.md
@@ -0,0 +1,43 @@
+# Egress
+## Introduction
+
+Mesher support Egress for your service, so that you can access any publicly accessible service from your microservice.
+
+## Configuration
+
+The egress related configurations is all in egress.yaml.
+
+**infra**
+> *(optional, string)* specifies from where the egress configuration need to be taken supports two values cse or pilot ,
+ cse means the egress configuration from egress.yaml file,
+ pilot means egress configuaration are taken from pilot of istio,
+ default is *cse*
+
+**address**
+> *(optional, string)* The end point of pilot from which configuration need to be fetched.
+
+**hosts**
+> *(optional, []string)* host associated with external service, could be a DNS name with wildcard prefix
+
+**ports.port**
+> *(optional, int)* The port associated with the external service, default is *80*
+
+**ports.protocol**
+> *(optional, int)* The protocol associated with the external service,supports only http default is *HTTP*
+
+## example
+edit egress.yaml
+
+```yaml
+egress:
+ infra: cse # pilot or cse
+ address: http://istio-pilot.istio-system:15010
+egressRule:
+ google-ext:
+ - hosts:
+ - "www.google.com"
+ - "*.yahoo.com"
+ ports:
+ - port: 80
+ protocol: HTTP
+```
diff --git a/go.mod b/go.mod
index 86cae6c..c3983db 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@
github.com/emicklei/go-restful-swagger12 v0.0.0-20170926063155-7524189396c6 // indirect
github.com/envoyproxy/go-control-plane v0.6.0
github.com/ghodss/yaml v1.0.0 // indirect
- github.com/go-chassis/go-archaius v0.0.0-20181108111652-ab19b4eae276
+ github.com/go-chassis/go-archaius v0.0.0-20181119064113-720d998498f3
github.com/go-chassis/go-cc-client v0.0.0-20181102101915-dea430061a34
github.com/go-chassis/go-chassis v1.1.3
diff --git a/mesher.go b/mesher.go
index 128aa25..93f41e3 100644
--- a/mesher.go
+++ b/mesher.go
@@ -16,6 +16,11 @@
_ "github.com/go-mesh/mesher/protocol/http"
"github.com/go-mesh/mesher/server"
+
+ _ "github.com/go-mesh/mesher/pkg/egress/archaius"
+ _ "github.com/go-mesh/mesher/pkg/egress/pilot"
+
+ _ "github.com/go-mesh/mesher/control/istio"
)
func main() {
diff --git a/pkg/egress/archaius/egress.go b/pkg/egress/archaius/egress.go
new file mode 100644
index 0000000..203830d
--- /dev/null
+++ b/pkg/egress/archaius/egress.go
@@ -0,0 +1,86 @@
+/*
+ * 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 archaius
+
+import (
+ "fmt"
+ "github.com/go-mesh/mesher/config"
+ "github.com/go-mesh/mesher/pkg/egress"
+ "sync"
+)
+
+//Egress is cse Egress service
+type Egress struct {
+}
+
+//FetchEgressRule return all rules
+func (r *Egress) FetchEgressRule() map[string][]*config.EgressRule {
+ return GetEgressRule()
+}
+
+//SetEgressRule set rules
+func (r *Egress) SetEgressRule(rr map[string][]*config.EgressRule) {
+ SetEgressRule(rr)
+}
+
+//Init init egress config
+func (r *Egress) Init(op egress.Options) error {
+ // the manager use dests to init, so must init after dests
+ if err := initEgressManager(); err != nil {
+ return err
+ }
+ return refresh()
+}
+
+// refresh all the egress config
+func refresh() error {
+ configs := config.GetEgressConfig()
+ ok, _ := egress.ValidateEgressRule(configs.Destinations)
+ if !ok {
+ err := fmt.Errorf("Egress rule type assertion fail, key: %s", configs.Destinations)
+ return err
+ }
+
+ dests = configs.Destinations
+ return nil
+}
+
+func newEgress() (egress.Egress, error) {
+ return &Egress{}, nil
+}
+
+var dests = make(map[string][]*config.EgressRule)
+var lock sync.RWMutex
+
+// GetEgressRule get egress rule
+func GetEgressRule() map[string][]*config.EgressRule {
+ lock.RLock()
+ defer lock.RUnlock()
+ return dests
+}
+
+// SetEgressRule set egress rule
+func SetEgressRule(rule map[string][]*config.EgressRule) {
+ lock.RLock()
+ defer lock.RUnlock()
+ dests = rule
+}
+
+func init() {
+ egress.InstallEgressService("cse", newEgress)
+}
diff --git a/pkg/egress/archaius/egress_manager.go b/pkg/egress/archaius/egress_manager.go
new file mode 100644
index 0000000..23d642d
--- /dev/null
+++ b/pkg/egress/archaius/egress_manager.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 archaius
+
+import (
+ "gopkg.in/yaml.v2"
+ "path/filepath"
+ "strings"
+
+ "github.com/go-chassis/go-archaius"
+ "github.com/go-chassis/go-archaius/core"
+ "github.com/go-chassis/go-archaius/sources/file-source"
+ "github.com/go-chassis/go-chassis/core/lager"
+ "github.com/go-chassis/go-chassis/pkg/util/fileutil"
+ "github.com/go-mesh/mesher/config"
+ "github.com/go-mesh/mesher/pkg/egress"
+)
+
+//EgressYaml egress yaml file name
+const EgressYaml = "egress.yaml"
+
+type egressRuleEventListener struct{}
+
+// update egress rule of a service
+func (r *egressRuleEventListener) Event(e *core.Event) {
+ if e == nil {
+ lager.Logger.Warn("Event pointer is nil", nil)
+ return
+ }
+ if !strings.Contains(e.Key, EgressYaml) {
+ return
+ }
+ v := archaius.Get(e.Key)
+ if v == nil {
+ lager.Logger.Infof("[%s] Error getting egress key", e.Key)
+ return
+ }
+
+ var egressconfig config.EgressConfig
+
+ if err := yaml.Unmarshal([]byte(v.([]byte)), &egressconfig); err != nil {
+ lager.Logger.Error("yaml unmarshal failed", nil)
+ return
+ }
+ var egressRules []*config.EgressRule
+
+ for key, value := range egressconfig.Destinations {
+ ok, _ := egress.ValidateEgressRule(map[string][]*config.EgressRule{key: value})
+ if !ok {
+ lager.Logger.Warn("Validating Egress Rule Failed")
+ return
+
+ }
+ egressRules = append(egressRules, value...)
+ }
+
+ SetEgressRule(map[string][]*config.EgressRule{e.Key: egressRules})
+ lager.Logger.Infof("Update [%s] egress rule SUCCESS", e.Key)
+}
+
+// initialize the config mgr and add several sources
+func initEgressManager() error {
+ egressListener := &egressRuleEventListener{}
+ archaius.AddFile(filepath.Join(fileutil.GetConfDir(), EgressYaml), archaius.WithFileHandler(filesource.Convert2configMap))
+ archaius.RegisterListener(egressListener, ".*")
+
+ return nil
+}
diff --git a/pkg/egress/egress.go b/pkg/egress/egress.go
new file mode 100644
index 0000000..a21ffa6
--- /dev/null
+++ b/pkg/egress/egress.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 egress
+
+import (
+ "errors"
+ "github.com/go-chassis/go-chassis/control"
+ "github.com/go-mesh/mesher/config"
+ meshercontrol "github.com/go-mesh/mesher/control"
+ "regexp"
+ "sync"
+)
+
+var lock sync.RWMutex
+
+var plainHosts = make(map[string]*config.EgressRule)
+var regexHosts = make(map[string]*config.EgressRule)
+
+//Egress return egress rule, you can also set custom egress rule
+type Egress interface {
+ Init(Options) error
+ SetEgressRule(map[string][]*config.EgressRule)
+ FetchEgressRule() map[string][]*config.EgressRule
+}
+
+// ErrNoExist means if there is no egress implementation
+var ErrNoExist = errors.New("Egress not exists")
+var egressServices = make(map[string]func() (Egress, error))
+
+// DefaultEgress is current egress implementation
+var DefaultEgress Egress
+
+// InstallEgressService install egress service for developer
+func InstallEgressService(name string, f func() (Egress, error)) {
+ egressServices[name] = f
+}
+
+//BuildEgress create a Egress
+func BuildEgress(name string) error {
+ f, ok := egressServices[name]
+ if !ok {
+ return ErrNoExist
+ }
+ r, err := f()
+ if err != nil {
+ return err
+ }
+ DefaultEgress = r
+ return nil
+}
+
+//Match Check Egress rule matches
+func Match(hostname string) (bool, *control.EgressConfig) {
+ var EgressRules []control.EgressConfig
+ if meshercontrol.DefaultPanelEgress != nil {
+ EgressRules = meshercontrol.DefaultPanelEgress.GetEgressRule()
+ } else {
+ mapEgressRules := DefaultEgress.FetchEgressRule()
+ for _, value := range mapEgressRules {
+ for _, rule := range value {
+ var Ports []*control.EgressPort
+ for _, port := range rule.Ports {
+ p := control.EgressPort{
+ Port: (*port).Port,
+ Protocol: (*port).Protocol,
+ }
+ Ports = append(Ports, &p)
+ }
+ c := control.EgressConfig{
+ Hosts: rule.Hosts,
+ Ports: Ports,
+ }
+ EgressRules = append(EgressRules, c)
+ }
+ }
+ }
+
+ for _, egress := range EgressRules {
+
+ for _, host := range egress.Hosts {
+ // Check host length greater than 0 and does not
+ // start with *
+ if len(host) > 0 && string(host[0]) != "*" {
+ if host == hostname {
+ return true, &egress
+ }
+ } else if string(host[0]) == "*" {
+ substring := host[1:]
+ match, _ := regexp.MatchString(substring+"$", hostname)
+ if match == true {
+ return true, &egress
+ }
+ }
+ }
+ }
+ return false, nil
+}
diff --git a/pkg/egress/egress_config.go b/pkg/egress/egress_config.go
new file mode 100644
index 0000000..d231db7
--- /dev/null
+++ b/pkg/egress/egress_config.go
@@ -0,0 +1,166 @@
+/*
+ * 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 egress
+
+import (
+ "crypto/tls"
+ "errors"
+ "fmt"
+ "regexp"
+ "strings"
+
+ gochassisconfig "github.com/go-chassis/go-chassis/core/config"
+ "github.com/go-chassis/go-chassis/core/lager"
+ chassisTLS "github.com/go-chassis/go-chassis/core/tls"
+ "github.com/go-chassis/go-chassis/pkg/util/iputil"
+ "github.com/go-mesh/mesher/config"
+)
+
+const (
+ dns1123LabelMaxLength int = 63
+ dns1123LabelFmt string = "[a-zA-Z0-9]([-a-z-A-Z0-9]*[a-zA-Z0-9])?"
+ wildcardPrefix string = "\\*|(\\*)?" + dns1123LabelFmt
+ DefaultEgressType = "cse"
+ // EgressTLS defines tls prefix
+ EgressTLS = "egress"
+)
+
+var (
+ dns1123LabelRegexp = regexp.MustCompile("^" + dns1123LabelFmt + "$")
+ wildcardPrefixRegexp = regexp.MustCompile("^" + wildcardPrefix + "$")
+)
+
+// Init initialize Egress config
+func Init() error {
+
+ // init dests
+ egressConfigFromFile := config.GetEgressConfig()
+ BuildEgress(GetEgressType(egressConfigFromFile.Egress))
+
+ op, err := getSpecifiedOptions()
+ if err != nil {
+ return fmt.Errorf("Egress options error: %v", err)
+ }
+ DefaultEgress.Init(op)
+ lager.Logger.Info("Egress init success")
+ return nil
+}
+
+//ValidateEgressRule validate the Egress rules of each service
+func ValidateEgressRule(rules map[string][]*config.EgressRule) (bool, error) {
+ for _, rule := range rules {
+ for _, egressrule := range rule {
+ if len(egressrule.Hosts) == 0 {
+ return false, errors.New("Egress rule should have atleast one host")
+ }
+ for _, host := range egressrule.Hosts {
+ err := ValidateHostName(host)
+ if err != nil {
+ return false, err
+ }
+ }
+ }
+
+ }
+ return true, nil
+}
+
+//ValidateHostName validates the host
+func ValidateHostName(host string) error {
+ if len(host) > 255 {
+ return fmt.Errorf("host name %q too long (max 255)", host)
+ }
+ if len(host) == 0 {
+ return fmt.Errorf("empty host name not allowed")
+ }
+
+ parts := strings.SplitN(host, ".", 2)
+ if !IsWildcardDNS1123Label(parts[0]) {
+ return fmt.Errorf("host name %q invalid (label %q invalid)", host, parts[0])
+ } else if len(parts) > 1 {
+ err := validateDNS1123Labels(parts[1])
+ return err
+ }
+
+ return nil
+
+}
+
+//IsWildcardDNS1123Label validate wild card label
+func IsWildcardDNS1123Label(value string) bool {
+ return len(value) <= dns1123LabelMaxLength && wildcardPrefixRegexp.MatchString(value)
+}
+
+//validateDNS1123Labels validate host
+func validateDNS1123Labels(host string) error {
+ for _, label := range strings.Split(host, ".") {
+ if !IsDNS1123Label(label) {
+ return fmt.Errorf("host name %q invalid (label %q invalid)", host, label)
+ }
+ }
+ return nil
+}
+
+//IsDNS1123Label validate label
+func IsDNS1123Label(value string) bool {
+ return len(value) <= dns1123LabelMaxLength && dns1123LabelRegexp.MatchString(value)
+}
+
+// Options defines how to init Egress and its fetcher
+type Options struct {
+ Endpoints []string
+ EnableSSL bool
+ TLSConfig *tls.Config
+ Version string
+ ConfigPath string
+
+ //TODO: need timeout for client
+ // TimeOut time.Duration
+}
+
+/*
+// ToPilotOptions translate options to client options
+func (o Options) ToPilotOptions() *client.PilotOptions {
+ return &client.PilotOptions{Endpoints: o.Endpoints}
+}
+*/
+func getSpecifiedOptions() (opts Options, err error) {
+ hosts, scheme, err := iputil.URIs2Hosts(strings.Split(config.GetEgressEndpoints(), ","))
+ if err != nil {
+ return
+ }
+ opts.Endpoints = hosts
+ // TODO: envoy api v1 or v2
+ opts.TLSConfig, err = chassisTLS.GetTLSConfig(scheme, EgressTLS)
+ if err != nil {
+ return
+ }
+ if opts.TLSConfig != nil {
+ opts.EnableSSL = true
+ }
+ opts.ConfigPath = gochassisconfig.GetServiceDiscoveryConfigPath()
+ return
+}
+
+// GetEgressType returns the type of egress
+func GetEgressType(egress config.Egress) string {
+ if egress.Infra != "" {
+ return egress.Infra
+ }
+ return DefaultEgressType
+}
diff --git a/pkg/egress/egress_config_test.go b/pkg/egress/egress_config_test.go
new file mode 100644
index 0000000..9dddb52
--- /dev/null
+++ b/pkg/egress/egress_config_test.go
@@ -0,0 +1,41 @@
+package egress_test
+
+import (
+ "fmt"
+ mesherconfig "github.com/go-mesh/mesher/config"
+ "github.com/go-mesh/mesher/pkg/egress"
+ "gopkg.in/yaml.v2"
+ "testing"
+)
+
+func TestValidateEgressRule(t *testing.T) {
+ var yamlContent = `---
+egress:
+ infra: cse # pilot or cse
+ address: http://istio-pilot.istio-system:15010
+egressRule:
+ google-ext:
+ - hosts:
+ - "www.google.com"
+ - "*.yahoo.com"
+ ports:
+ - port: 80
+ protocol: HTTP
+ facebook-ext:
+ - hosts:
+ - "www.facebook.com"
+ ports:
+ - port: 80
+ protocol: HTTP`
+
+ ss := mesherconfig.EgressConfig{}
+ err := yaml.Unmarshal([]byte(yamlContent), &ss)
+ if err != nil {
+ fmt.Println("unmarshal failed")
+ }
+
+ bool, err := egress.ValidateEgressRule(ss.Destinations)
+ if bool == false {
+ t.Errorf("Expected true but got false")
+ }
+}
diff --git a/pkg/egress/egress_test.go b/pkg/egress/egress_test.go
new file mode 100644
index 0000000..ff54e5c
--- /dev/null
+++ b/pkg/egress/egress_test.go
@@ -0,0 +1,109 @@
+package egress_test
+
+import (
+ "fmt"
+ "os"
+ "testing"
+
+ "github.com/go-chassis/go-chassis/control"
+ "github.com/go-chassis/go-chassis/core/config"
+ "github.com/go-chassis/go-chassis/core/lager"
+ "github.com/go-mesh/mesher/cmd"
+ mesherconfig "github.com/go-mesh/mesher/config"
+ _ "github.com/go-mesh/mesher/control/istio"
+ "github.com/go-mesh/mesher/pkg/egress"
+ "github.com/go-mesh/mesher/pkg/egress/archaius"
+ "gopkg.in/yaml.v2"
+)
+
+func BenchmarkMatch(b *testing.B) {
+ lager.Initialize("", "DEBUG", "",
+ "size", true, 1, 10, 7)
+
+ gopath := os.Getenv("GOPATH")
+ os.Setenv("CHASSIS_HOME", gopath+"/src/github.com/go-mesh/mesher")
+ cmd.Init()
+ config.Init()
+ mesherconfig.Init()
+ egress.Init()
+ control.Init()
+ var yamlContent = `---
+egress:
+ infra: cse # pilot or cse
+ address: http://istio-pilot.istio-system:15010
+egressRule:
+ google-ext:
+ - hosts:
+ - "www.google.com"
+ - "*.yahoo.com"
+ ports:
+ - port: 80
+ protocol: HTTP
+ facebook-ext:
+ - hosts:
+ - "www.facebook.com"
+ ports:
+ - port: 80
+ protocol: HTTP`
+
+ ss := mesherconfig.EgressConfig{}
+ err := yaml.Unmarshal([]byte(yamlContent), &ss)
+ if err != nil {
+ fmt.Println("unmarshal failed")
+ }
+ archaius.SetEgressRule(ss.Destinations)
+
+ myString := "www.google.com"
+ for i := 0; i < b.N; i++ {
+ egress.Match(myString)
+ }
+}
+
+func TestMatch(t *testing.T) {
+ lager.Initialize("", "DEBUG", "",
+ "size", true, 1, 10, 7)
+
+ gopath := os.Getenv("GOPATH")
+ os.Setenv("CHASSIS_HOME", gopath+"/src/github.com/go-mesh/mesher")
+ cmd.Init()
+ config.Init()
+ mesherconfig.Init()
+ egress.Init()
+ //control.Init()
+ var yamlContent = `---
+egress:
+ infra: cse # pilot or cse
+ address: http://istio-pilot.istio-system:15010
+egressRule:
+ google-ext:
+ - hosts:
+ - "www.google.com"
+ - "*.yahoo.com"
+ ports:
+ - port: 80
+ protocol: HTTP
+ facebook-ext:
+ - hosts:
+ - "www.facebook.com"
+ ports:
+ - port: 80
+ protocol: HTTP`
+
+ ss := mesherconfig.EgressConfig{}
+ err := yaml.Unmarshal([]byte(yamlContent), &ss)
+ if err != nil {
+ fmt.Println("unmarshal failed")
+ }
+ archaius.SetEgressRule(ss.Destinations)
+
+ myString := "www.google.com"
+ b, _ := egress.Match(myString)
+ if b == false {
+ t.Errorf("Expected true but got false")
+ }
+ myString = "*.yahoo.com"
+ b, _ = egress.Match(myString)
+ if b == false {
+ t.Errorf("Expected true but got false")
+ }
+}
diff --git a/pkg/egress/pilot/egress.go b/pkg/egress/pilot/egress.go
new file mode 100644
index 0000000..07c6201
--- /dev/null
+++ b/pkg/egress/pilot/egress.go
@@ -0,0 +1,71 @@
+package pilot
+
+import (
+ "fmt"
+ "github.com/go-mesh/mesher/config"
+ "github.com/go-mesh/mesher/pkg/egress"
+ "sync"
+)
+
+func init() { egress.InstallEgressService("pilot", newPilotEgress) }
+
+func newPilotEgress() (egress.Egress, error) { return &PilotEgress{}, nil }
+
+//PilotEgress is pilot egress service
+type PilotEgress struct{}
+
+//FetchEgressRule return all rules
+func (r *PilotEgress) FetchEgressRule() map[string][]*config.EgressRule {
+ return GetEgressRule()
+}
+
+//SetEgressRule set rules
+func (r *PilotEgress) SetEgressRule(rr map[string][]*config.EgressRule) {
+ SetEgressRule(rr)
+}
+
+//Init init egress config
+func (r *PilotEgress) Init(o egress.Options) error {
+ // the manager use dests to init, so must init after dests
+ if err := InitPilotFetcher(o); err != nil {
+ return err
+ }
+ return refresh()
+}
+
+// refresh all the egress config
+func refresh() error {
+ configs := pilotfetcher.GetConfigurations()
+
+ d := make(map[string][]*config.EgressRule)
+ for k, v := range configs {
+ rules, ok := v.([]*config.EgressRule)
+ if !ok {
+ err := fmt.Errorf("Egress rule type assertion fail, key: %s", k)
+ return err
+ }
+ d[k] = rules
+ }
+ ok, _ := egress.ValidateEgressRule(d)
+ if ok {
+ dests = d
+ }
+ return nil
+}
+
+var dests = make(map[string][]*config.EgressRule)
+var lock sync.RWMutex
+
+// GetEgressRule get egress rule
+func GetEgressRule() map[string][]*config.EgressRule {
+ lock.RLock()
+ defer lock.RUnlock()
+ return dests
+}
+
+// SetEgressRule set egress rule
+func SetEgressRule(rule map[string][]*config.EgressRule) {
+ lock.RLock()
+ defer lock.RUnlock()
+ dests = rule
+}
diff --git a/pkg/egress/pilot/pilotsource.go b/pkg/egress/pilot/pilotsource.go
new file mode 100644
index 0000000..5216b19
--- /dev/null
+++ b/pkg/egress/pilot/pilotsource.go
@@ -0,0 +1,299 @@
+package pilot
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/go-chassis/go-archaius/core"
+ cm "github.com/go-chassis/go-archaius/core/config-manager"
+ "github.com/go-chassis/go-archaius/core/event-system"
+ "github.com/go-chassis/go-chassis/core/lager"
+ "github.com/go-mesh/mesher/config"
+ "github.com/go-mesh/mesher/control/istio"
+ "github.com/go-mesh/mesher/pkg/egress"
+ istioinfra "github.com/go-mesh/mesher/pkg/infras/istio"
+)
+
+const egressPilotSourceName = "EgressPilotSource"
+const egressPilotSourcePriority = 8
+
+//OUTBOUND outbound
+const OUTBOUND = "outbound"
+
+//clusteroriginaldst cluster type
+const clusteroriginaldst = 4
+
+// DefaultPilotRefresh is default pilot refresh time
+// TODO: use stream instead
+var DefaultPilotRefresh = 10 * time.Second
+
+var pilotfetcher core.ConfigMgr
+var pilotChan = make(chan string, 10)
+
+func setChanForPilot(k string) bool {
+ select {
+ case pilotChan <- k:
+ return true
+ default:
+ return false
+ }
+}
+
+// InitPilotFetcher init the config mgr and add several sources
+func InitPilotFetcher(o egress.Options) error {
+ d := eventsystem.NewDispatcher()
+
+ // register and init pilot fetcher
+ d.RegisterListener(&pilotEventListener{}, ".*")
+ pilotfetcher = cm.NewConfigurationManager(d)
+
+ return addEgressPilotSource(o)
+}
+
+// addEgressPilotSource adds a config source to pilotfetcher
+func addEgressPilotSource(o egress.Options) error {
+ if pilotfetcher == nil {
+ return errors.New("pilotfetcher is nil, please init it first")
+ }
+
+ s, err := newPilotSource(o)
+ if err != nil {
+ return err
+ }
+ lager.Logger.Infof("New [%s] source success", s.GetSourceName())
+ return pilotfetcher.AddSource(s, s.GetPriority())
+}
+
+// pilotSource keeps the egress rule in istio
+type pilotSource struct {
+ refreshInverval time.Duration
+ fetcher istioinfra.XdsClient
+
+ mu sync.RWMutex
+ pmu sync.RWMutex
+ Configurations map[string]interface{}
+ PortToService map[string]string
+}
+
+func newPilotSource(o egress.Options) (*pilotSource, error) {
+ pilotAddr := o.Endpoints[0]
+ nodeInfo := getNodeInfo()
+ xdsClient, err := istioinfra.NewXdsClient(pilotAddr, nil, nodeInfo, o.ConfigPath)
+ if err != nil {
+ return nil, fmt.Errorf("connect to pilot failed: %v", err)
+ }
+
+ return &pilotSource{
+ // TODO: read from config
+ refreshInverval: DefaultPilotRefresh,
+ Configurations: map[string]interface{}{},
+ PortToService: map[string]string{},
+ fetcher: *xdsClient,
+ }, nil
+}
+
+func getNodeInfo() *istioinfra.NodeInfo {
+ PodName := os.Getenv("POD_NAME")
+ PodNamespace := os.Getenv("POD_NAMESPACE")
+ InstanceIP := os.Getenv("INSTANCE_IP")
+
+ nodeInfo := &istioinfra.NodeInfo{
+ PodName: PodName,
+ Namespace: PodNamespace,
+ InstanceIP: InstanceIP,
+ }
+ return nodeInfo
+}
+
+func (r *pilotSource) GetSourceName() string { return egressPilotSourceName }
+func (r *pilotSource) GetPriority() int { return egressPilotSourcePriority }
+func (r *pilotSource) Cleanup() error { return nil }
+
+func (r *pilotSource) AddDimensionInfo(d string) (map[string]string, error) { return nil, nil }
+func (r *pilotSource) GetConfigurationsByDI(d string) (map[string]interface{}, error) { return nil, nil }
+func (r *pilotSource) GetConfigurationByKeyAndDimensionInfo(key, d string) (interface{}, error) {
+ return nil, nil
+}
+
+func (r *pilotSource) GetConfigurations() (map[string]interface{}, error) {
+ egressConfigs, err := r.getEgressConfigFromPilot()
+ if err != nil {
+ lager.Logger.Error("Get egress config from pilot failed" + err.Error())
+ return nil, err
+ }
+ d := make(map[string]interface{}, 0)
+ d["pilotEgress"] = egressConfigs
+ r.mu.Lock()
+ r.Configurations = d
+ r.mu.Unlock()
+ return d, nil
+}
+
+func (r *pilotSource) GetConfigurationByKey(k string) (interface{}, error) {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+ if value, ok := r.Configurations[k]; ok {
+ return value, nil
+ }
+ return nil, fmt.Errorf("not found %s", k)
+}
+
+// get egress config from pilot
+func (r *pilotSource) getEgressConfigFromPilot() ([]*config.EgressRule, error) {
+ clusters, _ := r.fetcher.CDS()
+ var egressRules []*config.EgressRule
+ for _, cluster := range clusters {
+
+ if cluster.Type == clusteroriginaldst {
+ data := strings.Split(cluster.Name, "|")
+ if len(data) > 1 && data[0] == OUTBOUND {
+ intport, err := strconv.Atoi(data[1])
+ if err != nil {
+ return nil, nil
+ }
+ var rule config.EgressRule
+ rule.Hosts = []string{data[len(data)-1]}
+ rule.Ports = []*config.EgressPort{
+ {Port: int32(intport),
+ Protocol: "http"}}
+
+ egressRules = append(egressRules, &rule)
+ }
+
+ }
+
+ }
+
+ return egressRules, nil
+}
+
+func (r *pilotSource) setPortForDestination(service, port string) {
+ r.pmu.RLock()
+ r.PortToService[port] = service
+ r.pmu.RUnlock()
+}
+
+func (r *pilotSource) DynamicConfigHandler(callback core.DynamicConfigCallback) error {
+ // Periodically refresh configurations
+ ticker := time.NewTicker(r.refreshInverval)
+ for {
+ select {
+ case <-pilotChan:
+ data, err := r.GetConfigurations()
+ if err != nil {
+ lager.Logger.Error("pilot pull configuration error" + err.Error())
+ continue
+ }
+ for k, d := range data {
+ istio.SaveToEgressCache(map[string][]*config.EgressRule{k: d.([]*config.EgressRule)})
+ }
+
+ case <-ticker.C:
+ data, err := r.refreshConfigurations()
+ if err != nil {
+ lager.Logger.Error("pilot refresh configuration error" + err.Error())
+ continue
+ }
+ events, err := r.populateEvents(data)
+ if err != nil {
+ lager.Logger.Warnf("populate event error", err)
+ return err
+ }
+ //Generate OnEvent Callback based on the events created
+ lager.Logger.Debugf("event On receive %+v", events)
+ for _, event := range events {
+ callback.OnEvent(event)
+ }
+ }
+ }
+ return nil
+}
+
+func (r *pilotSource) refreshConfigurations() (map[string]interface{}, error) {
+ data := make(map[string]interface{}, 0)
+
+ egressConfigs, err := r.getEgressConfigFromPilot()
+ if err != nil {
+ lager.Logger.Error("Get egress config from pilot failed" + err.Error())
+ return nil, err
+ }
+
+ data["pilotEgress"] = egressConfigs
+ return data, nil
+}
+
+func (r *pilotSource) populateEvents(updates map[string]interface{}) ([]*core.Event, error) {
+ events := make([]*core.Event, 0)
+ new := make(map[string]interface{})
+
+ // generate create and update event
+ r.mu.RLock()
+ current := r.Configurations
+ r.mu.RUnlock()
+
+ for key, value := range updates {
+ new[key] = value
+ currentValue, ok := current[key]
+ if !ok { // if new configuration introduced
+ events = append(events, constructEvent(core.Create, key, value))
+ } else if !reflect.DeepEqual(currentValue, value) {
+ events = append(events, constructEvent(core.Update, key, value))
+ }
+ }
+ // generate delete event
+ for key, value := range current {
+ _, ok := new[key]
+ if !ok { // when old config not present in new config
+ events = append(events, constructEvent(core.Delete, key, value))
+ }
+ }
+
+ // update with latest config
+ r.mu.Lock()
+ r.Configurations = new
+ r.mu.Unlock()
+ return events, nil
+}
+
+func constructEvent(eventType string, key string, value interface{}) *core.Event {
+ return &core.Event{
+ EventType: eventType,
+ EventSource: egressPilotSourceName,
+ Value: value,
+ Key: key,
+ }
+}
+
+// pilotEventListener handle event dispatcher
+type pilotEventListener struct{}
+
+// update egress rule of a service
+func (r *pilotEventListener) Event(e *core.Event) {
+ if e == nil {
+ lager.Logger.Warn("pilot event pointer is nil", nil)
+ return
+ }
+
+ v := pilotfetcher.GetConfigurationsByKey(e.Key)
+ if v == nil {
+ istio.SaveToEgressCache(nil)
+ return
+ }
+ egressRules, ok := v.([]*config.EgressRule)
+ if !ok {
+ lager.Logger.Error("value of pilot is not type []*EgressRule", nil)
+ return
+ }
+
+ ok, _ = egress.ValidateEgressRule(map[string][]*config.EgressRule{e.Key: egressRules})
+ if ok {
+ istio.SaveToEgressCache(map[string][]*config.EgressRule{e.Key: egressRules})
+ lager.Logger.Infof("Update [%s] egress rule of pilot success", e.Key)
+ }
+}
diff --git a/protocol/http/reverse_proxy.go b/protocol/http/reverse_proxy.go
index ddc2814..2ec0a5e 100755
--- a/protocol/http/reverse_proxy.go
+++ b/protocol/http/reverse_proxy.go
@@ -39,10 +39,13 @@
"github.com/go-chassis/go-chassis/third_party/forked/afex/hystrix-go/hystrix"
"github.com/go-mesh/mesher/cmd"
"github.com/go-mesh/mesher/common"
+ "github.com/go-mesh/mesher/pkg/egress"
"github.com/go-mesh/mesher/pkg/metrics"
"github.com/go-mesh/mesher/protocol"
"github.com/go-mesh/mesher/resolver"
"github.com/go-mesh/openlogging"
+ "strconv"
+ "strings"
)
var dr = resolver.GetDestinationResolver("http")
@@ -113,11 +116,33 @@
//transfer header into ctx
inv.Ctx = context.WithValue(inv.Ctx, chassisCommon.ContextHeaderKey{}, h)
- c, err := handler.GetChain(chassisCommon.Consumer, common.ChainConsumerOutgoing)
- if err != nil {
- handleErrorResponse(inv, w, http.StatusBadGateway, err)
- lager.Logger.Error("Get chain failed: " + err.Error())
- return
+
+ var c *handler.Chain
+ ok, egressRule := egress.Match(inv.MicroServiceName)
+ if ok {
+ var intport int32 = 80
+ for _, port := range egressRule.Ports {
+ if strings.EqualFold(port.Protocol, common.HTTPProtocol) {
+ intport = port.Port
+ break
+ }
+ }
+ inv.Endpoint = inv.MicroServiceName + ":" + strconv.Itoa(int(intport))
+ c, err = handler.GetChain(common.ConsumerEgress, common.ChainConsumerEgress)
+
+ if err != nil {
+ handleErrorResponse(inv, w, http.StatusBadGateway, err)
+ lager.Logger.Error("Get chain failed" + err.Error())
+ return
+ }
+
+ } else {
+ c, err = handler.GetChain(chassisCommon.Consumer, common.ChainConsumerOutgoing)
+ if err != nil {
+ handleErrorResponse(inv, w, http.StatusBadGateway, err)
+ lager.Logger.Error("Get chain failed: " + err.Error())
+ return
+ }
}
defer func(begin time.Time) {
timeTaken := time.Since(begin).Seconds()
@@ -229,6 +254,7 @@
handleErrorResponse(inv, w, ir.Status, ir.Err)
default: //for other error, check response and response body, if there is body, just transparent response
resp, ok := inv.Reply.(*http.Response)
+
if ok { // return raw transport error
if resp != nil {
if resp.Body == nil {
diff --git a/server/server.go b/server/server.go
index dfe63ff..1bee253 100644
--- a/server/server.go
+++ b/server/server.go
@@ -28,6 +28,11 @@
lager.Logger.Error("Go chassis init failed, Mesher is not available: " + err.Error())
panic(err)
}
+ if err := bootstrap.InitEgressChain(); err != nil {
+ lager.Logger.Errorf("Egress chain int failed: %s", err)
+ panic(err)
+ }
+
if err := bootstrap.Start(); err != nil {
lager.Logger.Error("Bootstrap failed: " + err.Error())
panic(err)