basic caching working

This commit is contained in:
Kevin Delgado 2021-07-14 18:54:16 +00:00
parent dda31bbf2e
commit 7b9757faa4
3 changed files with 60 additions and 18 deletions

View File

@ -4,7 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@ -16,6 +16,7 @@ import (
corev1ac "k8s.io/client-go/applyconfigurations/core/v1" corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
metav1ac "k8s.io/client-go/applyconfigurations/meta/v1" metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
"k8s.io/client-go/discovery" "k8s.io/client-go/discovery"
"k8s.io/client-go/discovery/cached/disk"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
@ -24,17 +25,12 @@ import (
"sigs.k8s.io/structured-merge-diff/v4/typed" "sigs.k8s.io/structured-merge-diff/v4/typed"
) )
// This is a small hacky script that tests the extract config against a real cluster
// to demonstrate a bug I'm seeing with the GVKParser where it claims there
// are duplicate GVK entries
// TODO: don't commit this, delete it before merging.
// Any and all functionality from this script should be captured in the appropriate integration test.
func main() { func main() {
fmt.Println("vim-go")
defaultNS := "default" defaultNS := "default"
mydep := "mydep" mydep := "mydep"
mgr := "mymanager" mgr := "mymanager"
// TODO: make this an arg kubeconfig := "/usr/local/google/home/kevindelgado/.kube/config"
kubeconfig := os.Getenv("KUBECONFIG")
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil { if err != nil {
@ -173,7 +169,10 @@ func main() {
fmt.Printf("appliedGuestbook = %+v\n", appliedGuestbook) fmt.Printf("appliedGuestbook = %+v\n", appliedGuestbook)
fmt.Println("---") fmt.Println("---")
discoveryClient := discovery.NewDiscoveryClientForConfigOrDie(config) discoveryClient, err := disk.NewCachedDiscoveryClientForConfig(config, ".", ".", time.Hour)
if err != nil {
panic(err)
}
//// OLD WAY //// OLD WAY
//extractedGuestbook, err := extractUnstructured(appliedGuestbook, guestbookGVR, discoveryClient, mgr) //extractedGuestbook, err := extractUnstructured(appliedGuestbook, guestbookGVR, discoveryClient, mgr)
//if err != nil { //if err != nil {
@ -187,6 +186,8 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
// do it again see if we get a 304
extractedGuestbook, err = extractor.ExtractUnstructured(appliedGuestbook, mgr)
fmt.Println("---") fmt.Println("---")
fmt.Printf("extractedGuestbook = %+v\n", extractedGuestbook) fmt.Printf("extractedGuestbook = %+v\n", extractedGuestbook)

View File

@ -1,6 +1,10 @@
package v1 package v1
import ( import (
"crypto/sha512"
"encoding/json"
"fmt"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/managedfields" "k8s.io/apimachinery/pkg/util/managedfields"
@ -26,7 +30,11 @@ type objectTypeCache interface {
// (i.e. it downloads the OpenAPISchema every time) // (i.e. it downloads the OpenAPISchema every time)
// Useful during the proof-of-concept stage until we agree on a caching solution. // Useful during the proof-of-concept stage until we agree on a caching solution.
type nonCachingObjectTypeCache struct { type nonCachingObjectTypeCache struct {
discoveryClient *discovery.DiscoveryClient // TODO: lock this?
discoveryClient discovery.DiscoveryInterface
docHash [sha512.Size]uint8
gvkParser *fieldmanager.GvkParser
typeForGVK map[schema.GroupVersionKind]*typed.ParseableType
} }
// objectTypeForGVK retrieves the typed.ParseableType for a given gvk from the cache // objectTypeForGVK retrieves the typed.ParseableType for a given gvk from the cache
@ -36,17 +44,47 @@ func (c *nonCachingObjectTypeCache) objectTypeForGVK(gvk schema.GroupVersionKind
return nil, err return nil, err
} }
models, err := proto.NewOpenAPIData(doc) docBytes, err := json.Marshal(doc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
docHash := sha512.Sum512(docBytes)
gvkParser, err := fieldmanager.NewGVKParser(models, false) // cache hit
if err != nil { if c.docHash == docHash {
return nil, err fmt.Println("cache hit")
fmt.Printf("docHash = %+v\n", docHash)
objType, ok := c.typeForGVK[gvk]
if ok {
fmt.Println("gvk recognized")
fmt.Printf("gvk = %+v\n", gvk)
return objType, nil
}
objType = c.gvkParser.Type(gvk)
c.typeForGVK[gvk] = objType
return objType, nil
} else {
fmt.Println("cache miss")
// cache miss
models, err := proto.NewOpenAPIData(doc)
if err != nil {
return nil, err
}
gvkParser, err := fieldmanager.NewGVKParser(models, false)
if err != nil {
return nil, err
}
objType := gvkParser.Type(gvk)
c.docHash = docHash
c.gvkParser = gvkParser
c.typeForGVK = map[schema.GroupVersionKind]*typed.ParseableType{
gvk: objType,
}
return objType, nil
} }
return gvkParser.Type(gvk), nil
} }
type extractor struct { type extractor struct {
@ -55,9 +93,11 @@ type extractor struct {
// NewUnstructuredExtractor creates the extractor with which you can extract the applied configuration // NewUnstructuredExtractor creates the extractor with which you can extract the applied configuration
// for a given manager from an unstructured object. // for a given manager from an unstructured object.
func NewUnstructuredExtractor(dc *discovery.DiscoveryClient) UnstructuredExtractor { func NewUnstructuredExtractor(dc discovery.DiscoveryInterface) UnstructuredExtractor {
return &extractor{ return &extractor{
cache: &nonCachingObjectTypeCache{dc}, cache: &nonCachingObjectTypeCache{
discoveryClient: dc,
},
} }
} }

View File

@ -244,6 +244,7 @@ func TestUnstructuredExtract(t *testing.T) {
t.Fatalf("failed to marshal pod into bytes: %v", err) t.Fatalf("failed to marshal pod into bytes: %v", err)
} }
// apply the unstructured object to the cluster
actual, err := dynamicClient.Resource(resource).Namespace("default").Patch( actual, err := dynamicClient.Resource(resource).Namespace("default").Patch(
context.TODO(), context.TODO(),
name, name,