From 605aa9c9cbea1211a42b8f101d4b3259f39c0ca0 Mon Sep 17 00:00:00 2001 From: Alan Viast Date: Tue, 22 Oct 2024 16:12:15 +0800 Subject: [PATCH] support method metrics (#3) * feat: add endpoint metrics * feat: add upgrade.sh * feat: add rpc method prometheus label * fix: parse batch request json --- pkg/filters/proxies/providerproxy/metrics.go | 59 ++++++++++ .../proxies/providerproxy/providerproxy.go | 108 +++++++++++++++--- .../providerproxy/providerproxy_test.go | 65 ++++++++++- .../block_lag_selector.go} | 81 +++++++++---- .../selector/provider_selector.go | 55 +++++++++ .../providerproxy/selector/round_robin.go | 31 +++++ .../providerproxy/{ => selector}/rpc.go | 19 ++- scripts/upgrade.sh | 85 ++++++++++++++ 8 files changed, 453 insertions(+), 50 deletions(-) create mode 100644 pkg/filters/proxies/providerproxy/metrics.go rename pkg/filters/proxies/providerproxy/{provider_selector.go => selector/block_lag_selector.go} (56%) create mode 100644 pkg/filters/proxies/providerproxy/selector/provider_selector.go create mode 100644 pkg/filters/proxies/providerproxy/selector/round_robin.go rename pkg/filters/proxies/providerproxy/{ => selector}/rpc.go (72%) create mode 100755 scripts/upgrade.sh diff --git a/pkg/filters/proxies/providerproxy/metrics.go b/pkg/filters/proxies/providerproxy/metrics.go new file mode 100644 index 0000000000..257e04dfc5 --- /dev/null +++ b/pkg/filters/proxies/providerproxy/metrics.go @@ -0,0 +1,59 @@ +package providerproxy + +import ( + "strconv" + "time" + + "github.com/megaease/easegress/v2/pkg/util/prometheushelper" + "github.com/prometheus/client_golang/prometheus" +) + +type ( + metrics struct { + TotalRequests *prometheus.CounterVec + RequestsDuration prometheus.ObserverVec + } + + RequestMetrics struct { + Provider string + RpcMethod []string + StatusCode int + Duration time.Duration + } +) + +func (m *ProviderProxy) newMetrics() *metrics { + commonLabels := prometheus.Labels{ + "pipelineName": m.Name(), + "kind": Kind, + } + prometheusLabels := []string{ + "pipelineName", "kind", "policy", "statusCode", "provider", "rpcMethod", + } + + return &metrics{ + TotalRequests: prometheushelper.NewCounter( + "providerproxy_total_requests", + "the total count of http requests", prometheusLabels).MustCurryWith(commonLabels), + RequestsDuration: prometheushelper.NewHistogram( + prometheus.HistogramOpts{ + Name: "providerproxy_requests_duration", + Help: "request processing duration histogram of a backend", + Buckets: prometheushelper.DefaultDurationBuckets(), + }, prometheusLabels).MustCurryWith(commonLabels), + } +} + +func (m *ProviderProxy) collectMetrics(requestMetrics RequestMetrics) { + for _, method := range requestMetrics.RpcMethod { + labels := prometheus.Labels{ + "policy": m.spec.Policy, + "statusCode": strconv.Itoa(requestMetrics.StatusCode), + "provider": requestMetrics.Provider, + "rpcMethod": method, + } + + m.metrics.TotalRequests.With(labels).Inc() + m.metrics.RequestsDuration.With(labels).Observe(float64(requestMetrics.Duration.Milliseconds() / int64(len(requestMetrics.RpcMethod)))) + } +} diff --git a/pkg/filters/proxies/providerproxy/providerproxy.go b/pkg/filters/proxies/providerproxy/providerproxy.go index 315b275a39..bd2000af3a 100644 --- a/pkg/filters/proxies/providerproxy/providerproxy.go +++ b/pkg/filters/proxies/providerproxy/providerproxy.go @@ -1,15 +1,35 @@ +/* + * Copyright (c) 2017, The Easegress Authors + * All rights reserved. + * + * Licensed 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 providerproxy import ( + "encoding/json" "errors" - "math/rand" "net/http" "net/url" "github.com/megaease/easegress/v2/pkg/context" "github.com/megaease/easegress/v2/pkg/filters" + "github.com/megaease/easegress/v2/pkg/filters/proxies/providerproxy/selector" "github.com/megaease/easegress/v2/pkg/logger" "github.com/megaease/easegress/v2/pkg/protocols/httpprot" + "github.com/megaease/easegress/v2/pkg/supervisor" + "github.com/megaease/easegress/v2/pkg/util/fasttime" ) const ( @@ -19,9 +39,11 @@ const ( type ( ProviderProxy struct { + super *supervisor.Supervisor spec *Spec client *http.Client - providerSelector *ProviderSelector + providerSelector selector.ProviderSelector + metrics *metrics } Spec struct { @@ -30,16 +52,11 @@ type ( Urls []string `yaml:"urls"` Interval string `yaml:"interval,omitempty" jsonschema:"format=duration"` Lag uint64 `yaml:"lag,omitempty" jsonschema:"default=100"` + Policy string `yaml:"policy,omitempty" jsonschema:"default=roundRobin"` } ) func (m *ProviderProxy) SelectNode() (*url.URL, error) { - if m.providerSelector == nil { - urls := m.spec.Urls - randomIndex := rand.Intn(len(urls)) - rpcUrl := urls[randomIndex] - return url.Parse(rpcUrl) - } rpcUrl, err := m.providerSelector.ChooseServer() if err != nil { return nil, err @@ -47,7 +64,46 @@ func (m *ProviderProxy) SelectNode() (*url.URL, error) { return url.Parse(rpcUrl) } +func (m *ProviderProxy) ParsePayloadMethod(payload []byte) []string { + defaultValue := []string{"UNKNOWN"} + if len(payload) <= 0 { + return defaultValue + } + + jsonBody := map[string]interface{}{} + err := json.Unmarshal(payload, &jsonBody) + if err == nil { + method, exists := jsonBody["method"].(string) + if !exists { + return defaultValue + } + return []string{method} + } + + // parse batch call json array + var jsonBodyArr []map[string]interface{} + err = json.Unmarshal(payload, &jsonBodyArr) + if err != nil { + logger.Errorf("parse batch call err: %s, Body: %s", err, string(payload)) + return defaultValue + } + + methods := make([]string, 0) + + for _, item := range jsonBodyArr { + method, exists := item["method"].(string) + if !exists { + methods = append(methods, "UNKNOWN") + } + methods = append(methods, method) + } + return methods +} + func (m *ProviderProxy) Handle(ctx *context.Context) (result string) { + requestMetrics := RequestMetrics{} + + startTime := fasttime.Now() reqUrl, err := m.SelectNode() if err != nil { logger.Errorf(err.Error()) @@ -55,8 +111,17 @@ func (m *ProviderProxy) Handle(ctx *context.Context) (result string) { } logger.Infof("select rpc provider: %s", reqUrl.String()) + requestMetrics.Provider = reqUrl.String() req := ctx.GetInputRequest().(*httpprot.Request) + + requestMetrics.RpcMethod = m.ParsePayloadMethod(req.RawPayload()) + forwardReq, err := http.NewRequestWithContext(req.Context(), req.Method(), reqUrl.String(), req.GetPayload()) + if err != nil { + logger.Errorf(err.Error()) + return err.Error() + } + for key := range req.HTTPHeader() { forwardReq.Header.Add(key, req.HTTPHeader().Get(key)) } @@ -67,17 +132,20 @@ func (m *ProviderProxy) Handle(ctx *context.Context) (result string) { return err.Error() } + requestMetrics.Duration = fasttime.Since(startTime) + requestMetrics.StatusCode = response.StatusCode + defer m.collectMetrics(requestMetrics) + outputResponse, err := httpprot.NewResponse(response) outputResponse.Body = response.Body if err != nil { return err.Error() } - if err = outputResponse.FetchPayload(1024 * 1024); err != nil { + if err = outputResponse.FetchPayload(-1); err != nil { logger.Errorf("%s: failed to fetch response payload: %v, please consider to set serverMaxBodySize of SimpleHTTPProxy to -1.", m.Name(), err) return err.Error() } - ctx.SetResponse(context.DefaultNamespace, outputResponse) return "" } @@ -90,12 +158,14 @@ var kind = &filters.Kind{ return &Spec{ Urls: make([]string, 0), Interval: "1s", + Policy: "roundRobin", } }, CreateInstance: func(spec filters.Spec) filters.Filter { providerSpec := spec.(*Spec) return &ProviderProxy{ spec: providerSpec, + super: spec.Super(), client: http.DefaultClient, } }, @@ -130,16 +200,16 @@ func (m *ProviderProxy) reload() { client := http.DefaultClient m.client = client - if len(m.spec.Urls) > 1 { - providerSelectorSpec := ProviderSelectorSpec{ - Urls: m.spec.Urls, - Interval: m.spec.Interval, - Lag: m.spec.Lag, - } - - providerSelector := NewProviderSelector(providerSelectorSpec) - m.providerSelector = &providerSelector + providerSelectorSpec := selector.ProviderSelectorSpec{ + Urls: m.spec.Urls, + Interval: m.spec.Interval, + Lag: m.spec.Lag, } + + m.metrics = m.newMetrics() + + providerSelector := selector.CreateProviderSelectorByPolicy(m.spec.Policy, providerSelectorSpec, m.super) + m.providerSelector = providerSelector } // Status returns status. diff --git a/pkg/filters/proxies/providerproxy/providerproxy_test.go b/pkg/filters/proxies/providerproxy/providerproxy_test.go index 637f496b92..8d165c5dd2 100644 --- a/pkg/filters/proxies/providerproxy/providerproxy_test.go +++ b/pkg/filters/proxies/providerproxy/providerproxy_test.go @@ -18,15 +18,18 @@ package providerproxy import ( - "bytes" + "fmt" "net/http" "os" + "strings" "testing" "github.com/megaease/easegress/v2/pkg/context" "github.com/megaease/easegress/v2/pkg/filters" "github.com/megaease/easegress/v2/pkg/logger" + "github.com/megaease/easegress/v2/pkg/option" "github.com/megaease/easegress/v2/pkg/protocols/httpprot" + "github.com/megaease/easegress/v2/pkg/supervisor" "github.com/megaease/easegress/v2/pkg/tracing" "github.com/megaease/easegress/v2/pkg/util/codectool" "github.com/stretchr/testify/assert" @@ -39,11 +42,25 @@ func TestMain(m *testing.M) { } func newTestProviderProxy(yamlConfig string, assert *assert.Assertions) *ProviderProxy { + defer func() { + if err := recover(); err != nil { + fmt.Printf("Recovered from panic: %v\n", err) + } + }() + rawSpec := make(map[string]interface{}) err := codectool.Unmarshal([]byte(yamlConfig), &rawSpec) assert.NoError(err) - spec, err := filters.NewSpec(nil, "", rawSpec) + opt := option.New() + opt.Name = "test" + opt.ClusterName = "test" + opt.ClusterRole = "secondary" + + super := supervisor.NewMock(opt, nil, nil, + nil, false, nil, nil) + + spec, err := filters.NewSpec(super, "", rawSpec) assert.NoError(err) proxy := kind.CreateInstance(spec).(*ProviderProxy) @@ -61,7 +78,10 @@ func getCtx(stdr *http.Request) *context.Context { req.HTTPHeader().Set(key, stdr.Header.Get(key)) } - _ = req.FetchPayload(1024 * 1024) + err := req.FetchPayload(1024 * 1024) + if err != nil { + logger.Errorf(err.Error()) + } ctx := context.New(tracing.NoopSpan) ctx.SetRequest(context.DefaultNamespace, req) return ctx @@ -74,17 +94,50 @@ func TestProviderProxy(t *testing.T) { name: providerProxy kind: ProviderProxy urls: - - https://ethereum-mainnet.s.chainbase.online + - https://eth.llamarpc.com ` proxy := newTestProviderProxy(yamlConfig, assert) postData := "{\"method\":\"eth_blockNumber\",\"params\":[],\"id\":1,\"jsonrpc\":\"2.0\"}" - stdr, _ := http.NewRequest(http.MethodPost, "https://www.megaease.com", bytes.NewReader([]byte(postData))) + stdr, _ := http.NewRequest(http.MethodPost, "https://www.megaease.com", strings.NewReader(postData)) stdr.Header.Set("Content-Type", "application/json") ctx := getCtx(stdr) response := proxy.Handle(ctx) assert.Equal("", response) - assert.NotNil(string(ctx.GetOutputResponse().RawPayload())) + assert.NotNil(ctx.GetResponse(context.DefaultNamespace).GetPayload()) + + proxy.Close() +} + +func TestProviderProxy_ParsePayloadMethod(t *testing.T) { + assert := assert.New(t) + + const yamlConfig = ` +name: providerProxy +kind: ProviderProxy +urls: + - https://eth.llamarpc.com +` + proxy := newTestProviderProxy(yamlConfig, assert) + + method := proxy.ParsePayloadMethod([]byte("{\"method\":\"eth_blockNumber\",\"params\":[],\"id\":1,\"jsonrpc\":\"2.0\"}")) + assert.Equal([]string{"eth_blockNumber"}, method) + + method = proxy.ParsePayloadMethod([]byte("{\"method\":\"eth_getBlockByNumber\",\"params\":[\"0xc5043f\",false],\"id\":1,\"jsonrpc\":\"2.0\"}")) + assert.Equal([]string{"eth_getBlockByNumber"}, method) + + method = proxy.ParsePayloadMethod([]byte("test unknown payload")) + assert.Equal([]string{"UNKNOWN"}, method) + + method = proxy.ParsePayloadMethod([]byte{}) + assert.Equal([]string{"UNKNOWN"}, method) + + method = proxy.ParsePayloadMethod([]byte("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"suix_getAllBalances\",\"params\":[\"0x94f1a597b4e8f709a396f7f6b1482bdcd65a673d111e49286c527fab7c2d0961\"]}")) + assert.Equal([]string{"suix_getAllBalances"}, method) + + method = proxy.ParsePayloadMethod([]byte("[{\"jsonrpc\": \"2.0\", \"method\": \"eth_getTransactionReceipt\", \"params\": [\"0x7363bf80269875c6ddd3de0089baf0a9af28586dd0e536753d1cbb5eb9d6535b\"], \"id\": 0}, {\"jsonrpc\": \"2.0\", \"method\": \"eth_getTransactionReceipt\", \"params\": [\"0x29696eba0fa0eb5eb4c1495174c6fb37a9e64a3707dc8b64c9d19a650c8e1b5f\"], \"id\": 1}, {\"jsonrpc\": \"2.0\", \"method\": \"eth_getTransactionReceipt\", \"params\": [\"0x8f9631d0fd6a056e422ed0eea938a84b1eca3344729789f4465861c6406c2114\"], \"id\": 2}, {\"jsonrpc\": \"2.0\", \"method\": \"eth_getTransactionReceipt\", \"params\": [\"0x4787c3d707ff6d204590c77f672917dca49c36ec0381d6dea10d84981d008ec5\"], \"id\": 3}, {\"jsonrpc\": \"2.0\", \"method\": \"eth_getTransactionReceipt\", \"params\": [\"0x11132ec86262aa67b27f64a5f5a6550c7b86f85dabe3d824dbab0f20283f9aa6\"], \"id\": 4}, {\"jsonrpc\": \"2.0\", \"method\": \"eth_getTransactionReceipt\", \"params\": [\"0x8efac98473a7cdb5c40ca503089794b06723c09791833c2df5b4732ea5a10451\"], \"id\": 5}, {\"jsonrpc\": \"2.0\", \"method\": \"eth_getTransactionReceipt\", \"params\": [\"0x7df0d4c1b2d1a570a1367a8bd09301f8e6af5ffa03a2b97a693ba440bb87b002\"], \"id\": 6}, {\"jsonrpc\": \"2.0\", \"method\": \"eth_getTransactionReceipt\", \"params\": [\"0x7d473c856b0b8d207c43e46a3327217e809142ad5497109d01ec72d6a1bde45c\"], \"id\": 7}, {\"jsonrpc\": \"2.0\", \"method\": \"eth_getTransactionReceipt\", \"params\": [\"0x512013ff4a44b6604b81f63e330d57a63b03f23dae011753be8c168ce8f6fcc7\"], \"id\": 8}, {\"jsonrpc\": \"2.0\", \"method\": \"eth_getTransactionReceipt\", \"params\": [\"0x438abc4bfc9a46296f191ad3ecc742bbd7da9286a2a92fe8f27f2d0168a19661\"], \"id\": 9}]")) + assert.Equal([]string{"eth_getTransactionReceipt", "eth_getTransactionReceipt", "eth_getTransactionReceipt", "eth_getTransactionReceipt", "eth_getTransactionReceipt", "eth_getTransactionReceipt", "eth_getTransactionReceipt", "eth_getTransactionReceipt", "eth_getTransactionReceipt", "eth_getTransactionReceipt"}, method) + proxy.Close() } diff --git a/pkg/filters/proxies/providerproxy/provider_selector.go b/pkg/filters/proxies/providerproxy/selector/block_lag_selector.go similarity index 56% rename from pkg/filters/proxies/providerproxy/provider_selector.go rename to pkg/filters/proxies/providerproxy/selector/block_lag_selector.go index b7373143a4..fa7b2f12f4 100644 --- a/pkg/filters/proxies/providerproxy/provider_selector.go +++ b/pkg/filters/proxies/providerproxy/selector/block_lag_selector.go @@ -1,45 +1,50 @@ -package providerproxy +/* + * Copyright (c) 2017, The Easegress Authors + * All rights reserved. + * + * Licensed 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 selector import ( "encoding/json" "fmt" - "log" "net/http" "time" "github.com/ethereum/go-ethereum/core/types" "github.com/megaease/easegress/v2/pkg/logger" + "github.com/megaease/easegress/v2/pkg/supervisor" + "github.com/megaease/easegress/v2/pkg/util/prometheushelper" + "github.com/prometheus/client_golang/prometheus" "golang.org/x/sync/errgroup" ) -type ProviderSelectorSpec struct { - Urls []string `json:"urls"` - Interval string `json:"interval,omitempty" jsonschema:"format=duration"` - Lag uint64 `json:"lag,omitempty" jsonschema:"default=100"` -} - -// GetInterval returns the interval duration. -func (ps *ProviderSelectorSpec) GetInterval() time.Duration { - interval, _ := time.ParseDuration(ps.Interval) - if interval <= 0 { - interval = time.Second - } - return interval -} - type ProviderWeight struct { Url string BlockNumber uint64 Client *RPCClient } -type ProviderSelector struct { +type BlockLagProviderSelector struct { done chan struct{} providers []ProviderWeight lag uint64 + metrics *metrics } -func NewProviderSelector(spec ProviderSelectorSpec) ProviderSelector { +func NewBlockLagProviderSelector(spec ProviderSelectorSpec, super *supervisor.Supervisor) ProviderSelector { providers := make([]ProviderWeight, 0) @@ -58,10 +63,11 @@ func NewProviderSelector(spec ProviderSelectorSpec) ProviderSelector { }) } - ps := ProviderSelector{ + ps := BlockLagProviderSelector{ done: make(chan struct{}), providers: providers, lag: spec.Lag, + metrics: newMetrics(super), } ticker := time.NewTicker(intervalDuration) ps.checkServers() @@ -84,8 +90,7 @@ type ProviderBlock struct { block uint64 } -func (ps ProviderSelector) checkServers() { - log.Println("check block number") +func (ps BlockLagProviderSelector) checkServers() { eg := new(errgroup.Group) blockNumberChannel := make(chan ProviderBlock, len(ps.providers)) startTime := time.Now().Local() @@ -128,20 +133,28 @@ func (ps ProviderSelector) checkServers() { for i := 0; i < len(ps.providers); i++ { blockIndex := <-blockNumberChannel ps.providers[blockIndex.index].BlockNumber = blockIndex.block + labels := prometheus.Labels{ + "provider": ps.providers[blockIndex.index].Url, + } + ps.metrics.ProviderBlockHeight.With(labels).Set(float64(blockIndex.block)) } logger.Debugf("update block number time: %s", time.Since(startTime)) } -func (ps ProviderSelector) Close() { +func (ps BlockLagProviderSelector) Close() { close(ps.done) } -func (ps ProviderSelector) ChooseServer() (string, error) { +func (ps BlockLagProviderSelector) ChooseServer() (string, error) { if len(ps.providers) == 0 { return "", fmt.Errorf("no provider available") } + if len(ps.providers) == 1 { + return ps.providers[0].Url, nil + } + var bestProvider ProviderWeight for _, provider := range ps.providers { if provider.BlockNumber == 0 { @@ -159,3 +172,23 @@ func (ps ProviderSelector) ChooseServer() (string, error) { return ps.providers[0].Url, nil } + +type metrics struct { + ProviderBlockHeight *prometheus.GaugeVec +} + +func newMetrics(super *supervisor.Supervisor) *metrics { + commonLabels := prometheus.Labels{ + "pipelineName": super.Options().Name, + "kind": "BlockLagProviderSelector", + } + prometheusLabels := []string{ + "pipelineName", "kind", "provider", + } + + return &metrics{ + ProviderBlockHeight: prometheushelper.NewGauge( + "provider_block_height", + "the block height of provider", prometheusLabels).MustCurryWith(commonLabels), + } +} diff --git a/pkg/filters/proxies/providerproxy/selector/provider_selector.go b/pkg/filters/proxies/providerproxy/selector/provider_selector.go new file mode 100644 index 0000000000..12df26e151 --- /dev/null +++ b/pkg/filters/proxies/providerproxy/selector/provider_selector.go @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017, The Easegress Authors + * All rights reserved. + * + * Licensed 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 selector + +import ( + "time" + + "github.com/megaease/easegress/v2/pkg/supervisor" +) + +type ProviderSelectorSpec struct { + Urls []string `json:"urls"` + Interval string `json:"interval,omitempty" jsonschema:"format=duration"` + Lag uint64 `json:"lag,omitempty" jsonschema:"default=100"` +} + +// GetInterval returns the interval duration. +func (ps *ProviderSelectorSpec) GetInterval() time.Duration { + interval, _ := time.ParseDuration(ps.Interval) + if interval <= 0 { + interval = time.Second + } + return interval +} + +type ProviderSelector interface { + ChooseServer() (string, error) + Close() +} + +func CreateProviderSelectorByPolicy(policy string, spec ProviderSelectorSpec, super *supervisor.Supervisor) ProviderSelector { + switch policy { + case "blockLag": + return NewBlockLagProviderSelector(spec, super) + case "roundRobin": + return NewRoundRobinProviderSelector(spec) + default: + return NewRoundRobinProviderSelector(spec) + } +} diff --git a/pkg/filters/proxies/providerproxy/selector/round_robin.go b/pkg/filters/proxies/providerproxy/selector/round_robin.go new file mode 100644 index 0000000000..ffe2eff8c9 --- /dev/null +++ b/pkg/filters/proxies/providerproxy/selector/round_robin.go @@ -0,0 +1,31 @@ +package selector + +import ( + "fmt" + "math/rand" +) + +type RoundRobinProviderSelector struct { + providers []string +} + +func (ps *RoundRobinProviderSelector) ChooseServer() (string, error) { + if len(ps.providers) == 0 { + return "", fmt.Errorf("no provider available") + } + + urls := ps.providers + randomIndex := rand.Intn(len(urls)) + rpcUrl := urls[randomIndex] + return rpcUrl, nil +} + +func (ps *RoundRobinProviderSelector) Close() { + // do nothing +} + +func NewRoundRobinProviderSelector(spec ProviderSelectorSpec) ProviderSelector { + return &RoundRobinProviderSelector{ + providers: spec.Urls, + } +} diff --git a/pkg/filters/proxies/providerproxy/rpc.go b/pkg/filters/proxies/providerproxy/selector/rpc.go similarity index 72% rename from pkg/filters/proxies/providerproxy/rpc.go rename to pkg/filters/proxies/providerproxy/selector/rpc.go index c58071603a..7a9105ff3a 100644 --- a/pkg/filters/proxies/providerproxy/rpc.go +++ b/pkg/filters/proxies/providerproxy/selector/rpc.go @@ -1,4 +1,21 @@ -package providerproxy +/* + * Copyright (c) 2017, The Easegress Authors + * All rights reserved. + * + * Licensed 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 selector import ( "bytes" diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh new file mode 100755 index 0000000000..1888a7a6a4 --- /dev/null +++ b/scripts/upgrade.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +set -e + +RED='\033[0;31m' +NC='\033[0m' + +# First - check OS. +OS="$(uname)" +if [[ "${OS}" == "Linux" ]]; then + OS=linux + DISTRO=$(awk -F= '/^NAME/{print $2}' /etc/os-release | tr -d '\"') +elif [[ "${OS}" == "Darwin" ]];then + OS=darwin +else + echo -e "Error: ${RED}Unsupport OS - ${OS}${NC}" + exit +fi + +# Second - check the CPU arch +# refer to: https://stackoverflow.com/questions/45125516/possible-values-for-uname-m +ARCH=$(uname -m) +if [[ $ARCH == x86_64 ]]; then + ARCH=amd64 +elif [[ $ARCH == i686 || $ARCH == i386 ]]; then + ARCH=386 +elif [[ $ARCH == aarch64* || $ARCH == armv8* || $ARCH == arm64* ]]; then + ARCH=arm64 +else + echo -e "Error: ${RED}Unsupport CPU - ${ARCH}${NC}" + exit +fi + +# Third - download the binaries +GITHUB_URL=https://github.com/chainbase-labs/easegress-providerproxy +LATEST_RELEASE=$(curl -L -s -H 'Accept: application/json' ${GITHUB_URL}/releases/latest) +LATEST_VERSION=$(echo $LATEST_RELEASE | sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/') +ARTIFACT="easegress-providerproxy-${LATEST_VERSION}-${OS}-${ARCH}.tar.gz" +ARTIFACT_URL="${GITHUB_URL}/releases/download/${LATEST_VERSION}/${ARTIFACT}" + + +read -p "Enter the install directory [$(pwd)/easegress]: " DIR +DIR=${DIR:-$(pwd)/easegress} +BINDIR=${DIR}/bin + +mkdir -p ${DIR} +echo "Create the directory - \"${DIR}\" successfully." +echo "Downloading the release file - \"${ARTIFACT}\" ..." +curl -sL ${ARTIFACT_URL} -o ${DIR}/${ARTIFACT} +echo "Downloaded \"${ARTIFACT}\"" +tar -zxf ${DIR}/${ARTIFACT} -C "${DIR}" +echo "Extract the files successfully" + + +FILE=${DIR}/config.yaml +if [ -f $FILE ]; then + echo "File $FILE exists." +else + # Fourth - configure the easegress + echo "Download the config.yaml file" + RAW_GITHUB_URL=https://raw.githubusercontent.com/chainbase-labs/easegress-providerproxy + curl -sL ${RAW_GITHUB_URL}/main/scripts/config.yaml -o ${DIR}/config.yaml + sed -i -e "s~##DIR##~${DIR}~g" ${DIR}/config.yaml +fi + + +if [[ "${OS}" == "linux" ]]; then + + # SELinux prevents you from running a system service where the binary is in a user's home directory. + # We have to copy the binary to a proper directory, such as /usr/local/bin + if [[ "${DISTRO}" == "CentOS"* ]] && [[ $(getenforce) != "Disabled" ]] && [[ "$(pwd)" == "/home"* ]]; then + BINDIR=/usr/local/bin + echo "SELinux enabled, cannot install in home, copy Easegress to ${BINDIR}" + sudo cp -f ${DIR}/bin/* ${BINDIR} + fi + + + systemctl -q is-active easegress.service && echo "Stop the easegress service" && systemctl stop easegress.service + + + echo "Start the easegress service" + systemctl start easegress.service +fi + +echo "Upgrade successfully"