...

Source file src/k8s.io/apimachinery/pkg/runtime/schema/group_version.go

Documentation: k8s.io/apimachinery/pkg/runtime/schema

     1  /*
     2  Copyright 2015 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 schema
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  )
    23  
    24  // ParseResourceArg takes the common style of string which may be either `resource.group.com` or `resource.version.group.com`
    25  // and parses it out into both possibilities.  This code takes no responsibility for knowing which representation was intended
    26  // but with a knowledge of all GroupVersions, calling code can take a very good guess.  If there are only two segments, then
    27  // `*GroupVersionResource` is nil.
    28  // `resource.group.com` -> `group=com, version=group, resource=resource` and `group=group.com, resource=resource`
    29  func ParseResourceArg(arg string) (*GroupVersionResource, GroupResource) {
    30  	var gvr *GroupVersionResource
    31  	if strings.Count(arg, ".") >= 2 {
    32  		s := strings.SplitN(arg, ".", 3)
    33  		gvr = &GroupVersionResource{Group: s[2], Version: s[1], Resource: s[0]}
    34  	}
    35  
    36  	return gvr, ParseGroupResource(arg)
    37  }
    38  
    39  // ParseKindArg takes the common style of string which may be either `Kind.group.com` or `Kind.version.group.com`
    40  // and parses it out into both possibilities. This code takes no responsibility for knowing which representation was intended
    41  // but with a knowledge of all GroupKinds, calling code can take a very good guess. If there are only two segments, then
    42  // `*GroupVersionKind` is nil.
    43  // `Kind.group.com` -> `group=com, version=group, kind=Kind` and `group=group.com, kind=Kind`
    44  func ParseKindArg(arg string) (*GroupVersionKind, GroupKind) {
    45  	var gvk *GroupVersionKind
    46  	if strings.Count(arg, ".") >= 2 {
    47  		s := strings.SplitN(arg, ".", 3)
    48  		gvk = &GroupVersionKind{Group: s[2], Version: s[1], Kind: s[0]}
    49  	}
    50  
    51  	return gvk, ParseGroupKind(arg)
    52  }
    53  
    54  // GroupResource specifies a Group and a Resource, but does not force a version.  This is useful for identifying
    55  // concepts during lookup stages without having partially valid types
    56  type GroupResource struct {
    57  	Group    string
    58  	Resource string
    59  }
    60  
    61  func (gr GroupResource) WithVersion(version string) GroupVersionResource {
    62  	return GroupVersionResource{Group: gr.Group, Version: version, Resource: gr.Resource}
    63  }
    64  
    65  func (gr GroupResource) Empty() bool {
    66  	return len(gr.Group) == 0 && len(gr.Resource) == 0
    67  }
    68  
    69  func (gr GroupResource) String() string {
    70  	if len(gr.Group) == 0 {
    71  		return gr.Resource
    72  	}
    73  	return gr.Resource + "." + gr.Group
    74  }
    75  
    76  func ParseGroupKind(gk string) GroupKind {
    77  	i := strings.Index(gk, ".")
    78  	if i == -1 {
    79  		return GroupKind{Kind: gk}
    80  	}
    81  
    82  	return GroupKind{Group: gk[i+1:], Kind: gk[:i]}
    83  }
    84  
    85  // ParseGroupResource turns "resource.group" string into a GroupResource struct.  Empty strings are allowed
    86  // for each field.
    87  func ParseGroupResource(gr string) GroupResource {
    88  	if i := strings.Index(gr, "."); i >= 0 {
    89  		return GroupResource{Group: gr[i+1:], Resource: gr[:i]}
    90  	}
    91  	return GroupResource{Resource: gr}
    92  }
    93  
    94  // GroupVersionResource unambiguously identifies a resource.  It doesn't anonymously include GroupVersion
    95  // to avoid automatic coercion.  It doesn't use a GroupVersion to avoid custom marshalling
    96  type GroupVersionResource struct {
    97  	Group    string
    98  	Version  string
    99  	Resource string
   100  }
   101  
   102  func (gvr GroupVersionResource) Empty() bool {
   103  	return len(gvr.Group) == 0 && len(gvr.Version) == 0 && len(gvr.Resource) == 0
   104  }
   105  
   106  func (gvr GroupVersionResource) GroupResource() GroupResource {
   107  	return GroupResource{Group: gvr.Group, Resource: gvr.Resource}
   108  }
   109  
   110  func (gvr GroupVersionResource) GroupVersion() GroupVersion {
   111  	return GroupVersion{Group: gvr.Group, Version: gvr.Version}
   112  }
   113  
   114  func (gvr GroupVersionResource) String() string {
   115  	return strings.Join([]string{gvr.Group, "/", gvr.Version, ", Resource=", gvr.Resource}, "")
   116  }
   117  
   118  // GroupKind specifies a Group and a Kind, but does not force a version.  This is useful for identifying
   119  // concepts during lookup stages without having partially valid types
   120  type GroupKind struct {
   121  	Group string
   122  	Kind  string
   123  }
   124  
   125  func (gk GroupKind) Empty() bool {
   126  	return len(gk.Group) == 0 && len(gk.Kind) == 0
   127  }
   128  
   129  func (gk GroupKind) WithVersion(version string) GroupVersionKind {
   130  	return GroupVersionKind{Group: gk.Group, Version: version, Kind: gk.Kind}
   131  }
   132  
   133  func (gk GroupKind) String() string {
   134  	if len(gk.Group) == 0 {
   135  		return gk.Kind
   136  	}
   137  	return gk.Kind + "." + gk.Group
   138  }
   139  
   140  // GroupVersionKind unambiguously identifies a kind.  It doesn't anonymously include GroupVersion
   141  // to avoid automatic coercion.  It doesn't use a GroupVersion to avoid custom marshalling
   142  type GroupVersionKind struct {
   143  	Group   string
   144  	Version string
   145  	Kind    string
   146  }
   147  
   148  // Empty returns true if group, version, and kind are empty
   149  func (gvk GroupVersionKind) Empty() bool {
   150  	return len(gvk.Group) == 0 && len(gvk.Version) == 0 && len(gvk.Kind) == 0
   151  }
   152  
   153  func (gvk GroupVersionKind) GroupKind() GroupKind {
   154  	return GroupKind{Group: gvk.Group, Kind: gvk.Kind}
   155  }
   156  
   157  func (gvk GroupVersionKind) GroupVersion() GroupVersion {
   158  	return GroupVersion{Group: gvk.Group, Version: gvk.Version}
   159  }
   160  
   161  func (gvk GroupVersionKind) String() string {
   162  	return gvk.Group + "/" + gvk.Version + ", Kind=" + gvk.Kind
   163  }
   164  
   165  // GroupVersion contains the "group" and the "version", which uniquely identifies the API.
   166  type GroupVersion struct {
   167  	Group   string
   168  	Version string
   169  }
   170  
   171  // Empty returns true if group and version are empty
   172  func (gv GroupVersion) Empty() bool {
   173  	return len(gv.Group) == 0 && len(gv.Version) == 0
   174  }
   175  
   176  // String puts "group" and "version" into a single "group/version" string. For the legacy v1
   177  // it returns "v1".
   178  func (gv GroupVersion) String() string {
   179  	if len(gv.Group) > 0 {
   180  		return gv.Group + "/" + gv.Version
   181  	}
   182  	return gv.Version
   183  }
   184  
   185  // Identifier implements runtime.GroupVersioner interface.
   186  func (gv GroupVersion) Identifier() string {
   187  	return gv.String()
   188  }
   189  
   190  // KindForGroupVersionKinds identifies the preferred GroupVersionKind out of a list. It returns ok false
   191  // if none of the options match the group. It prefers a match to group and version over just group.
   192  // TODO: Move GroupVersion to a package under pkg/runtime, since it's used by scheme.
   193  // TODO: Introduce an adapter type between GroupVersion and runtime.GroupVersioner, and use LegacyCodec(GroupVersion)
   194  // in fewer places.
   195  func (gv GroupVersion) KindForGroupVersionKinds(kinds []GroupVersionKind) (target GroupVersionKind, ok bool) {
   196  	for _, gvk := range kinds {
   197  		if gvk.Group == gv.Group && gvk.Version == gv.Version {
   198  			return gvk, true
   199  		}
   200  	}
   201  	for _, gvk := range kinds {
   202  		if gvk.Group == gv.Group {
   203  			return gv.WithKind(gvk.Kind), true
   204  		}
   205  	}
   206  	return GroupVersionKind{}, false
   207  }
   208  
   209  // ParseGroupVersion turns "group/version" string into a GroupVersion struct. It reports error
   210  // if it cannot parse the string.
   211  func ParseGroupVersion(gv string) (GroupVersion, error) {
   212  	// this can be the internal version for the legacy kube types
   213  	// TODO once we've cleared the last uses as strings, this special case should be removed.
   214  	if (len(gv) == 0) || (gv == "/") {
   215  		return GroupVersion{}, nil
   216  	}
   217  
   218  	switch strings.Count(gv, "/") {
   219  	case 0:
   220  		return GroupVersion{"", gv}, nil
   221  	case 1:
   222  		i := strings.Index(gv, "/")
   223  		return GroupVersion{gv[:i], gv[i+1:]}, nil
   224  	default:
   225  		return GroupVersion{}, fmt.Errorf("unexpected GroupVersion string: %v", gv)
   226  	}
   227  }
   228  
   229  // WithKind creates a GroupVersionKind based on the method receiver's GroupVersion and the passed Kind.
   230  func (gv GroupVersion) WithKind(kind string) GroupVersionKind {
   231  	return GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
   232  }
   233  
   234  // WithResource creates a GroupVersionResource based on the method receiver's GroupVersion and the passed Resource.
   235  func (gv GroupVersion) WithResource(resource string) GroupVersionResource {
   236  	return GroupVersionResource{Group: gv.Group, Version: gv.Version, Resource: resource}
   237  }
   238  
   239  // GroupVersions can be used to represent a set of desired group versions.
   240  // TODO: Move GroupVersions to a package under pkg/runtime, since it's used by scheme.
   241  // TODO: Introduce an adapter type between GroupVersions and runtime.GroupVersioner, and use LegacyCodec(GroupVersion)
   242  // in fewer places.
   243  type GroupVersions []GroupVersion
   244  
   245  // Identifier implements runtime.GroupVersioner interface.
   246  func (gvs GroupVersions) Identifier() string {
   247  	groupVersions := make([]string, 0, len(gvs))
   248  	for i := range gvs {
   249  		groupVersions = append(groupVersions, gvs[i].String())
   250  	}
   251  	return fmt.Sprintf("[%s]", strings.Join(groupVersions, ","))
   252  }
   253  
   254  // KindForGroupVersionKinds identifies the preferred GroupVersionKind out of a list. It returns ok false
   255  // if none of the options match the group.
   256  func (gvs GroupVersions) KindForGroupVersionKinds(kinds []GroupVersionKind) (GroupVersionKind, bool) {
   257  	var targets []GroupVersionKind
   258  	for _, gv := range gvs {
   259  		target, ok := gv.KindForGroupVersionKinds(kinds)
   260  		if !ok {
   261  			continue
   262  		}
   263  		targets = append(targets, target)
   264  	}
   265  	if len(targets) == 1 {
   266  		return targets[0], true
   267  	}
   268  	if len(targets) > 1 {
   269  		return bestMatch(kinds, targets), true
   270  	}
   271  	return GroupVersionKind{}, false
   272  }
   273  
   274  // bestMatch tries to pick best matching GroupVersionKind and falls back to the first
   275  // found if no exact match exists.
   276  func bestMatch(kinds []GroupVersionKind, targets []GroupVersionKind) GroupVersionKind {
   277  	for _, gvk := range targets {
   278  		for _, k := range kinds {
   279  			if k == gvk {
   280  				return k
   281  			}
   282  		}
   283  	}
   284  	return targets[0]
   285  }
   286  
   287  // ToAPIVersionAndKind is a convenience method for satisfying runtime.Object on types that
   288  // do not use TypeMeta.
   289  func (gvk GroupVersionKind) ToAPIVersionAndKind() (string, string) {
   290  	if gvk.Empty() {
   291  		return "", ""
   292  	}
   293  	return gvk.GroupVersion().String(), gvk.Kind
   294  }
   295  
   296  // FromAPIVersionAndKind returns a GVK representing the provided fields for types that
   297  // do not use TypeMeta. This method exists to support test types and legacy serializations
   298  // that have a distinct group and kind.
   299  // TODO: further reduce usage of this method.
   300  func FromAPIVersionAndKind(apiVersion, kind string) GroupVersionKind {
   301  	if gv, err := ParseGroupVersion(apiVersion); err == nil {
   302  		return GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: kind}
   303  	}
   304  	return GroupVersionKind{Kind: kind}
   305  }
   306  

View as plain text