Skip to content

Commit

Permalink
Merge pull request #1892 from p0lyn0mial/manifest-client-patch-rsp
Browse files Browse the repository at this point in the history
API-1835: manifestclient: set GVK on patch response
  • Loading branch information
openshift-merge-bot[bot] authored Nov 27, 2024
2 parents 3c59352 + c967cd9 commit 64d8d9e
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 153 deletions.
150 changes: 150 additions & 0 deletions pkg/manifestclient/discovery_reader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package manifestclient

import (
"embed"
"errors"
"fmt"
"io/fs"
"path/filepath"
"sync"

apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/json"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
"sigs.k8s.io/yaml"
)

type kindData struct {
kind schema.GroupVersionKind
listKind schema.GroupVersionKind
err error
}

func newDiscoveryReader(content fs.FS) *discoveryReader {
return &discoveryReader{
sourceFS: content,
kindForResource: make(map[schema.GroupVersionResource]kindData),
}
}

type discoveryReader struct {
kindForResource map[schema.GroupVersionResource]kindData

sourceFS fs.FS
lock sync.RWMutex
}

func (dr *discoveryReader) getKindForResource(gvr schema.GroupVersionResource) (kindData, error) {
dr.lock.RLock()
kindForGVR, ok := dr.kindForResource[gvr]
if ok {
defer dr.lock.RUnlock()
return kindForGVR, kindForGVR.err
}
dr.lock.RUnlock()

dr.lock.Lock()
defer dr.lock.Unlock()

kindForGVR, ok = dr.kindForResource[gvr]
if ok {
return kindForGVR, kindForGVR.err
}

discoveryPath := "/apis"
if len(gvr.Group) == 0 {
discoveryPath = "/api"
}
discoveryBytes, err := dr.getGroupResourceDiscovery(&apirequest.RequestInfo{Path: discoveryPath})
if err != nil {
kindForGVR.err = fmt.Errorf("error reading discovery: %w", err)
dr.kindForResource[gvr] = kindForGVR
return kindForGVR, kindForGVR.err
}

discoveryInfo := &apidiscoveryv2.APIGroupDiscoveryList{}
if err := json.Unmarshal(discoveryBytes, discoveryInfo); err != nil {
kindForGVR.err = fmt.Errorf("error unmarshalling discovery: %w", err)
dr.kindForResource[gvr] = kindForGVR
return kindForGVR, kindForGVR.err
}

kindForGVR.err = fmt.Errorf("did not find kind for %v\n", gvr)
for _, groupInfo := range discoveryInfo.Items {
if groupInfo.Name != gvr.Group {
continue
}
for _, versionInfo := range groupInfo.Versions {
if versionInfo.Version != gvr.Version {
continue
}
for _, resourceInfo := range versionInfo.Resources {
if resourceInfo.Resource != gvr.Resource {
continue
}
if resourceInfo.ResponseKind == nil {
continue
}
kindForGVR.kind = schema.GroupVersionKind{
Group: gvr.Group,
Version: gvr.Version,
Kind: resourceInfo.ResponseKind.Kind,
}
if len(resourceInfo.ResponseKind.Group) > 0 {
kindForGVR.kind.Group = resourceInfo.ResponseKind.Group
}
if len(resourceInfo.ResponseKind.Version) > 0 {
kindForGVR.kind.Version = resourceInfo.ResponseKind.Version
}
kindForGVR.listKind = schema.GroupVersionKind{
Group: kindForGVR.kind.Group,
Version: kindForGVR.kind.Version,
Kind: resourceInfo.ResponseKind.Kind + "List",
}
kindForGVR.err = nil
dr.kindForResource[gvr] = kindForGVR
return kindForGVR, kindForGVR.err
}
}
}

dr.kindForResource[gvr] = kindForGVR
return kindForGVR, kindForGVR.err
}

