k8s.io/apiserver@v0.31.1/pkg/endpoints/discovery/util.go (about)

     1  /*
     2  Copyright 2017 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 discovery
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io"
    24  
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  	"k8s.io/klog/v2"
    27  )
    28  
    29  const APIGroupPrefix = "/apis"
    30  
    31  func keepUnversioned(group string) bool {
    32  	return group == "" || group == "extensions"
    33  }
    34  
    35  // stripVersionEncoder strips APIVersion field from the encoding output. It's
    36  // used to keep the responses at the discovery endpoints backward compatible
    37  // with release-1.1, when the responses have empty APIVersion.
    38  type stripVersionEncoder struct {
    39  	encoder    runtime.Encoder
    40  	serializer runtime.Serializer
    41  	identifier runtime.Identifier
    42  }
    43  
    44  func newStripVersionEncoder(e runtime.Encoder, s runtime.Serializer) runtime.Encoder {
    45  	return stripVersionEncoder{
    46  		encoder:    e,
    47  		serializer: s,
    48  		identifier: identifier(e),
    49  	}
    50  }
    51  
    52  func identifier(e runtime.Encoder) runtime.Identifier {
    53  	result := map[string]string{
    54  		"name": "stripVersion",
    55  	}
    56  	if e != nil {
    57  		result["encoder"] = string(e.Identifier())
    58  	}
    59  	identifier, err := json.Marshal(result)
    60  	if err != nil {
    61  		klog.Fatalf("Failed marshaling identifier for stripVersionEncoder: %v", err)
    62  	}
    63  	return runtime.Identifier(identifier)
    64  }
    65  
    66  func (c stripVersionEncoder) Encode(obj runtime.Object, w io.Writer) error {
    67  	if co, ok := obj.(runtime.CacheableObject); ok {
    68  		return co.CacheEncode(c.Identifier(), c.doEncode, w)
    69  	}
    70  	return c.doEncode(obj, w)
    71  }
    72  
    73  func (c stripVersionEncoder) doEncode(obj runtime.Object, w io.Writer) error {
    74  	buf := bytes.NewBuffer([]byte{})
    75  	err := c.encoder.Encode(obj, buf)
    76  	if err != nil {
    77  		return err
    78  	}
    79  	roundTrippedObj, gvk, err := c.serializer.Decode(buf.Bytes(), nil, nil)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	gvk.Group = ""
    84  	gvk.Version = ""
    85  	roundTrippedObj.GetObjectKind().SetGroupVersionKind(*gvk)
    86  	return c.serializer.Encode(roundTrippedObj, w)
    87  }
    88  
    89  // Identifier implements runtime.Encoder interface.
    90  func (c stripVersionEncoder) Identifier() runtime.Identifier {
    91  	return c.identifier
    92  }
    93  
    94  // stripVersionNegotiatedSerializer will return stripVersionEncoder when
    95  // EncoderForVersion is called. See comments for stripVersionEncoder.
    96  type stripVersionNegotiatedSerializer struct {
    97  	runtime.NegotiatedSerializer
    98  }
    99  
   100  func (n stripVersionNegotiatedSerializer) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
   101  	serializer, ok := encoder.(runtime.Serializer)
   102  	if !ok {
   103  		// The stripVersionEncoder needs both an encoder and decoder, but is called from a context that doesn't have access to the
   104  		// decoder. We do a best effort cast here (since this code path is only for backwards compatibility) to get access to the caller's
   105  		// decoder.
   106  		panic(fmt.Sprintf("Unable to extract serializer from %#v", encoder))
   107  	}
   108  	versioned := n.NegotiatedSerializer.EncoderForVersion(encoder, gv)
   109  	return newStripVersionEncoder(versioned, serializer)
   110  }