...

Source file src/k8s.io/cli-runtime/pkg/resource/mapper.go

Documentation: k8s.io/cli-runtime/pkg/resource

     1  /*
     2  Copyright 2014 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 resource
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  
    23  	"k8s.io/apimachinery/pkg/api/meta"
    24  	"k8s.io/apimachinery/pkg/runtime"
    25  	"k8s.io/apimachinery/pkg/runtime/schema"
    26  )
    27  
    28  // Mapper is a convenience struct for holding references to the interfaces
    29  // needed to create Info for arbitrary objects.
    30  type mapper struct {
    31  	// localFn indicates the call can't make server requests
    32  	localFn func() bool
    33  
    34  	restMapperFn RESTMapperFunc
    35  	clientFn     func(version schema.GroupVersion) (RESTClient, error)
    36  	decoder      runtime.Decoder
    37  }
    38  
    39  // InfoForData creates an Info object for the given data. An error is returned
    40  // if any of the decoding or client lookup steps fail. Name and namespace will be
    41  // set into Info if the mapping's MetadataAccessor can retrieve them.
    42  func (m *mapper) infoForData(data []byte, source string) (*Info, error) {
    43  	obj, gvk, err := m.decoder.Decode(data, nil, nil)
    44  	if err != nil {
    45  		return nil, fmt.Errorf("unable to decode %q: %v", source, err)
    46  	}
    47  
    48  	name, _ := metadataAccessor.Name(obj)
    49  	namespace, _ := metadataAccessor.Namespace(obj)
    50  	resourceVersion, _ := metadataAccessor.ResourceVersion(obj)
    51  
    52  	ret := &Info{
    53  		Source:          source,
    54  		Namespace:       namespace,
    55  		Name:            name,
    56  		ResourceVersion: resourceVersion,
    57  
    58  		Object: obj,
    59  	}
    60  
    61  	if m.localFn == nil || !m.localFn() {
    62  		restMapper, err := m.restMapperFn()
    63  		if err != nil {
    64  			return nil, err
    65  		}
    66  		mapping, err := restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
    67  		if err != nil {
    68  			if _, ok := err.(*meta.NoKindMatchError); ok {
    69  				return nil, fmt.Errorf("resource mapping not found for name: %q namespace: %q from %q: %w\nensure CRDs are installed first",
    70  					name, namespace, source, err)
    71  			}
    72  			return nil, fmt.Errorf("unable to recognize %q: %v", source, err)
    73  		}
    74  		ret.Mapping = mapping
    75  
    76  		client, err := m.clientFn(gvk.GroupVersion())
    77  		if err != nil {
    78  			return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
    79  		}
    80  		ret.Client = client
    81  	}
    82  
    83  	return ret, nil
    84  }
    85  
    86  // InfoForObject creates an Info object for the given Object. An error is returned
    87  // if the object cannot be introspected. Name and namespace will be set into Info
    88  // if the mapping's MetadataAccessor can retrieve them.
    89  func (m *mapper) infoForObject(obj runtime.Object, typer runtime.ObjectTyper, preferredGVKs []schema.GroupVersionKind) (*Info, error) {
    90  	groupVersionKinds, _, err := typer.ObjectKinds(obj)
    91  	if err != nil {
    92  		return nil, fmt.Errorf("unable to get type info from the object %q: %v", reflect.TypeOf(obj), err)
    93  	}
    94  
    95  	gvk := groupVersionKinds[0]
    96  	if len(groupVersionKinds) > 1 && len(preferredGVKs) > 0 {
    97  		gvk = preferredObjectKind(groupVersionKinds, preferredGVKs)
    98  	}
    99  
   100  	name, _ := metadataAccessor.Name(obj)
   101  	namespace, _ := metadataAccessor.Namespace(obj)
   102  	resourceVersion, _ := metadataAccessor.ResourceVersion(obj)
   103  	ret := &Info{
   104  		Namespace:       namespace,
   105  		Name:            name,
   106  		ResourceVersion: resourceVersion,
   107  
   108  		Object: obj,
   109  	}
   110  
   111  	if m.localFn == nil || !m.localFn() {
   112  		restMapper, err := m.restMapperFn()
   113  		if err != nil {
   114  			return nil, err
   115  		}
   116  		mapping, err := restMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
   117  		if err != nil {
   118  			return nil, fmt.Errorf("unable to recognize %v", err)
   119  		}
   120  		ret.Mapping = mapping
   121  
   122  		client, err := m.clientFn(gvk.GroupVersion())
   123  		if err != nil {
   124  			return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
   125  		}
   126  		ret.Client = client
   127  	}
   128  
   129  	return ret, nil
   130  }
   131  
   132  // preferredObjectKind picks the possibility that most closely matches the priority list in this order:
   133  // GroupVersionKind matches (exact match)
   134  // GroupKind matches
   135  // Group matches
   136  func preferredObjectKind(possibilities []schema.GroupVersionKind, preferences []schema.GroupVersionKind) schema.GroupVersionKind {
   137  	// Exact match
   138  	for _, priority := range preferences {
   139  		for _, possibility := range possibilities {
   140  			if possibility == priority {
   141  				return possibility
   142  			}
   143  		}
   144  	}
   145  
   146  	// GroupKind match
   147  	for _, priority := range preferences {
   148  		for _, possibility := range possibilities {
   149  			if possibility.GroupKind() == priority.GroupKind() {
   150  				return possibility
   151  			}
   152  		}
   153  	}
   154  
   155  	// Group match
   156  	for _, priority := range preferences {
   157  		for _, possibility := range possibilities {
   158  			if possibility.Group == priority.Group {
   159  				return possibility
   160  			}
   161  		}
   162  	}
   163  
   164  	// Just pick the first
   165  	return possibilities[0]
   166  }
   167  

View as plain text