sigs.k8s.io/external-dns@v0.14.1/source/gloo_proxy.go (about) 1 /* 2 Copyright 2020n The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package source 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 "strings" 24 25 log "github.com/sirupsen/logrus" 26 corev1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/runtime/schema" 29 "k8s.io/client-go/dynamic" 30 "k8s.io/client-go/kubernetes" 31 32 "sigs.k8s.io/external-dns/endpoint" 33 ) 34 35 var ( 36 proxyGVR = schema.GroupVersionResource{ 37 Group: "gloo.solo.io", 38 Version: "v1", 39 Resource: "proxies", 40 } 41 virtualServiceGVR = schema.GroupVersionResource{ 42 Group: "gateway.solo.io", 43 Version: "v1", 44 Resource: "virtualservices", 45 } 46 ) 47 48 // Basic redefinition of "Proxy" CRD : https://github.com/solo-io/gloo/blob/v1.4.6/projects/gloo/pkg/api/v1/proxy.pb.go 49 type proxy struct { 50 metav1.TypeMeta `json:",inline"` 51 Metadata metav1.ObjectMeta `json:"metadata,omitempty"` 52 Spec proxySpec `json:"spec,omitempty"` 53 } 54 55 type proxySpec struct { 56 Listeners []proxySpecListener `json:"listeners,omitempty"` 57 } 58 59 type proxySpecListener struct { 60 HTTPListener proxySpecHTTPListener `json:"httpListener,omitempty"` 61 } 62 63 type proxySpecHTTPListener struct { 64 VirtualHosts []proxyVirtualHost `json:"virtualHosts,omitempty"` 65 } 66 67 type proxyVirtualHost struct { 68 Domains []string `json:"domains,omitempty"` 69 Metadata proxyVirtualHostMetadata `json:"metadata,omitempty"` 70 MetadataStatic proxyVirtualHostMetadataStatic `json:"metadataStatic,omitempty"` 71 } 72 73 type proxyVirtualHostMetadata struct { 74 Source []proxyVirtualHostMetadataSource `json:"sources,omitempty"` 75 } 76 77 type proxyVirtualHostMetadataStatic struct { 78 Source []proxyVirtualHostMetadataStaticSource `json:"sources,omitempty"` 79 } 80 81 type proxyVirtualHostMetadataSource struct { 82 Kind string `json:"kind,omitempty"` 83 Name string `json:"name,omitempty"` 84 Namespace string `json:"namespace,omitempty"` 85 } 86 87 type proxyVirtualHostMetadataStaticSource struct { 88 ResourceKind string `json:"resourceKind,omitempty"` 89 ResourceRef proxyVirtualHostMetadataSourceResourceRef `json:"resourceRef,omitempty"` 90 } 91 92 type proxyVirtualHostMetadataSourceResourceRef struct { 93 Name string `json:"name,omitempty"` 94 Namespace string `json:"namespace,omitempty"` 95 } 96 97 type glooSource struct { 98 dynamicKubeClient dynamic.Interface 99 kubeClient kubernetes.Interface 100 glooNamespaces []string 101 } 102 103 // NewGlooSource creates a new glooSource with the given config 104 func NewGlooSource(dynamicKubeClient dynamic.Interface, kubeClient kubernetes.Interface, 105 glooNamespaces []string) (Source, error) { 106 return &glooSource{ 107 dynamicKubeClient, 108 kubeClient, 109 glooNamespaces, 110 }, nil 111 } 112 113 func (gs *glooSource) AddEventHandler(ctx context.Context, handler func()) { 114 } 115 116 // Endpoints returns endpoint objects 117 func (gs *glooSource) Endpoints(ctx context.Context) ([]*endpoint.Endpoint, error) { 118 endpoints := []*endpoint.Endpoint{} 119 120 for _, ns := range gs.glooNamespaces { 121 proxies, err := gs.dynamicKubeClient.Resource(proxyGVR).Namespace(ns).List(ctx, metav1.ListOptions{}) 122 if err != nil { 123 return nil, err 124 } 125 for _, obj := range proxies.Items { 126 proxy := proxy{} 127 jsonString, err := obj.MarshalJSON() 128 if err != nil { 129 return nil, err 130 } 131 err = json.Unmarshal(jsonString, &proxy) 132 if err != nil { 133 return nil, err 134 } 135 log.Debugf("Gloo: Find %s proxy", proxy.Metadata.Name) 136 137 proxyTargets := getTargetsFromTargetAnnotation(proxy.Metadata.Annotations) 138 if len(proxyTargets) == 0 { 139 proxyTargets, err = gs.proxyTargets(ctx, proxy.Metadata.Name, ns) 140 if err != nil { 141 return nil, err 142 } 143 } 144 log.Debugf("Gloo[%s]: Find %d target(s) (%+v)", proxy.Metadata.Name, len(proxyTargets), proxyTargets) 145 146 proxyEndpoints, err := gs.generateEndpointsFromProxy(ctx, &proxy, proxyTargets) 147 if err != nil { 148 return nil, err 149 } 150 log.Debugf("Gloo[%s]: Generate %d endpoint(s)", proxy.Metadata.Name, len(proxyEndpoints)) 151 endpoints = append(endpoints, proxyEndpoints...) 152 } 153 } 154 return endpoints, nil 155 } 156 157 func (gs *glooSource) generateEndpointsFromProxy(ctx context.Context, proxy *proxy, targets endpoint.Targets) ([]*endpoint.Endpoint, error) { 158 endpoints := []*endpoint.Endpoint{} 159 160 resource := fmt.Sprintf("proxy/%s/%s", proxy.Metadata.Namespace, proxy.Metadata.Name) 161 162 for _, listener := range proxy.Spec.Listeners { 163 for _, virtualHost := range listener.HTTPListener.VirtualHosts { 164 annotations, err := gs.annotationsFromProxySource(ctx, virtualHost) 165 if err != nil { 166 return nil, err 167 } 168 ttl := getTTLFromAnnotations(annotations, resource) 169 providerSpecific, setIdentifier := getProviderSpecificAnnotations(annotations) 170 for _, domain := range virtualHost.Domains { 171 endpoints = append(endpoints, endpointsForHostname(strings.TrimSuffix(domain, "."), targets, ttl, providerSpecific, setIdentifier, "")...) 172 } 173 } 174 } 175 return endpoints, nil 176 } 177 178 func (gs *glooSource) annotationsFromProxySource(ctx context.Context, virtualHost proxyVirtualHost) (map[string]string, error) { 179 annotations := map[string]string{} 180 for _, src := range virtualHost.Metadata.Source { 181 kind := sourceKind(src.Kind) 182 if kind != nil { 183 source, err := gs.dynamicKubeClient.Resource(*kind).Namespace(src.Namespace).Get(ctx, src.Name, metav1.GetOptions{}) 184 if err != nil { 185 return nil, err 186 } 187 for key, value := range source.GetAnnotations() { 188 annotations[key] = value 189 } 190 } 191 } 192 for _, src := range virtualHost.MetadataStatic.Source { 193 kind := sourceKind(src.ResourceKind) 194 if kind != nil { 195 source, err := gs.dynamicKubeClient.Resource(*kind).Namespace(src.ResourceRef.Namespace).Get(ctx, src.ResourceRef.Name, metav1.GetOptions{}) 196 if err != nil { 197 return nil, err 198 } 199 for key, value := range source.GetAnnotations() { 200 annotations[key] = value 201 } 202 } 203 } 204 return annotations, nil 205 } 206 207 func (gs *glooSource) proxyTargets(ctx context.Context, name string, namespace string) (endpoint.Targets, error) { 208 svc, err := gs.kubeClient.CoreV1().Services(namespace).Get(ctx, name, metav1.GetOptions{}) 209 if err != nil { 210 return nil, err 211 } 212 213 var targets endpoint.Targets 214 switch svc.Spec.Type { 215 case corev1.ServiceTypeLoadBalancer: 216 for _, lb := range svc.Status.LoadBalancer.Ingress { 217 if lb.IP != "" { 218 targets = append(targets, lb.IP) 219 } 220 if lb.Hostname != "" { 221 targets = append(targets, lb.Hostname) 222 } 223 } 224 default: 225 log.WithField("gateway", name).WithField("service", svc).Warn("Gloo: Proxy service type not supported") 226 } 227 return targets, nil 228 } 229 230 func sourceKind(kind string) *schema.GroupVersionResource { 231 switch kind { 232 case "*v1.VirtualService": 233 return &virtualServiceGVR 234 } 235 return nil 236 }