github.com/fabianvf/ocp-release-operator-sdk@v0.0.0-20190426141702-57620ee2f090/pkg/ansible/proxy/proxy.go (about)

     1  // Copyright 2018 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  // This file contains this project's custom code, as opposed to kubectl.go
    18  // which contains code retrieved from the kubernetes project.
    19  
    20  import (
    21  	"bytes"
    22  	"context"
    23  	"encoding/base64"
    24  	"encoding/json"
    25  	"errors"
    26  	"fmt"
    27  	"io/ioutil"
    28  	"net/http"
    29  	"net/http/httputil"
    30  	"strings"
    31  
    32  	"github.com/operator-framework/operator-sdk/pkg/ansible/proxy/controllermap"
    33  	"github.com/operator-framework/operator-sdk/pkg/ansible/proxy/kubeconfig"
    34  	k8sRequest "github.com/operator-framework/operator-sdk/pkg/ansible/proxy/requestfactory"
    35  	osdkHandler "github.com/operator-framework/operator-sdk/pkg/handler"
    36  	"k8s.io/apimachinery/pkg/api/meta"
    37  	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
    38  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    39  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    40  	"k8s.io/apimachinery/pkg/runtime/schema"
    41  	"k8s.io/apimachinery/pkg/util/sets"
    42  	"k8s.io/client-go/rest"
    43  	"sigs.k8s.io/controller-runtime/pkg/cache"
    44  	"sigs.k8s.io/controller-runtime/pkg/client"
    45  	"sigs.k8s.io/controller-runtime/pkg/handler"
    46  	"sigs.k8s.io/controller-runtime/pkg/source"
    47  )
    48  
    49  type marshaler interface {
    50  	MarshalJSON() ([]byte, error)
    51  }
    52  
    53  // CacheResponseHandler will handle proxied requests and check if the requested
    54  // resource exists in our cache. If it does then there is no need to bombard
    55  // the APIserver with our request and we should write the response from the
    56  // proxy.
    57  func CacheResponseHandler(h http.Handler, informerCache cache.Cache, restMapper meta.RESTMapper, watchedNamespaces map[string]interface{}, cMap *controllermap.ControllerMap, injectOwnerRef bool) http.Handler {
    58  	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    59  		switch req.Method {
    60  		case http.MethodGet:
    61  			// GET request means we need to check the cache
    62  			rf := k8sRequest.RequestInfoFactory{APIPrefixes: sets.NewString("api", "apis"), GrouplessAPIPrefixes: sets.NewString("api")}
    63  			r, err := rf.NewRequestInfo(req)
    64  			if err != nil {
    65  				log.Error(err, "Failed to convert request")
    66  				break
    67  			}
    68  
    69  			// check if resource is present on request
    70  			if !r.IsResourceRequest {
    71  				break
    72  			}
    73  
    74  			// check if resource doesn't exist in watched namespaces
    75  			// if watchedNamespaces[""] exists then we are watching all namespaces
    76  			// and want to continue
    77  			_, allNsPresent := watchedNamespaces[metav1.NamespaceAll]
    78  			_, reqNsPresent := watchedNamespaces[r.Namespace]
    79  			if !allNsPresent && !reqNsPresent {
    80  				break
    81  			}
    82  
    83  			if strings.HasPrefix(r.Path, "/version") {
    84  				// Temporarily pass along to API server
    85  				// Ideally we cache this response as well
    86  				break
    87  			}
    88  
    89  			gvr := schema.GroupVersionResource{
    90  				Group:    r.APIGroup,
    91  				Version:  r.APIVersion,
    92  				Resource: r.Resource,
    93  			}
    94  			if restMapper == nil {
    95  				restMapper = meta.NewDefaultRESTMapper([]schema.GroupVersion{schema.GroupVersion{
    96  					Group:   r.APIGroup,
    97  					Version: r.APIVersion,
    98  				}})
    99  			}
   100  
   101  			k, err := restMapper.KindFor(gvr)
   102  			if err != nil {
   103  				// break here in case resource doesn't exist in cache
   104  				log.Info("Cache miss, can not find in rest mapper", "GVR", gvr)
   105  				break
   106  			}
   107  
   108  			var m marshaler
   109  
   110  			log.V(2).Info("Get resource in our cache", "r", r)
   111  			if r.Verb == "list" {
   112  				listOptions := &metav1.ListOptions{}
   113  				if err := metainternalversion.ParameterCodec.DecodeParameters(req.URL.Query(), metav1.SchemeGroupVersion, listOptions); err != nil {
   114  					log.Error(err, "Unable to decode list options from request")
   115  					break
   116  				}
   117  				lo := client.InNamespace(r.Namespace)
   118  				if err := lo.SetLabelSelector(listOptions.LabelSelector); err != nil {
   119  					log.Error(err, "Unable to set label selectors for the client")
   120  					break
   121  				}
   122  				if listOptions.FieldSelector != "" {
   123  					if err := lo.SetFieldSelector(listOptions.FieldSelector); err != nil {
   124  						log.Error(err, "Unable to set field selectors for the client")
   125  						break
   126  					}
   127  				}
   128  				k.Kind = k.Kind + "List"
   129  				un := unstructured.UnstructuredList{}
   130  				un.SetGroupVersionKind(k)
   131  				err = informerCache.List(context.Background(), lo, &un)
   132  				if err != nil {
   133  					// break here in case resource doesn't exist in cache but exists on APIserver
   134  					// This is very unlikely but provides user with expected 404
   135  					log.Info(fmt.Sprintf("cache miss: %v err-%v", k, err))
   136  					break
   137  				}
   138  				m = &un
   139  			} else {
   140  				un := &unstructured.Unstructured{}
   141  				un.SetGroupVersionKind(k)
   142  				obj := client.ObjectKey{Namespace: r.Namespace, Name: r.Name}
   143  				err = informerCache.Get(context.Background(), obj, un)
   144  				if err != nil {
   145  					// break here in case resource doesn't exist in cache but exists on APIserver
   146  					// This is very unlikely but provides user with expected 404
   147  					log.Info(fmt.Sprintf("Cache miss: %v, %v", k, obj))
   148  					break
   149  				}
   150  				m = un
   151  				// Once we get the resource, we are going to attempt to recover the dependent watches here,
   152  				// This will happen in the background, and log errors.
   153  				if injectOwnerRef {
   154  					go recoverDependentWatches(req, un, cMap, restMapper)
   155  				}
   156  			}
   157  
   158  			i := bytes.Buffer{}
   159  			resp, err := m.MarshalJSON()
   160  			if err != nil {
   161  				// return will give a 500
   162  				log.Error(err, "Failed to marshal data")
   163  				http.Error(w, "", http.StatusInternalServerError)
   164  				return
   165  			}
   166  
   167  			// Set Content-Type header
   168  			w.Header().Set("Content-Type", "application/json")
   169  			// Set X-Cache header to signal that response is served from Cache
   170  			w.Header().Set("X-Cache", "HIT")
   171  			if err := json.Indent(&i, resp, "", "  "); err != nil {
   172  				log.Error(err, "Failed to indent json")
   173  			}
   174  			_, err = w.Write(i.Bytes())
   175  			if err != nil {
   176  				log.Error(err, "Failed to write response")
   177  				http.Error(w, "", http.StatusInternalServerError)
   178  				return
   179  			}
   180  
   181  			// Return so that request isn't passed along to APIserver
   182  			return
   183  		}
   184  		h.ServeHTTP(w, req)
   185  	})
   186  }
   187  
   188  func recoverDependentWatches(req *http.Request, un *unstructured.Unstructured, cMap *controllermap.ControllerMap, restMapper meta.RESTMapper) {
   189  	ownerRef, err := getRequestOwnerRef(req)
   190  	if err != nil {
   191  		log.Error(err, "Could not get ownerRef from proxy")
   192  		return
   193  	}
   194  
   195  	for _, oRef := range un.GetOwnerReferences() {
   196  		if oRef.APIVersion == ownerRef.APIVersion && oRef.Kind == ownerRef.Kind {
   197  			err := addWatchToController(ownerRef, cMap, un, restMapper, true)
   198  			if err != nil {
   199  				log.Error(err, "Could not recover dependent resource watch", "owner", ownerRef)
   200  				return
   201  			}
   202  		}
   203  	}
   204  	if typeString, ok := un.GetAnnotations()[osdkHandler.TypeAnnotation]; ok {
   205  		ownerGV, err := schema.ParseGroupVersion(ownerRef.APIVersion)
   206  		if err != nil {
   207  			log.Error(err, "Could not get ownerRef from proxy")
   208  			return
   209  		}
   210  		if typeString == fmt.Sprintf("%v.%v", ownerRef.Kind, ownerGV.Group) {
   211  			err := addWatchToController(ownerRef, cMap, un, restMapper, false)
   212  			if err != nil {
   213  				log.Error(err, "Could not recover dependent resource watch", "owner", ownerRef)
   214  				return
   215  			}
   216  		}
   217  	}
   218  }
   219  
   220  // InjectOwnerReferenceHandler will handle proxied requests and inject the
   221  // owner reference found in the authorization header. The Authorization is
   222  // then deleted so that the proxy can re-set with the correct authorization.
   223  func InjectOwnerReferenceHandler(h http.Handler, cMap *controllermap.ControllerMap, restMapper meta.RESTMapper, watchedNamespaces map[string]interface{}) http.Handler {
   224  	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   225  		switch req.Method {
   226  		case http.MethodPost:
   227  			dump, _ := httputil.DumpRequest(req, false)
   228  			log.V(1).Info("Dumping request", "RequestDump", string(dump))
   229  			rf := k8sRequest.RequestInfoFactory{APIPrefixes: sets.NewString("api", "apis"), GrouplessAPIPrefixes: sets.NewString("api")}
   230  			r, err := rf.NewRequestInfo(req)
   231  			if err != nil {
   232  				m := "Could not convert request"
   233  				log.Error(err, m)
   234  				http.Error(w, m, http.StatusBadRequest)
   235  				return
   236  			}
   237  			if r.Subresource != "" {
   238  				// Don't inject owner ref if we are POSTing to a subresource
   239  				break
   240  			}
   241  			log.Info("Injecting owner reference")
   242  			owner, err := getRequestOwnerRef(req)
   243  			if err != nil {
   244  				m := "Could not get owner reference"
   245  				log.Error(err, m)
   246  				http.Error(w, m, http.StatusInternalServerError)
   247  				return
   248  			}
   249  
   250  			body, err := ioutil.ReadAll(req.Body)
   251  			if err != nil {
   252  				m := "Could not read request body"
   253  				log.Error(err, m)
   254  				http.Error(w, m, http.StatusInternalServerError)
   255  				return
   256  			}
   257  			data := &unstructured.Unstructured{}
   258  			err = json.Unmarshal(body, data)
   259  			if err != nil {
   260  				m := "Could not deserialize request body"
   261  				log.Error(err, m)
   262  				http.Error(w, m, http.StatusBadRequest)
   263  				return
   264  			}
   265  
   266  			addOwnerRef, err := shouldAddOwnerRef(data, owner, restMapper)
   267  			if err != nil {
   268  				m := "Could not determine if we should add owner ref"
   269  				log.Error(err, m)
   270  				http.Error(w, m, http.StatusBadRequest)
   271  				return
   272  			}
   273  			if addOwnerRef {
   274  				data.SetOwnerReferences(append(data.GetOwnerReferences(), owner.OwnerReference))
   275  			} else {
   276  				ownerGV, err := schema.ParseGroupVersion(owner.APIVersion)
   277  				if err != nil {
   278  					m := fmt.Sprintf("could not get broup version for: %v", owner)
   279  					log.Error(err, m)
   280  					http.Error(w, m, http.StatusBadRequest)
   281  					return
   282  				}
   283  				a := data.GetAnnotations()
   284  				if a == nil {
   285  					a = map[string]string{}
   286  				}
   287  				a[osdkHandler.NamespacedNameAnnotation] = strings.Join([]string{owner.Namespace, owner.Name}, "/")
   288  				a[osdkHandler.TypeAnnotation] = fmt.Sprintf("%v.%v", owner.Kind, ownerGV.Group)
   289  
   290  				data.SetAnnotations(a)
   291  			}
   292  			newBody, err := json.Marshal(data.Object)
   293  			if err != nil {
   294  				m := "Could not serialize body"
   295  				log.Error(err, m)
   296  				http.Error(w, m, http.StatusInternalServerError)
   297  				return
   298  			}
   299  			log.V(1).Info("Serialized body", "Body", string(newBody))
   300  			req.Body = ioutil.NopCloser(bytes.NewBuffer(newBody))
   301  			req.ContentLength = int64(len(newBody))
   302  
   303  			// add watch for resource
   304  			// check if resource doesn't exist in watched namespaces
   305  			// if watchedNamespaces[""] exists then we are watching all namespaces
   306  			// and want to continue
   307  			// This is making sure we are not attempting to watch a resource outside of the
   308  			// namespaces that the cache can watch.
   309  			_, allNsPresent := watchedNamespaces[metav1.NamespaceAll]
   310  			_, reqNsPresent := watchedNamespaces[r.Namespace]
   311  			if allNsPresent || reqNsPresent {
   312  				err = addWatchToController(owner, cMap, data, restMapper, addOwnerRef)
   313  				if err != nil {
   314  					m := "could not add watch to controller"
   315  					log.Error(err, m)
   316  					http.Error(w, m, http.StatusInternalServerError)
   317  					return
   318  				}
   319  			}
   320  		}
   321  		h.ServeHTTP(w, req)
   322  	})
   323  }
   324  
   325  func removeAuthorizationHeader(h http.Handler) http.Handler {
   326  	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   327  		req.Header.Del("Authorization")
   328  		h.ServeHTTP(w, req)
   329  	})
   330  }
   331  
   332  func shouldAddOwnerRef(data *unstructured.Unstructured, owner kubeconfig.NamespacedOwnerReference, restMapper meta.RESTMapper) (bool, error) {
   333  	dataMapping, err := restMapper.RESTMapping(data.GroupVersionKind().GroupKind(), data.GroupVersionKind().Version)
   334  	if err != nil {
   335  		m := fmt.Sprintf("Could not get rest mapping for: %v", data.GroupVersionKind())
   336  		log.Error(err, m)
   337  		return false, err
   338  
   339  	}
   340  	// We need to determine whether or not the owner is a cluster-scoped
   341  	// resource because enqueue based on an owner reference does not work if
   342  	// a namespaced resource owns a cluster-scoped resource
   343  	ownerGV, err := schema.ParseGroupVersion(owner.APIVersion)
   344  	if err != nil {
   345  		m := fmt.Sprintf("could not get group version for: %v", owner)
   346  		log.Error(err, m)
   347  		return false, err
   348  	}
   349  	ownerMapping, err := restMapper.RESTMapping(schema.GroupKind{Kind: owner.Kind, Group: ownerGV.Group}, ownerGV.Version)
   350  	if err != nil {
   351  		m := fmt.Sprintf("could not get rest mapping for: %v", owner)
   352  		log.Error(err, m)
   353  		return false, err
   354  	}
   355  
   356  	dataNamespaceScoped := dataMapping.Scope.Name() != meta.RESTScopeNameRoot
   357  	ownerNamespaceScoped := ownerMapping.Scope.Name() != meta.RESTScopeNameRoot
   358  
   359  	if dataNamespaceScoped && ownerNamespaceScoped && data.GetNamespace() == owner.Namespace {
   360  		return true, nil
   361  	}
   362  	return false, nil
   363  }
   364  
   365  // RequestLogHandler - log the requests that come through the proxy.
   366  func RequestLogHandler(h http.Handler) http.Handler {
   367  	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   368  		// read body
   369  		body, err := ioutil.ReadAll(req.Body)
   370  		if err != nil {
   371  			log.Error(err, "Could not read request body")
   372  		}
   373  		// fix body
   374  		req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
   375  		log.Info("Request Info", "method", req.Method, "uri", req.RequestURI, "body", string(body))
   376  		// Removing the authorization so that the proxy can set the correct authorization.
   377  		req.Header.Del("Authorization")
   378  		h.ServeHTTP(w, req)
   379  	})
   380  }
   381  
   382  // HandlerChain will be used for users to pass defined handlers to the proxy.
   383  // The hander chain will be run after InjectingOwnerReference if it is added
   384  // and before the proxy handler.
   385  type HandlerChain func(http.Handler) http.Handler
   386  
   387  // Options will be used by the user to specify the desired details
   388  // for the proxy.
   389  type Options struct {
   390  	Address           string
   391  	Port              int
   392  	Handler           HandlerChain
   393  	OwnerInjection    bool
   394  	LogRequests       bool
   395  	KubeConfig        *rest.Config
   396  	Cache             cache.Cache
   397  	RESTMapper        meta.RESTMapper
   398  	ControllerMap     *controllermap.ControllerMap
   399  	WatchedNamespaces []string
   400  	DisableCache      bool
   401  }
   402  
   403  // Run will start a proxy server in a go routine that returns on the error
   404  // channel if something is not correct on startup. Run will not return until
   405  // the network socket is listening.
   406  func Run(done chan error, o Options) error {
   407  	server, err := newServer("/", o.KubeConfig)
   408  	if err != nil {
   409  		return err
   410  	}
   411  	if o.Handler != nil {
   412  		server.Handler = o.Handler(server.Handler)
   413  	}
   414  	if o.ControllerMap == nil {
   415  		return fmt.Errorf("failed to get controller map from options")
   416  	}
   417  	if o.WatchedNamespaces == nil {
   418  		return fmt.Errorf("failed to get list of watched namespaces from options")
   419  	}
   420  
   421  	watchedNamespaceMap := make(map[string]interface{})
   422  	// Convert string list to map
   423  	for _, ns := range o.WatchedNamespaces {
   424  		watchedNamespaceMap[ns] = nil
   425  	}
   426  
   427  	if o.Cache == nil && !o.DisableCache {
   428  		// Need to initialize cache since we don't have one
   429  		log.Info("Initializing and starting informer cache...")
   430  		informerCache, err := cache.New(o.KubeConfig, cache.Options{})
   431  		if err != nil {
   432  			return err
   433  		}
   434  		stop := make(chan struct{})
   435  		go func() {
   436  			if err := informerCache.Start(stop); err != nil {
   437  				log.Error(err, "Failed to start informer cache")
   438  			}
   439  			defer close(stop)
   440  		}()
   441  		log.Info("Waiting for cache to sync...")
   442  		synced := informerCache.WaitForCacheSync(stop)
   443  		if !synced {
   444  			return fmt.Errorf("failed to sync cache")
   445  		}
   446  		log.Info("Cache sync was successful")
   447  		o.Cache = informerCache
   448  	}
   449  
   450  	server.Handler = removeAuthorizationHeader(server.Handler)
   451  
   452  	if o.OwnerInjection {
   453  		server.Handler = InjectOwnerReferenceHandler(server.Handler, o.ControllerMap, o.RESTMapper, watchedNamespaceMap)
   454  	} else {
   455  		log.Info("Warning: injection of owner references and dependent watches is turned off")
   456  	}
   457  	if o.LogRequests {
   458  		server.Handler = RequestLogHandler(server.Handler)
   459  	}
   460  	if !o.DisableCache {
   461  		server.Handler = CacheResponseHandler(server.Handler, o.Cache, o.RESTMapper, watchedNamespaceMap, o.ControllerMap, o.OwnerInjection)
   462  	}
   463  
   464  	l, err := server.Listen(o.Address, o.Port)
   465  	if err != nil {
   466  		return err
   467  	}
   468  	go func() {
   469  		log.Info("Starting to serve", "Address", l.Addr().String())
   470  		done <- server.ServeOnListener(l)
   471  	}()
   472  	return nil
   473  }
   474  
   475  func addWatchToController(owner kubeconfig.NamespacedOwnerReference, cMap *controllermap.ControllerMap, resource *unstructured.Unstructured, restMapper meta.RESTMapper, useOwnerRef bool) error {
   476  	dataMapping, err := restMapper.RESTMapping(resource.GroupVersionKind().GroupKind(), resource.GroupVersionKind().Version)
   477  	if err != nil {
   478  		m := fmt.Sprintf("Could not get rest mapping for: %v", resource.GroupVersionKind())
   479  		log.Error(err, m)
   480  		return err
   481  
   482  	}
   483  	ownerGV, err := schema.ParseGroupVersion(owner.APIVersion)
   484  	if err != nil {
   485  		m := fmt.Sprintf("could not get broup version for: %v", owner)
   486  		log.Error(err, m)
   487  		return err
   488  	}
   489  	ownerMapping, err := restMapper.RESTMapping(schema.GroupKind{Kind: owner.Kind, Group: ownerGV.Group}, ownerGV.Version)
   490  	if err != nil {
   491  		m := fmt.Sprintf("could not get rest mapping for: %v", owner)
   492  		log.Error(err, m)
   493  		return err
   494  	}
   495  
   496  	dataNamespaceScoped := dataMapping.Scope.Name() != meta.RESTScopeNameRoot
   497  	contents, ok := cMap.Get(ownerMapping.GroupVersionKind)
   498  	if !ok {
   499  		return errors.New("failed to find controller in map")
   500  	}
   501  	owMap := contents.OwnerWatchMap
   502  	awMap := contents.AnnotationWatchMap
   503  	u := &unstructured.Unstructured{}
   504  	u.SetGroupVersionKind(ownerMapping.GroupVersionKind)
   505  	// Add a watch to controller
   506  	if contents.WatchDependentResources {
   507  		// Store watch in map
   508  		// Use EnqueueRequestForOwner unless user has configured watching cluster scoped resources and we have to
   509  		switch {
   510  		case useOwnerRef:
   511  			_, exists := owMap.Get(resource.GroupVersionKind())
   512  			// If already watching resource no need to add a new watch
   513  			if exists {
   514  				return nil
   515  			}
   516  
   517  			owMap.Store(resource.GroupVersionKind())
   518  			log.Info("Watching child resource", "kind", resource.GroupVersionKind(), "enqueue_kind", u.GroupVersionKind())
   519  			// Store watch in map
   520  			err := contents.Controller.Watch(&source.Kind{Type: resource}, &handler.EnqueueRequestForOwner{OwnerType: u})
   521  			if err != nil {
   522  				return err
   523  			}
   524  		case (!useOwnerRef && dataNamespaceScoped) || contents.WatchClusterScopedResources:
   525  			_, exists := awMap.Get(resource.GroupVersionKind())
   526  			// If already watching resource no need to add a new watch
   527  			if exists {
   528  				return nil
   529  			}
   530  			awMap.Store(resource.GroupVersionKind())
   531  			typeString := fmt.Sprintf("%v.%v", owner.Kind, ownerGV.Group)
   532  			log.Info("Watching child resource", "kind", resource.GroupVersionKind(), "enqueue_annotation_type", typeString)
   533  			err = contents.Controller.Watch(&source.Kind{Type: resource}, &osdkHandler.EnqueueRequestForAnnotation{Type: typeString})
   534  			if err != nil {
   535  				return err
   536  			}
   537  		}
   538  	}
   539  	return nil
   540  }
   541  
   542  func getRequestOwnerRef(req *http.Request) (kubeconfig.NamespacedOwnerReference, error) {
   543  	owner := kubeconfig.NamespacedOwnerReference{}
   544  	user, _, ok := req.BasicAuth()
   545  	if !ok {
   546  		return owner, errors.New("basic auth header not found")
   547  	}
   548  	authString, err := base64.StdEncoding.DecodeString(user)
   549  	if err != nil {
   550  		m := "Could not base64 decode username"
   551  		log.Error(err, m)
   552  		return owner, err
   553  	}
   554  	// Set owner to NamespacedOwnerReference, which has metav1.OwnerReference
   555  	// as a subset along with the Namespace of the owner. Please see the
   556  	// kubeconfig.NamespacedOwnerReference type for more information. The
   557  	// namespace is required when creating the reconcile requests.
   558  	json.Unmarshal(authString, &owner)
   559  	if err := json.Unmarshal(authString, &owner); err != nil {
   560  		m := "Could not unmarshal auth string"
   561  		log.Error(err, m)
   562  		return owner, err
   563  	}
   564  	return owner, err
   565  }