github.com/cilium/cilium@v1.16.2/pkg/envoy/envoyadminclient.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package envoy 5 6 import ( 7 "context" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "net" 12 "net/http" 13 "strings" 14 15 "github.com/sirupsen/logrus" 16 17 "github.com/cilium/cilium/pkg/safeio" 18 ) 19 20 type EnvoyAdminClient struct { 21 adminURL string 22 unixPath string 23 level string 24 } 25 26 func NewEnvoyAdminClientForSocket(envoySocketDir string) *EnvoyAdminClient { 27 return &EnvoyAdminClient{ 28 // Needs to be provided to envoy (received as ':authority') - even though we Dial to a Unix domain socket. 29 adminURL: fmt.Sprintf("http://%s/", "envoy-admin"), 30 unixPath: getAdminSocketPath(envoySocketDir), 31 } 32 } 33 34 func (a *EnvoyAdminClient) transact(query string) error { 35 // Use a custom dialer to use a Unix domain socket for an HTTP connection. 36 var conn net.Conn 37 var err error 38 client := &http.Client{ 39 Transport: &http.Transport{ 40 DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { 41 conn, err = net.Dial("unix", a.unixPath) 42 return conn, err 43 }, 44 }, 45 } 46 47 resp, err := client.Post(a.adminURL+query, "", nil) 48 if err != nil { 49 return err 50 } 51 defer conn.Close() 52 defer resp.Body.Close() 53 body, err := safeio.ReadAllLimit(resp.Body, safeio.MB) 54 if err != nil { 55 return err 56 } 57 ret := strings.Replace(string(body), "\r", "", -1) 58 log.Debugf("Envoy: Admin response to %s: %s", query, ret) 59 return nil 60 } 61 62 // ChangeLogLevel changes Envoy log level to correspond to the logrus log level 'level'. 63 func (a *EnvoyAdminClient) ChangeLogLevel(level logrus.Level) error { 64 envoyLevel := mapLogLevel(level) 65 66 if envoyLevel == a.level { 67 log.Debugf("Envoy: Log level is already set as: %v", envoyLevel) 68 return nil 69 } 70 71 err := a.transact("logging?level=" + envoyLevel) 72 if err != nil { 73 log.WithError(err).Warnf("Envoy: Failed to set log level to: %v", envoyLevel) 74 } else { 75 a.level = envoyLevel 76 } 77 return err 78 } 79 80 func (a *EnvoyAdminClient) quit() error { 81 return a.transact("quitquitquit") 82 } 83 84 // GetEnvoyVersion returns the envoy binary version string 85 func (a *EnvoyAdminClient) GetEnvoyVersion() (string, error) { 86 // Use a custom dialer to use a Unix domain socket for a HTTP connection. 87 var conn net.Conn 88 var err error 89 client := &http.Client{ 90 Transport: &http.Transport{ 91 DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { 92 conn, err = net.Dial("unix", a.unixPath) 93 return conn, err 94 }, 95 }, 96 } 97 98 resp, err := client.Get(fmt.Sprintf("%s%s", a.adminURL, "server_info")) 99 if err != nil { 100 return "", fmt.Errorf("failed to call ServerInfo endpoint: %w", err) 101 } 102 defer conn.Close() 103 defer resp.Body.Close() 104 105 body, err := safeio.ReadAllLimit(resp.Body, safeio.MB) 106 if err != nil { 107 return "", fmt.Errorf("failed to read ServerInfo response: %w", err) 108 } 109 110 serverInfo := map[string]interface{}{} 111 if err := json.Unmarshal(body, &serverInfo); err != nil { 112 return "", fmt.Errorf("failed to parse ServerInfo: %w", err) 113 } 114 115 version, ok := serverInfo["version"] 116 117 if !ok { 118 return "", errors.New("failed to read version from ServerInfo") 119 } 120 121 return fmt.Sprintf("%s", version), nil 122 }