mirror of https://github.com/fluxcd/cli-utils.git
313 lines
6.9 KiB
Go
313 lines
6.9 KiB
Go
// Copyright 2020 The Kubernetes Authors.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package task
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
|
|
"gotest.tools/assert"
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/cli-runtime/pkg/resource"
|
|
"sigs.k8s.io/cli-utils/pkg/apply/event"
|
|
"sigs.k8s.io/cli-utils/pkg/apply/taskrunner"
|
|
"sigs.k8s.io/cli-utils/pkg/object"
|
|
"sigs.k8s.io/cli-utils/pkg/testutil"
|
|
)
|
|
|
|
type resourceInfo struct {
|
|
group string
|
|
apiVersion string
|
|
kind string
|
|
name string
|
|
namespace string
|
|
generation int64
|
|
}
|
|
|
|
func TestApplyTask_FetchGeneration(t *testing.T) {
|
|
testCases := map[string]struct {
|
|
rss []resourceInfo
|
|
}{
|
|
"single namespaced resource": {
|
|
rss: []resourceInfo{
|
|
{
|
|
group: "apps",
|
|
apiVersion: "apps/v1",
|
|
kind: "Deployment",
|
|
name: "foo",
|
|
namespace: "default",
|
|
generation: int64(42),
|
|
},
|
|
},
|
|
},
|
|
"multiple clusterscoped resources": {
|
|
rss: []resourceInfo{
|
|
{
|
|
group: "custom.io",
|
|
apiVersion: "custom.io/v1beta1",
|
|
kind: "Custom",
|
|
name: "bar",
|
|
generation: int64(32),
|
|
},
|
|
{
|
|
group: "custom2.io",
|
|
apiVersion: "custom2.io/v1",
|
|
kind: "Custom2",
|
|
name: "foo",
|
|
generation: int64(1),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for tn, tc := range testCases {
|
|
t.Run(tn, func(t *testing.T) {
|
|
eventChannel := make(chan event.Event)
|
|
defer close(eventChannel)
|
|
taskContext := taskrunner.NewTaskContext(eventChannel)
|
|
|
|
infos := toInfos(tc.rss)
|
|
|
|
applyOptions := &fakeApplyOptions{}
|
|
|
|
applyTask := &ApplyTask{
|
|
ApplyOptions: applyOptions,
|
|
Objects: infos,
|
|
InfoHelper: &fakeInfoHelper{},
|
|
}
|
|
|
|
applyTask.Start(taskContext)
|
|
|
|
<-taskContext.TaskChannel()
|
|
|
|
for _, info := range tc.rss {
|
|
id := object.ObjMetadata{
|
|
GroupKind: schema.GroupKind{
|
|
Group: info.group,
|
|
Kind: info.kind,
|
|
},
|
|
Name: info.name,
|
|
Namespace: info.namespace,
|
|
}
|
|
gen := taskContext.ResourceGeneration(id)
|
|
assert.Equal(t, info.generation, gen)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestApplyTask_DryRun(t *testing.T) {
|
|
testCases := map[string]struct {
|
|
infos []*resource.Info
|
|
crds []*resource.Info
|
|
expectedObjects []object.ObjMetadata
|
|
expectedEvents []event.Event
|
|
}{
|
|
"dry run with no CRDs or CRs": {
|
|
infos: []*resource.Info{
|
|
toInfo(map[string]interface{}{
|
|
"apiVersion": "apps/v1",
|
|
"kind": "Deployment",
|
|
"metadata": map[string]interface{}{
|
|
"name": "foo",
|
|
"namespace": "default",
|
|
},
|
|
}),
|
|
},
|
|
expectedObjects: []object.ObjMetadata{
|
|
{
|
|
GroupKind: schema.GroupKind{
|
|
Group: "apps",
|
|
Kind: "Deployment",
|
|
},
|
|
Name: "foo",
|
|
Namespace: "default",
|
|
},
|
|
},
|
|
expectedEvents: []event.Event{},
|
|
},
|
|
"dry run with CRD and CR": {
|
|
crds: []*resource.Info{
|
|
toInfo(map[string]interface{}{
|
|
"apiVersion": "apiextensions.k8s.io/v1",
|
|
"kind": "CustomResourceDefinition",
|
|
"metadata": map[string]interface{}{
|
|
"name": "foo",
|
|
},
|
|
"spec": map[string]interface{}{
|
|
"group": "custom.io",
|
|
"names": map[string]interface{}{
|
|
"kind": "Custom",
|
|
},
|
|
"versions": []interface{}{
|
|
map[string]interface{}{
|
|
"name": "v1alpha1",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
infos: []*resource.Info{
|
|
toInfo(map[string]interface{}{
|
|
"apiVersion": "custom.io/v1alpha1",
|
|
"kind": "Custom",
|
|
"metadata": map[string]interface{}{
|
|
"name": "bar",
|
|
},
|
|
}),
|
|
},
|
|
expectedObjects: []object.ObjMetadata{},
|
|
expectedEvents: []event.Event{
|
|
{
|
|
Type: event.ApplyType,
|
|
},
|
|
},
|
|
},
|
|
"dry run with CRD and CR and CRD already installed": {
|
|
crds: []*resource.Info{
|
|
toInfo(map[string]interface{}{
|
|
"apiVersion": "apiextensions.k8s.io/v1",
|
|
"kind": "CustomResourceDefinition",
|
|
"metadata": map[string]interface{}{
|
|
"name": "foo",
|
|
},
|
|
"spec": map[string]interface{}{
|
|
"group": "anothercustom.io",
|
|
"names": map[string]interface{}{
|
|
"kind": "AnotherCustom",
|
|
},
|
|
"versions": []interface{}{
|
|
map[string]interface{}{
|
|
"name": "v2",
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
infos: []*resource.Info{
|
|
toInfo(map[string]interface{}{
|
|
"apiVersion": "anothercustom.io/v2",
|
|
"kind": "AnotherCustom",
|
|
"metadata": map[string]interface{}{
|
|
"name": "bar",
|
|
"namespace": "barbar",
|
|
},
|
|
}),
|
|
},
|
|
expectedObjects: []object.ObjMetadata{
|
|
{
|
|
GroupKind: schema.GroupKind{
|
|
Group: "anothercustom.io",
|
|
Kind: "AnotherCustom",
|
|
},
|
|
Name: "bar",
|
|
Namespace: "barbar",
|
|
},
|
|
},
|
|
expectedEvents: []event.Event{},
|
|
},
|
|
}
|
|
|
|
for tn, tc := range testCases {
|
|
t.Run(tn, func(t *testing.T) {
|
|
eventChannel := make(chan event.Event)
|
|
taskContext := taskrunner.NewTaskContext(eventChannel)
|
|
|
|
restMapper := testutil.NewFakeRESTMapper(schema.GroupVersionKind{
|
|
Group: "apps",
|
|
Version: "v1",
|
|
Kind: "Deployment",
|
|
}, schema.GroupVersionKind{
|
|
Group: "anothercustom.io",
|
|
Version: "v2",
|
|
Kind: "AnotherCustom",
|
|
})
|
|
|
|
applyOptions := &fakeApplyOptions{}
|
|
|
|
applyTask := &ApplyTask{
|
|
ApplyOptions: applyOptions,
|
|
Objects: tc.infos,
|
|
InfoHelper: &fakeInfoHelper{},
|
|
Mapper: restMapper,
|
|
DryRun: true,
|
|
CRDs: tc.crds,
|
|
}
|
|
|
|
var events []event.Event
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for msg := range eventChannel {
|
|
events = append(events, msg)
|
|
}
|
|
}()
|
|
|
|
applyTask.Start(taskContext)
|
|
<-taskContext.TaskChannel()
|
|
close(eventChannel)
|
|
wg.Wait()
|
|
|
|
assert.Equal(t, len(tc.expectedObjects), len(applyOptions.objects))
|
|
for i, obj := range applyOptions.objects {
|
|
assert.Equal(t, tc.expectedObjects[i], object.InfoToObjMeta(obj))
|
|
}
|
|
|
|
assert.Equal(t, len(tc.expectedEvents), len(events))
|
|
for i, e := range events {
|
|
assert.Equal(t, tc.expectedEvents[i].Type, e.Type)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func toInfo(obj map[string]interface{}) *resource.Info {
|
|
return &resource.Info{
|
|
Object: &unstructured.Unstructured{
|
|
Object: obj,
|
|
},
|
|
}
|
|
}
|
|
|
|
func toInfos(rss []resourceInfo) []*resource.Info {
|
|
var infos []*resource.Info
|
|
|
|
for _, rs := range rss {
|
|
infos = append(infos, &resource.Info{
|
|
Object: &unstructured.Unstructured{
|
|
Object: map[string]interface{}{
|
|
"apiVersion": rs.apiVersion,
|
|
"kind": rs.kind,
|
|
"metadata": map[string]interface{}{
|
|
"name": rs.name,
|
|
"namespace": rs.namespace,
|
|
"generation": rs.generation,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
return infos
|
|
}
|
|
|
|
type fakeApplyOptions struct {
|
|
objects []*resource.Info
|
|
}
|
|
|
|
func (f *fakeApplyOptions) Run() error {
|
|
return nil
|
|
}
|
|
|
|
func (f *fakeApplyOptions) SetObjects(objects []*resource.Info) {
|
|
f.objects = objects
|
|
}
|
|
|
|
type fakeInfoHelper struct{}
|
|
|
|
func (f *fakeInfoHelper) UpdateInfos([]*resource.Info) error {
|
|
return nil
|
|
}
|