sigs.k8s.io/external-dns@v0.14.1/provider/webhook/api/httpapi.go (about) 1 /* 2 Copyright 2023 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 api 18 19 import ( 20 "context" 21 "encoding/json" 22 "net" 23 "net/http" 24 "time" 25 26 "sigs.k8s.io/external-dns/endpoint" 27 "sigs.k8s.io/external-dns/plan" 28 "sigs.k8s.io/external-dns/provider" 29 30 log "github.com/sirupsen/logrus" 31 ) 32 33 const ( 34 MediaTypeFormatAndVersion = "application/external.dns.webhook+json;version=1" 35 ContentTypeHeader = "Content-Type" 36 ) 37 38 type WebhookServer struct { 39 Provider provider.Provider 40 } 41 42 func (p *WebhookServer) RecordsHandler(w http.ResponseWriter, req *http.Request) { 43 switch req.Method { 44 case http.MethodGet: 45 records, err := p.Provider.Records(context.Background()) 46 if err != nil { 47 log.Errorf("Failed to get Records: %v", err) 48 w.WriteHeader(http.StatusInternalServerError) 49 return 50 } 51 w.Header().Set(ContentTypeHeader, MediaTypeFormatAndVersion) 52 w.WriteHeader(http.StatusOK) 53 if err := json.NewEncoder(w).Encode(records); err != nil { 54 log.Errorf("Failed to encode records: %v", err) 55 } 56 return 57 case http.MethodPost: 58 var changes plan.Changes 59 if err := json.NewDecoder(req.Body).Decode(&changes); err != nil { 60 log.Errorf("Failed to decode changes: %v", err) 61 w.WriteHeader(http.StatusBadRequest) 62 return 63 } 64 err := p.Provider.ApplyChanges(context.Background(), &changes) 65 if err != nil { 66 log.Errorf("Failed to apply changes: %v", err) 67 w.WriteHeader(http.StatusInternalServerError) 68 return 69 } 70 w.WriteHeader(http.StatusNoContent) 71 return 72 default: 73 log.Errorf("Unsupported method %s", req.Method) 74 w.WriteHeader(http.StatusBadRequest) 75 } 76 } 77 78 func (p *WebhookServer) AdjustEndpointsHandler(w http.ResponseWriter, req *http.Request) { 79 if req.Method != http.MethodPost { 80 log.Errorf("Unsupported method %s", req.Method) 81 w.WriteHeader(http.StatusBadRequest) 82 return 83 } 84 85 pve := []*endpoint.Endpoint{} 86 if err := json.NewDecoder(req.Body).Decode(&pve); err != nil { 87 log.Errorf("Failed to decode in adjustEndpointsHandler: %v", err) 88 w.WriteHeader(http.StatusBadRequest) 89 return 90 } 91 w.Header().Set(ContentTypeHeader, MediaTypeFormatAndVersion) 92 pve, err := p.Provider.AdjustEndpoints(pve) 93 if err != nil { 94 log.Errorf("Failed to call adjust endpoints: %v", err) 95 w.WriteHeader(http.StatusInternalServerError) 96 } 97 if err := json.NewEncoder(w).Encode(&pve); err != nil { 98 log.Errorf("Failed to encode in adjustEndpointsHandler: %v", err) 99 w.WriteHeader(http.StatusInternalServerError) 100 return 101 } 102 } 103 104 func (p *WebhookServer) NegotiateHandler(w http.ResponseWriter, req *http.Request) { 105 w.Header().Set(ContentTypeHeader, MediaTypeFormatAndVersion) 106 json.NewEncoder(w).Encode(p.Provider.GetDomainFilter()) 107 } 108 109 // StartHTTPApi starts a HTTP server given any provider. 110 // the function takes an optional channel as input which is used to signal that the server has started. 111 // The server will listen on port `providerPort`. 112 // The server will respond to the following endpoints: 113 // - / (GET): initialization, negotiates headers and returns the domain filter 114 // - /records (GET): returns the current records 115 // - /records (POST): applies the changes 116 // - /adjustendpoints (POST): executes the AdjustEndpoints method 117 func StartHTTPApi(provider provider.Provider, startedChan chan struct{}, readTimeout, writeTimeout time.Duration, providerPort string) { 118 p := WebhookServer{ 119 Provider: provider, 120 } 121 122 m := http.NewServeMux() 123 m.HandleFunc("/", p.NegotiateHandler) 124 m.HandleFunc("/records", p.RecordsHandler) 125 m.HandleFunc("/adjustendpoints", p.AdjustEndpointsHandler) 126 127 s := &http.Server{ 128 Addr: providerPort, 129 Handler: m, 130 ReadTimeout: readTimeout, 131 WriteTimeout: writeTimeout, 132 } 133 134 l, err := net.Listen("tcp", providerPort) 135 if err != nil { 136 log.Fatal(err) 137 } 138 139 if startedChan != nil { 140 startedChan <- struct{}{} 141 } 142 143 if err := s.Serve(l); err != nil { 144 log.Fatal(err) 145 } 146 }