@ -24,9 +24,15 @@ import (
"testing"
"testing"
"time"
"time"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
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/runtime"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
jsonserializer "k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/types"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest/fake"
"k8s.io/client-go/rest/fake"
@ -682,6 +688,153 @@ func TestReal(t *testing.T) {
}
}
}
}
type createPatchTestCase struct {
name string
// The target state.
target * unstructured . Unstructured
// The current state as it exists in the release.
current * unstructured . Unstructured
// The actual state as it exists in the cluster.
actual * unstructured . Unstructured
threeWayMergeForUnstructured bool
// The patch is supposed to transfer the current state to the target state,
// thereby preserving the actual state, wherever possible.
expectedPatch string
expectedPatchType types . PatchType
}
func ( c createPatchTestCase ) run ( t * testing . T ) {
scheme := runtime . NewScheme ( )
corev1 . AddToScheme ( scheme )
encoder := jsonserializer . NewSerializerWithOptions (
jsonserializer . DefaultMetaFactory , scheme , scheme , jsonserializer . SerializerOptions {
Yaml : false , Pretty : false , Strict : true ,
} ,
)
objBody := func ( obj runtime . Object ) io . ReadCloser {
return io . NopCloser ( bytes . NewReader ( [ ] byte ( runtime . EncodeOrDie ( encoder , obj ) ) ) )
}
header := make ( http . Header )
header . Set ( "Content-Type" , runtime . ContentTypeJSON )
restClient := & fake . RESTClient {
NegotiatedSerializer : unstructuredSerializer ,
Resp : & http . Response {
StatusCode : 200 ,
Body : objBody ( c . actual ) ,
Header : header ,
} ,
}
targetInfo := & resource . Info {
Client : restClient ,
Namespace : "default" ,
Name : "test-obj" ,
Object : c . target ,
Mapping : & meta . RESTMapping {
Resource : schema . GroupVersionResource {
Group : "crd.com" ,
Version : "v1" ,
Resource : "datas" ,
} ,
Scope : meta . RESTScopeNamespace ,
} ,
}
patch , patchType , err := createPatch ( targetInfo , c . current , c . threeWayMergeForUnstructured )
if err != nil {
t . Fatalf ( "Failed to create patch: %v" , err )
}
if c . expectedPatch != string ( patch ) {
t . Errorf ( "Unexpected patch.\nTarget:\n%s\nCurrent:\n%s\nActual:\n%s\n\nExpected:\n%s\nGot:\n%s" ,
c . target ,
c . current ,
c . actual ,
c . expectedPatch ,
string ( patch ) ,
)
}
if patchType != types . MergePatchType {
t . Errorf ( "Expected patch type %s, got %s" , types . MergePatchType , patchType )
}
}
func newTestCustomResourceData ( metadata map [ string ] string , spec map [ string ] interface { } ) * unstructured . Unstructured {
if metadata == nil {
metadata = make ( map [ string ] string )
}
if _ , ok := metadata [ "name" ] ; ! ok {
metadata [ "name" ] = "test-obj"
}
if _ , ok := metadata [ "namespace" ] ; ! ok {
metadata [ "namespace" ] = "default"
}
o := map [ string ] interface { } {
"apiVersion" : "crd.com/v1" ,
"kind" : "Data" ,
"metadata" : metadata ,
}
if len ( spec ) > 0 {
o [ "spec" ] = spec
}
return & unstructured . Unstructured {
Object : o ,
}
}
func TestCreatePatchCustomResourceMetadata ( t * testing . T ) {
target := newTestCustomResourceData ( map [ string ] string {
"meta.helm.sh/release-name" : "foo-simple" ,
"meta.helm.sh/release-namespace" : "default" ,
"objectset.rio.cattle.io/id" : "default-foo-simple" ,
} , nil )
testCase := createPatchTestCase {
name : "take ownership of resource" ,
target : target ,
current : target ,
actual : newTestCustomResourceData ( nil , map [ string ] interface { } {
"color" : "red" ,
} ) ,
threeWayMergeForUnstructured : true ,
expectedPatch : ` { "metadata": { "meta.helm.sh/release-name":"foo-simple","meta.helm.sh/release-namespace":"default","objectset.rio.cattle.io/id":"default-foo-simple"}} ` ,
expectedPatchType : types . MergePatchType ,
}
t . Run ( testCase . name , testCase . run )
// Previous behavior.
testCase . threeWayMergeForUnstructured = false
testCase . expectedPatch = ` { } `
t . Run ( testCase . name , testCase . run )
}
func TestCreatePatchCustomResourceSpec ( t * testing . T ) {
target := newTestCustomResourceData ( nil , map [ string ] interface { } {
"color" : "red" ,
"size" : "large" ,
} )
testCase := createPatchTestCase {
name : "merge with spec of existing custom resource" ,
target : target ,
current : target ,
actual : newTestCustomResourceData ( nil , map [ string ] interface { } {
"color" : "red" ,
"weight" : "heavy" ,
} ) ,
threeWayMergeForUnstructured : true ,
expectedPatch : ` { "spec": { "size":"large"}} ` ,
expectedPatchType : types . MergePatchType ,
}
t . Run ( testCase . name , testCase . run )
// Previous behavior.
testCase . threeWayMergeForUnstructured = false
testCase . expectedPatch = ` { } `
t . Run ( testCase . name , testCase . run )
}
const testServiceManifest = `
const testServiceManifest = `
kind : Service
kind : Service
apiVersion : v1
apiVersion : v1