func (dr *discoveryReader) getGroupResourceDiscovery(requestInfo *apirequest.RequestInfo) ([]byte, error) {
switch {
case requestInfo.Path == "/api":
return dr.getAggregatedDiscoveryForURL("aggregated-discovery-api.yaml", requestInfo.Path)
case requestInfo.Path == "/apis":
return dr.getAggregatedDiscoveryForURL("aggregated-discovery-apis.yaml", requestInfo.Path)
default:
// TODO can probably do better
return nil, fmt.Errorf("unsupported discovery path: %q", requestInfo.Path)
}
}

func (dr *discoveryReader) getAggregatedDiscoveryForURL(filename, url string) ([]byte, error) {
discoveryBytes, err := fs.ReadFile(dr.sourceFS, filename)
if errors.Is(err, fs.ErrNotExist) {
discoveryBytes, err = fs.ReadFile(defaultDiscovery, filepath.Join("default-discovery", filename))
}
if err != nil {
return nil, fmt.Errorf("error reading discovery: %w", err)
}

apiMap := map[string]interface{}{}
if err := yaml.Unmarshal(discoveryBytes, &apiMap); err != nil {
return nil, fmt.Errorf("discovery %q unmarshal failed: %w", url, err)
}
apiJSON, err := json.Marshal(apiMap)
if err != nil {
return nil, fmt.Errorf("discovery %q marshal failed: %w", url, err)
}

return apiJSON, err
}

//go:embed default-discovery
var defaultDiscovery embed.FS
46 changes: 0 additions & 46 deletions pkg/manifestclient/group_resource_discovery.go

This file was deleted.

