github.com/lablabs/operator-sdk@v0.8.2/pkg/ansible/proxy/inject_owner.go (about) 1 // Copyright 2019 The Operator-SDK 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 proxy 16 17 import ( 18 "bytes" 19 "encoding/json" 20 "fmt" 21 "io/ioutil" 22 "net/http" 23 "net/http/httputil" 24 "strings" 25 26 "github.com/operator-framework/operator-sdk/pkg/ansible/proxy/controllermap" 27 "github.com/operator-framework/operator-sdk/pkg/ansible/proxy/kubeconfig" 28 k8sRequest "github.com/operator-framework/operator-sdk/pkg/ansible/proxy/requestfactory" 29 osdkHandler "github.com/operator-framework/operator-sdk/pkg/handler" 30 "k8s.io/apimachinery/pkg/api/meta" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 33 "k8s.io/apimachinery/pkg/runtime/schema" 34 "k8s.io/apimachinery/pkg/util/sets" 35 ) 36 37 // injectOwnerReferenceHandler will handle proxied requests and inject the 38 // owner reference found in the authorization header. The Authorization is 39 // then deleted so that the proxy can re-set with the correct authorization. 40 type injectOwnerReferenceHandler struct { 41 next http.Handler 42 cMap *controllermap.ControllerMap 43 restMapper meta.RESTMapper 44 watchedNamespaces map[string]interface{} 45 } 46 47 func (i *injectOwnerReferenceHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { 48 switch req.Method { 49 case http.MethodPost: 50 dump, _ := httputil.DumpRequest(req, false) 51 log.V(2).Info("Dumping request", "RequestDump", string(dump)) 52 rf := k8sRequest.RequestInfoFactory{APIPrefixes: sets.NewString("api", "apis"), GrouplessAPIPrefixes: sets.NewString("api")} 53 r, err := rf.NewRequestInfo(req) 54 if err != nil { 55 m := "Could not convert request" 56 log.Error(err, m) 57 http.Error(w, m, http.StatusBadRequest) 58 return 59 } 60 if r.Subresource != "" { 61 // Don't inject owner ref if we are POSTing to a subresource 62 break 63 } 64 log.Info("Injecting owner reference") 65 owner, err := getRequestOwnerRef(req) 66 if err != nil { 67 m := "Could not get owner reference" 68 log.Error(err, m) 69 http.Error(w, m, http.StatusInternalServerError) 70 return 71 } 72 73 body, err := ioutil.ReadAll(req.Body) 74 if err != nil { 75 m := "Could not read request body" 76 log.Error(err, m) 77 http.Error(w, m, http.StatusInternalServerError) 78 return 79 } 80 data := &unstructured.Unstructured{} 81 err = json.Unmarshal(body, data) 82 if err != nil { 83 m := "Could not deserialize request body" 84 log.Error(err, m) 85 http.Error(w, m, http.StatusBadRequest) 86 return 87 } 88 89 addOwnerRef, err := shouldAddOwnerRef(data, owner, i.restMapper) 90 if err != nil { 91 m := "Could not determine if we should add owner ref" 92 log.Error(err, m) 93 http.Error(w, m, http.StatusBadRequest) 94 return 95 } 96 if addOwnerRef { 97 data.SetOwnerReferences(append(data.GetOwnerReferences(), owner.OwnerReference)) 98 } else { 99 ownerGV, err := schema.ParseGroupVersion(owner.APIVersion) 100 if err != nil { 101 m := fmt.Sprintf("could not get broup version for: %v", owner) 102 log.Error(err, m) 103 http.Error(w, m, http.StatusBadRequest) 104 return 105 } 106 a := data.GetAnnotations() 107 if a == nil { 108 a = map[string]string{} 109 } 110 a[osdkHandler.NamespacedNameAnnotation] = strings.Join([]string{owner.Namespace, owner.Name}, "/") 111 a[osdkHandler.TypeAnnotation] = fmt.Sprintf("%v.%v", owner.Kind, ownerGV.Group) 112 113 data.SetAnnotations(a) 114 } 115 newBody, err := json.Marshal(data.Object) 116 if err != nil { 117 m := "Could not serialize body" 118 log.Error(err, m) 119 http.Error(w, m, http.StatusInternalServerError) 120 return 121 } 122 log.V(2).Info("Serialized body", "Body", string(newBody)) 123 req.Body = ioutil.NopCloser(bytes.NewBuffer(newBody)) 124 req.ContentLength = int64(len(newBody)) 125 126 // add watch for resource 127 // check if resource doesn't exist in watched namespaces 128 // if watchedNamespaces[""] exists then we are watching all namespaces 129 // and want to continue 130 // This is making sure we are not attempting to watch a resource outside of the 131 // namespaces that the cache can watch. 132 _, allNsPresent := i.watchedNamespaces[metav1.NamespaceAll] 133 _, reqNsPresent := i.watchedNamespaces[r.Namespace] 134 if allNsPresent || reqNsPresent { 135 err = addWatchToController(owner, i.cMap, data, i.restMapper, addOwnerRef) 136 if err != nil { 137 m := "could not add watch to controller" 138 log.Error(err, m) 139 http.Error(w, m, http.StatusInternalServerError) 140 return 141 } 142 } 143 } 144 i.next.ServeHTTP(w, req) 145 } 146 147 func shouldAddOwnerRef(data *unstructured.Unstructured, owner kubeconfig.NamespacedOwnerReference, restMapper meta.RESTMapper) (bool, error) { 148 dataMapping, err := restMapper.RESTMapping(data.GroupVersionKind().GroupKind(), data.GroupVersionKind().Version) 149 if err != nil { 150 m := fmt.Sprintf("Could not get rest mapping for: %v", data.GroupVersionKind()) 151 log.Error(err, m) 152 return false, err 153 154 } 155 // We need to determine whether or not the owner is a cluster-scoped 156 // resource because enqueue based on an owner reference does not work if 157 // a namespaced resource owns a cluster-scoped resource 158 ownerGV, err := schema.ParseGroupVersion(owner.APIVersion) 159 if err != nil { 160 m := fmt.Sprintf("could not get group version for: %v", owner) 161 log.Error(err, m) 162 return false, err 163 } 164 ownerMapping, err := restMapper.RESTMapping(schema.GroupKind{Kind: owner.Kind, Group: ownerGV.Group}, ownerGV.Version) 165 if err != nil { 166 m := fmt.Sprintf("could not get rest mapping for: %v", owner) 167 log.Error(err, m) 168 return false, err 169 } 170 171 dataNamespaceScoped := dataMapping.Scope.Name() != meta.RESTScopeNameRoot 172 ownerNamespaceScoped := ownerMapping.Scope.Name() != meta.RESTScopeNameRoot 173 174 if dataNamespaceScoped && ownerNamespaceScoped && data.GetNamespace() == owner.Namespace { 175 return true, nil 176 } 177 return false, nil 178 }