istio
233 строки · 7.2 Кб
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
15package ambient
16
17import (
18"fmt"
19"net/netip"
20"strings"
21
22v1 "k8s.io/api/core/v1"
23
24"istio.io/istio/pilot/pkg/model"
25"istio.io/istio/pkg/config/labels"
26"istio.io/istio/pkg/workloadapi"
27)
28
29// name format: <cluster>/<group>/<kind>/<namespace>/<name></section-name>
30func (a *index) generatePodUID(p *v1.Pod) string {
31return a.ClusterID.String() + "//" + "Pod/" + p.Namespace + "/" + p.Name
32}
33
34// name format: <cluster>/<group>/<kind>/<namespace>/<name></section-name>
35// if the WorkloadEntry is inlined in the ServiceEntry, we may need section name. caller should use generateServiceEntryUID
36func (a *index) generateWorkloadEntryUID(wkEntryNamespace, wkEntryName string) string {
37return a.ClusterID.String() + "/networking.istio.io/WorkloadEntry/" + wkEntryNamespace + "/" + wkEntryName
38}
39
40// name format: <cluster>/<group>/<kind>/<namespace>/<name></section-name>
41// section name should be the WE address, which needs to be stable across SE updates (it is assumed WE addresses are unique)
42func (a *index) generateServiceEntryUID(svcEntryNamespace, svcEntryName, addr string) string {
43return a.ClusterID.String() + "/networking.istio.io/ServiceEntry/" + svcEntryNamespace + "/" + svcEntryName + "/" + addr
44}
45
46func workloadToAddressInfo(w *workloadapi.Workload) model.AddressInfo {
47return model.AddressInfo{
48Address: &workloadapi.Address{
49Type: &workloadapi.Address_Workload{
50Workload: w,
51},
52},
53}
54}
55
56func modelWorkloadToAddressInfo(w model.WorkloadInfo) model.AddressInfo {
57return workloadToAddressInfo(w.Workload)
58}
59
60func serviceToAddressInfo(s *workloadapi.Service) model.AddressInfo {
61return model.AddressInfo{
62Address: &workloadapi.Address{
63Type: &workloadapi.Address_Service{
64Service: s,
65},
66},
67}
68}
69
70func mustByteIPToString(b []byte) string {
71ip, _ := netip.AddrFromSlice(b) // Address only comes from objects we create, so it must be valid
72return ip.String()
73}
74
75func byteIPToAddr(b []byte) netip.Addr {
76ip, _ := netip.AddrFromSlice(b) // Address only comes from objects we create, so it must be valid
77return ip
78}
79
80func (a index) getWaypointAddress(w *Waypoint) *workloadapi.GatewayAddress {
81// probably overly cautious... I don't think the ambient index impl counts something with zero addresses as waypoint
82if w != nil && len(w.Addresses) >= 1 {
83return &workloadapi.GatewayAddress{
84Destination: &workloadapi.GatewayAddress_Address{
85// probably use from Cidr instead?
86Address: a.toNetworkAddressFromIP(w.Addresses[0]),
87},
88// TODO: look up the HBONE port instead of hardcoding it
89HboneMtlsPort: 15008,
90}
91}
92return nil
93}
94
95func (a *index) toNetworkAddress(vip string) (*workloadapi.NetworkAddress, error) {
96ip, err := netip.ParseAddr(vip)
97if err != nil {
98return nil, fmt.Errorf("parse %v: %v", vip, err)
99}
100return &workloadapi.NetworkAddress{
101Network: a.Network(vip, make(labels.Instance, 0)).String(),
102Address: ip.AsSlice(),
103}, nil
104}
105
106func (a *index) toNetworkAddressFromIP(ip netip.Addr) *workloadapi.NetworkAddress {
107return &workloadapi.NetworkAddress{
108Network: a.Network(ip.String(), make(labels.Instance, 0)).String(),
109Address: ip.AsSlice(),
110}
111}
112
113func (a *index) toNetworkAddressFromCidr(vip string) (*workloadapi.NetworkAddress, error) {
114ip, err := parseCidrOrIP(vip)
115if err != nil {
116return nil, err
117}
118return &workloadapi.NetworkAddress{
119Network: a.Network(vip, make(labels.Instance, 0)).String(),
120Address: ip.AsSlice(),
121}, nil
122}
123
124// parseCidrOrIP parses an IP or a CIDR of a exactly 1 IP (e.g. /32).
125// This is to support ServiceEntry which supports CIDRs, but we don't currently support more than 1 IP
126func parseCidrOrIP(ip string) (netip.Addr, error) {
127if strings.Contains(ip, "/") {
128prefix, err := netip.ParsePrefix(ip)
129if err != nil {
130return netip.Addr{}, err
131}
132if !prefix.IsSingleIP() {
133return netip.Addr{}, fmt.Errorf("only single IP CIDR is allowed")
134}
135return prefix.Addr(), nil
136}
137return netip.ParseAddr(ip)
138}
139
140func AppendNonNil[T any](data []T, i *T) []T {
141if i != nil {
142data = append(data, *i)
143}
144return data
145}
146
147func IsPodRunning(pod *v1.Pod) bool {
148return pod.Status.Phase == v1.PodRunning
149}
150
151func IsPodPending(pod *v1.Pod) bool {
152return pod.Status.Phase == v1.PodPending
153}
154
155// IsPodReady is copied from kubernetes/pkg/api/v1/pod/utils.go
156func IsPodReady(pod *v1.Pod) bool {
157return IsPodReadyConditionTrue(pod.Status)
158}
159
160// IsPodReadyConditionTrue returns true if a pod is ready; false otherwise.
161func IsPodReadyConditionTrue(status v1.PodStatus) bool {
162condition := GetPodReadyCondition(status)
163return condition != nil && condition.Status == v1.ConditionTrue
164}
165
166func GetPodReadyCondition(status v1.PodStatus) *v1.PodCondition {
167_, condition := GetPodCondition(&status, v1.PodReady)
168return condition
169}
170
171func GetPodCondition(status *v1.PodStatus, conditionType v1.PodConditionType) (int, *v1.PodCondition) {
172if status == nil {
173return -1, nil
174}
175return GetPodConditionFromList(status.Conditions, conditionType)
176}
177
178// GetPodConditionFromList extracts the provided condition from the given list of condition and
179// returns the index of the condition and the condition. Returns -1 and nil if the condition is not present.
180func GetPodConditionFromList(conditions []v1.PodCondition, conditionType v1.PodConditionType) (int, *v1.PodCondition) {
181if conditions == nil {
182return -1, nil
183}
184for i := range conditions {
185if conditions[i].Type == conditionType {
186return i, &conditions[i]
187}
188}
189return -1, nil
190}
191
192func FindPortName(pod *v1.Pod, name string) (int32, bool) {
193for _, container := range pod.Spec.Containers {
194for _, port := range container.Ports {
195if port.Name == name && port.Protocol == v1.ProtocolTCP {
196return port.ContainerPort, true
197}
198}
199}
200return 0, false
201}
202
203func namespacedHostname(namespace, hostname string) string {
204return namespace + "/" + hostname
205}
206
207func networkAddressFromWorkload(wl model.WorkloadInfo) []networkAddress {
208networkAddrs := make([]networkAddress, 0, len(wl.Addresses))
209for _, addr := range wl.Addresses {
210// mustByteIPToString is ok since this is from our IP constructed
211networkAddrs = append(networkAddrs, networkAddress{network: wl.Network, ip: mustByteIPToString(addr)})
212}
213return networkAddrs
214}
215
216func networkAddressFromService(s model.ServiceInfo) []networkAddress {
217networkAddrs := make([]networkAddress, 0, len(s.Addresses))
218for _, addr := range s.Addresses {
219// mustByteIPToString is ok since this is from our IP constructed
220networkAddrs = append(networkAddrs, networkAddress{network: addr.Network, ip: mustByteIPToString(addr.Address)})
221}
222return networkAddrs
223}
224
225// internal object used for indexing in ambientindex maps
226type networkAddress struct {
227network string
228ip string
229}
230
231func (n *networkAddress) String() string {
232return n.network + "/" + n.ip
233}
234