istio

Форк
0
165 строк · 4.8 Кб
1
// Copyright Istio Authors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
package util
16

17
import (
18
	"bytes"
19
	"fmt"
20
	"net"
21
	"strconv"
22
	"strings"
23
	"time"
24

25
	multierror "github.com/hashicorp/go-multierror"
26

27
	"istio.io/istio/pkg/http"
28
)
29

30
const (
31
	statCdsRejected    = "cluster_manager.cds.update_rejected"
32
	statsCdsSuccess    = "cluster_manager.cds.update_success"
33
	statLdsRejected    = "listener_manager.lds.update_rejected"
34
	statLdsSuccess     = "listener_manager.lds.update_success"
35
	statServerState    = "server.state"
36
	statWorkersStarted = "listener_manager.workers_started"
37
	readyStatsRegex    = "^(server\\.state|listener_manager\\.workers_started)"
38
	updateStatsRegex   = "^(cluster_manager\\.cds|listener_manager\\.lds)\\.(update_success|update_rejected)$"
39
)
40

41
var readinessTimeout = time.Second * 3 // Default Readiness timeout. It is set the same in helm charts.
42

43
type stat struct {
44
	name  string
45
	value *uint64
46
	found bool
47
}
48

49
// Stats contains values of interest from a poll of Envoy stats.
50
type Stats struct {
51
	// Update Stats.
52
	CDSUpdatesSuccess   uint64
53
	CDSUpdatesRejection uint64
54
	LDSUpdatesSuccess   uint64
55
	LDSUpdatesRejection uint64
56
	// Server State of Envoy.
57
	ServerState    uint64
58
	WorkersStarted uint64
59
}
60

61
// String representation of the Stats.
62
func (s *Stats) String() string {
63
	return fmt.Sprintf("cds updates: %d successful, %d rejected; lds updates: %d successful, %d rejected",
64
		s.CDSUpdatesSuccess,
65
		s.CDSUpdatesRejection,
66
		s.LDSUpdatesSuccess,
67
		s.LDSUpdatesRejection)
68
}
69

70
// GetReadinessStats returns the current Envoy state by checking the "server.state" stat.
71
func GetReadinessStats(localHostAddr string, adminPort uint16) (*uint64, bool, error) {
72
	// If the localHostAddr was not set, we use 'localhost' to void empty host in URL.
73
	if localHostAddr == "" {
74
		localHostAddr = "localhost"
75
	}
76

77
	hostPort := net.JoinHostPort(localHostAddr, strconv.Itoa(int(adminPort)))
78
	readinessURL := fmt.Sprintf("http://%s/stats?usedonly&filter=%s", hostPort, readyStatsRegex)
79
	stats, err := http.DoHTTPGetWithTimeout(readinessURL, readinessTimeout)
80
	if err != nil {
81
		return nil, false, err
82
	}
83
	if !strings.Contains(stats.String(), "server.state") {
84
		return nil, false, fmt.Errorf("server.state is not yet updated: %s", stats.String())
85
	}
86

87
	if !strings.Contains(stats.String(), "listener_manager.workers_started") {
88
		return nil, false, fmt.Errorf("listener_manager.workers_started is not yet updated: %s", stats.String())
89
	}
90

91
	s := &Stats{}
92
	allStats := []*stat{
93
		{name: statServerState, value: &s.ServerState},
94
		{name: statWorkersStarted, value: &s.WorkersStarted},
95
	}
96
	if err := parseStats(stats, allStats); err != nil {
97
		return nil, false, err
98
	}
99

100
	return &s.ServerState, s.WorkersStarted == 1, nil
101
}
102

103
// GetUpdateStatusStats returns the version stats for CDS and LDS.
104
func GetUpdateStatusStats(localHostAddr string, adminPort uint16) (*Stats, error) {
105
	// If the localHostAddr was not set, we use 'localhost' to void empty host in URL.
106
	if localHostAddr == "" {
107
		localHostAddr = "localhost"
108
	}
109

110
	hostPort := net.JoinHostPort(localHostAddr, strconv.Itoa(int(adminPort)))
111
	stats, err := http.DoHTTPGet(fmt.Sprintf("http://%s/stats?usedonly&filter=%s", hostPort, updateStatsRegex))
112
	if err != nil {
113
		return nil, err
114
	}
115

116
	s := &Stats{}
117
	allStats := []*stat{
118
		{name: statsCdsSuccess, value: &s.CDSUpdatesSuccess},
119
		{name: statCdsRejected, value: &s.CDSUpdatesRejection},
120
		{name: statLdsSuccess, value: &s.LDSUpdatesSuccess},
121
		{name: statLdsRejected, value: &s.LDSUpdatesRejection},
122
	}
123
	if err := parseStats(stats, allStats); err != nil {
124
		return nil, err
125
	}
126

127
	return s, nil
128
}
129

130
func parseStats(input *bytes.Buffer, stats []*stat) (err error) {
131
	for input.Len() > 0 {
132
		line, _ := input.ReadString('\n')
133
		for _, stat := range stats {
134
			if e := stat.processLine(line); e != nil {
135
				err = multierror.Append(err, e)
136
			}
137
		}
138
	}
139
	for _, stat := range stats {
140
		if !stat.found {
141
			*stat.value = 0
142
		}
143
	}
144
	return
145
}
146

147
func (s *stat) processLine(line string) error {
148
	if !s.found && strings.HasPrefix(line, s.name) {
149
		s.found = true
150

151
		parts := strings.Split(line, ":")
152
		if len(parts) != 2 {
153
			return fmt.Errorf("envoy stat %s missing separator. line:%s", s.name, line)
154
		}
155

156
		val, err := strconv.ParseUint(strings.TrimSpace(parts[1]), 10, 64)
157
		if err != nil {
158
			return fmt.Errorf("failed parsing Envoy stat %s (error: %s) line: %s", s.name, err.Error(), line)
159
		}
160

161
		*s.value = val
162
	}
163

164
	return nil
165
}
166

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.