2 changes: 1 addition & 1 deletion pkg/manifestclient/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (mrt *manifestRoundTripper) listAll(requestInfo *apirequest.RequestInfo) ([
Resource: requestInfo.Resource,
}

kind, err := mrt.getKindForResource(gvr)
kind, err := mrt.discoveryReader.getKindForResource(gvr)
if err != nil {
return nil, fmt.Errorf("unable to determine list kind: %w", err)
}
Expand Down
101 changes: 4 additions & 97 deletions pkg/manifestclient/read_roundtripper.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,14 @@ package manifestclient

import (
"bytes"
"embed"
"fmt"
"io"
"io/fs"
"net/http"
"strconv"
"strings"
"sync"
"time"

apidiscoveryv2 "k8s.io/api/apidiscovery/v2"
"k8s.io/apimachinery/pkg/util/json"

apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime/schema"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
Expand All @@ -36,23 +31,16 @@ type manifestRoundTripper struct {
// requestInfoResolver is the same type constructed the same way as the kube-apiserver
requestInfoResolver *apirequest.RequestInfoFactory

lock sync.RWMutex
kindForResource map[schema.GroupVersionResource]kindData
}

type kindData struct {
kind schema.GroupVersionKind
listKind schema.GroupVersionKind
err error
discoveryReader *discoveryReader
}

func newReadRoundTripper(content fs.FS) *manifestRoundTripper {
func newReadRoundTripper(content fs.FS, discoveryRoundTripper *discoveryReader) *manifestRoundTripper {
return &manifestRoundTripper{
sourceFS: content,
requestInfoResolver: server.NewRequestInfoResolver(&server.Config{
LegacyAPIGroupPrefixes: sets.NewString(server.DefaultLegacyAPIPrefix),
}),
kindForResource: make(map[schema.GroupVersionResource]kindData),
discoveryReader: discoveryRoundTripper,
}
}

Expand Down Expand Up @@ -82,7 +70,7 @@ func (mrt *manifestRoundTripper) RoundTrip(req *http.Request) (*http.Response, e
switch requestInfo.Verb {
case "get":
if isDiscovery {
returnBody, returnErr = mrt.getGroupResourceDiscovery(requestInfo)
returnBody, returnErr = mrt.discoveryReader.getGroupResourceDiscovery(requestInfo)
} else {
// TODO handle label and field selectors because single item lists are GETs
returnBody, returnErr = mrt.get(requestInfo)
Expand Down Expand Up @@ -168,84 +156,3 @@ func isServerGroupResourceDiscovery(path string) bool {
}
return parts[0] == "" && parts[1] == "apis"
}

//go:embed default-discovery
var defaultDiscovery embed.FS

func (mrt *manifestRoundTripper) getKindForResource(gvr schema.GroupVersionResource) (kindData, error) {
mrt.lock.RLock()
kindForGVR, ok := mrt.kindForResource[gvr]
if ok {
defer mrt.lock.RUnlock()
return kindForGVR, kindForGVR.err
}
mrt.lock.RUnlock()

mrt.lock.Lock()
defer mrt.lock.Unlock()

kindForGVR, ok = mrt.kindForResource[gvr]
if ok {
return kindForGVR, kindForGVR.err
}

discoveryPath := "/apis"
if len(gvr.Group) == 0 {
discoveryPath = "/api"
}
discoveryBytes, err := mrt.getGroupResourceDiscovery(&apirequest.RequestInfo{Path: discoveryPath})
if err != nil {
kindForGVR.err = fmt.Errorf("error reading discovery: %w", err)
mrt.kindForResource[gvr] = kindForGVR
return kindForGVR, kindForGVR.err
}

discoveryInfo := &apidiscoveryv2.APIGroupDiscoveryList{}
if err := json.Unmarshal(discoveryBytes, discoveryInfo); err != nil {
kindForGVR.err = fmt.Errorf("error unmarshalling discovery: %w", err)
mrt.kindForResource[gvr] = kindForGVR
return kindForGVR, kindForGVR.err
}

kindForGVR.err = fmt.Errorf("did not find kind for %v\n", gvr)
for _, groupInfo := range discoveryInfo.Items {
if groupInfo.Name != gvr.Group {
continue
}
for _, versionInfo := range groupInfo.Versions {
if versionInfo.Version != gvr.Version {
continue
}
for _, resourceInfo := range versionInfo.Resources {
if resourceInfo.Resource != gvr.Resource {
continue
}
if resourceInfo.ResponseKind == nil {
continue
}
kindForGVR.kind = schema.GroupVersionKind{
Group: gvr.Group,
Version: gvr.Version,
Kind: resourceInfo.ResponseKind.Kind,
}
if len(resourceInfo.ResponseKind.Group) > 0 {
kindForGVR.kind.Group = resourceInfo.ResponseKind.Group
}
if len(resourceInfo.ResponseKind.Version) > 0 {
kindForGVR.kind.Version = resourceInfo.ResponseKind.Version
}
kindForGVR.listKind = schema.GroupVersionKind{
Group: kindForGVR.kind.Group,
Version: kindForGVR.kind.Version,
Kind: resourceInfo.ResponseKind.Kind + "List",
}
kindForGVR.err = nil
mrt.kindForResource[gvr] = kindForGVR
return kindForGVR, kindForGVR.err
}
}
}

mrt.kindForResource[gvr] = kindForGVR
return kindForGVR, kindForGVR.err
}
9 changes: 5 additions & 4 deletions pkg/manifestclient/readwrite_roundtripper.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ func NewRoundTripper(mustGatherDir string) *readWriteRoundTripper {
}

func newReadWriteRoundTripper(sourceFS fs.FS) *readWriteRoundTripper {
return &readWriteRoundTripper{
readDelegate: newReadRoundTripper(sourceFS),
writeDelegate: newWriteRoundTripper(),
}
rt := &readWriteRoundTripper{}
discoveryReader := newDiscoveryReader(sourceFS)
rt.readDelegate = newReadRoundTripper(sourceFS, discoveryReader)
rt.writeDelegate = newWriteRoundTripper(discoveryReader)
return rt
}

type readWriteRoundTripper struct {
Expand Down
Loading

0 comments on commit 64d8d9e

Please sign in to comment.