istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/writer/envoy/configdump/endpoint.go (about) 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 configdump 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "sort" 21 "strconv" 22 "strings" 23 "text/tabwriter" 24 25 core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 26 endpoint "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" 27 "google.golang.org/protobuf/types/known/anypb" 28 "sigs.k8s.io/yaml" 29 30 "istio.io/istio/istioctl/pkg/util/proto" 31 "istio.io/istio/pilot/pkg/networking/util" 32 ) 33 34 type EndpointFilter struct { 35 Address string 36 Port uint32 37 Cluster string 38 Status string 39 } 40 41 // Verify returns true if the passed host matches the filter fields 42 func (e *EndpointFilter) Verify(ep *endpoint.LbEndpoint, cluster string) bool { 43 if e.Address == "" && e.Port == 0 && e.Cluster == "" && e.Status == "" { 44 return true 45 } 46 if e.Address != "" && !strings.EqualFold(retrieveEndpointAddress(ep), e.Address) { 47 return false 48 } 49 if e.Port != 0 && retrieveEndpointPort(ep) != e.Port { 50 return false 51 } 52 if e.Cluster != "" && !strings.EqualFold(cluster, e.Cluster) { 53 return false 54 } 55 status := retrieveEndpointStatus(ep) 56 if e.Status != "" && !strings.EqualFold(core.HealthStatus_name[int32(status)], e.Status) { 57 return false 58 } 59 return true 60 } 61 62 func retrieveEndpointStatus(ep *endpoint.LbEndpoint) core.HealthStatus { 63 return ep.GetHealthStatus() 64 } 65 66 func retrieveEndpointPort(ep *endpoint.LbEndpoint) uint32 { 67 return ep.GetEndpoint().GetAddress().GetSocketAddress().GetPortValue() 68 } 69 70 func retrieveEndpointAddress(ep *endpoint.LbEndpoint) string { 71 addr := ep.GetEndpoint().GetAddress() 72 if addr := addr.GetSocketAddress(); addr != nil { 73 return addr.Address + ":" + strconv.Itoa(int(addr.GetPortValue())) 74 } 75 if addr := addr.GetPipe(); addr != nil { 76 return addr.GetPath() 77 } 78 if internal := addr.GetEnvoyInternalAddress(); internal != nil { 79 switch an := internal.GetAddressNameSpecifier().(type) { 80 case *core.EnvoyInternalAddress_ServerListenerName: 81 return fmt.Sprintf("envoy://%s/%s", an.ServerListenerName, internal.EndpointId) 82 } 83 } 84 return "unknown" 85 } 86 87 func (c *ConfigWriter) PrintEndpoints(filter EndpointFilter, outputFormat string) error { 88 if c.configDump == nil { 89 return fmt.Errorf("config writer has not been primed") 90 } 91 dump, err := c.retrieveSortedEndpointsSlice(filter) 92 if err != nil { 93 return err 94 } 95 marshaller := make(proto.MessageSlice, 0, len(dump)) 96 for _, eds := range dump { 97 marshaller = append(marshaller, eds) 98 } 99 out, err := json.MarshalIndent(marshaller, "", " ") 100 if err != nil { 101 return err 102 } 103 if outputFormat == "yaml" { 104 if out, err = yaml.JSONToYAML(out); err != nil { 105 return err 106 } 107 } 108 _, _ = fmt.Fprintln(c.Stdout, string(out)) 109 return nil 110 } 111 112 func (c *ConfigWriter) PrintEndpointsSummary(filter EndpointFilter) error { 113 w := new(tabwriter.Writer).Init(c.Stdout, 0, 8, 5, ' ', 0) 114 115 fmt.Fprintln(w, "NAME\tSTATUS\tLOCALITY\tCLUSTER") 116 dump, err := c.retrieveSortedEndpointsSlice(filter) 117 if err != nil { 118 return err 119 } 120 for _, eds := range dump { 121 for _, llb := range eds.Endpoints { 122 for _, ep := range llb.LbEndpoints { 123 addr := retrieveEndpointAddress(ep) 124 if includeConfigType { 125 addr = fmt.Sprintf("endpoint/%s", addr) 126 } 127 fmt.Fprintf(w, "%v\t%v\t%v\t%v\n", 128 addr, 129 ep.GetHealthStatus().String(), 130 util.LocalityToString(llb.Locality), 131 eds.ClusterName, 132 ) 133 } 134 } 135 } 136 137 return w.Flush() 138 } 139 140 func (c *ConfigWriter) retrieveSortedEndpointsSlice(filter EndpointFilter) ([]*endpoint.ClusterLoadAssignment, error) { 141 if c.configDump == nil { 142 return nil, fmt.Errorf("config writer has not been primed") 143 } 144 dump, err := c.configDump.GetEndpointsConfigDump() 145 if err != nil { 146 return nil, err 147 } 148 if dump == nil { 149 return nil, nil 150 } 151 endpoints := make([]*endpoint.ClusterLoadAssignment, 0, len(dump.DynamicEndpointConfigs)) 152 for _, e := range dump.GetDynamicEndpointConfigs() { 153 cla, epCount := retrieveEndpoint(e.EndpointConfig, filter) 154 if epCount != 0 { 155 endpoints = append(endpoints, cla) 156 } 157 } 158 for _, e := range dump.GetStaticEndpointConfigs() { 159 cla, epCount := retrieveEndpoint(e.EndpointConfig, filter) 160 if epCount != 0 { 161 endpoints = append(endpoints, cla) 162 } 163 } 164 sort.Slice(endpoints, func(i, j int) bool { 165 iDirection, iSubset, iName, iPort := safelyParseSubsetKey(endpoints[i].ClusterName) 166 jDirection, jSubset, jName, jPort := safelyParseSubsetKey(endpoints[j].ClusterName) 167 if iName == jName { 168 if iSubset == jSubset { 169 if iPort == jPort { 170 return iDirection < jDirection 171 } 172 return iPort < jPort 173 } 174 return iSubset < jSubset 175 } 176 return iName < jName 177 }) 178 return endpoints, nil 179 } 180 181 func retrieveEndpoint(epConfig *anypb.Any, filter EndpointFilter) (*endpoint.ClusterLoadAssignment, int) { 182 cla := &endpoint.ClusterLoadAssignment{} 183 if err := epConfig.UnmarshalTo(cla); err != nil { 184 return nil, 0 185 } 186 filteredCount := 0 187 for _, llb := range cla.Endpoints { 188 filtered := make([]*endpoint.LbEndpoint, 0, len(llb.LbEndpoints)) 189 for _, ep := range llb.LbEndpoints { 190 if !filter.Verify(ep, cla.ClusterName) { 191 continue 192 } 193 filtered = append(filtered, ep) 194 } 195 llb.LbEndpoints = filtered 196 filteredCount += len(llb.LbEndpoints) 197 } 198 199 return cla, filteredCount 200 }