mirror of https://github.com/k3s-io/k3s
1672 lines
48 KiB
Go
1672 lines
48 KiB
Go
/*
|
|
Copyright 2016 The Kubernetes Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package history
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
|
|
apps "k8s.io/api/apps/v1"
|
|
"k8s.io/api/core/v1"
|
|
"k8s.io/client-go/informers"
|
|
"k8s.io/client-go/kubernetes/fake"
|
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
|
"k8s.io/kubernetes/pkg/api/testapi"
|
|
"k8s.io/kubernetes/pkg/controller"
|
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"k8s.io/apimachinery/pkg/util/strategicpatch"
|
|
|
|
core "k8s.io/client-go/testing"
|
|
)
|
|
|
|
func TestRealHistory_ListControllerRevisions(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
parent metav1.Object
|
|
selector labels.Selector
|
|
revisions []*apps.ControllerRevision
|
|
want map[string]bool
|
|
}
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
client := fake.NewSimpleClientset()
|
|
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
|
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
informerFactory.Start(stop)
|
|
informer := informerFactory.Apps().V1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
for i := range test.revisions {
|
|
informer.Informer().GetIndexer().Add(test.revisions[i])
|
|
}
|
|
|
|
history := NewHistory(client, informer.Lister())
|
|
revisions, err := history.ListControllerRevisions(test.parent, test.selector)
|
|
if err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
got := make(map[string]bool)
|
|
for i := range revisions {
|
|
got[revisions[i].Name] = true
|
|
}
|
|
if !reflect.DeepEqual(test.want, got) {
|
|
t.Errorf("%s: want %v got %v", test.name, test.want, got)
|
|
}
|
|
}
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"})
|
|
sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 1, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
ss1Orphan, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 3, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Orphan.Namespace = ss1.Namespace
|
|
ss1Orphan.OwnerReferences = nil
|
|
|
|
tests := []testcase{
|
|
{
|
|
name: "selects none",
|
|
parent: &ss1.ObjectMeta,
|
|
selector: sel1,
|
|
revisions: nil,
|
|
want: map[string]bool{},
|
|
},
|
|
{
|
|
name: "selects all",
|
|
parent: &ss1.ObjectMeta,
|
|
selector: sel1,
|
|
revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2},
|
|
want: map[string]bool{ss1Rev1.Name: true, ss1Rev2.Name: true},
|
|
},
|
|
{
|
|
name: "doesn't select another Objects history",
|
|
parent: &ss1.ObjectMeta,
|
|
selector: sel1,
|
|
revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2, ss2Rev1},
|
|
want: map[string]bool{ss1Rev1.Name: true, ss1Rev2.Name: true},
|
|
},
|
|
{
|
|
name: "selects orphans",
|
|
parent: &ss1.ObjectMeta,
|
|
selector: sel1,
|
|
revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2, ss1Orphan},
|
|
want: map[string]bool{ss1Rev1.Name: true, ss1Rev2.Name: true, ss1Orphan.Name: true},
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestFakeHistory_ListControllerRevisions(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
parent metav1.Object
|
|
selector labels.Selector
|
|
revisions []*apps.ControllerRevision
|
|
want map[string]bool
|
|
}
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
client := fake.NewSimpleClientset()
|
|
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
|
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
informerFactory.Start(stop)
|
|
informer := informerFactory.Apps().V1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
for i := range test.revisions {
|
|
informer.Informer().GetIndexer().Add(test.revisions[i])
|
|
}
|
|
|
|
history := NewFakeHistory(informer)
|
|
revisions, err := history.ListControllerRevisions(test.parent, test.selector)
|
|
if err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
got := make(map[string]bool)
|
|
for i := range revisions {
|
|
got[revisions[i].Name] = true
|
|
}
|
|
if !reflect.DeepEqual(test.want, got) {
|
|
t.Errorf("%s: want %v got %v", test.name, test.want, got)
|
|
}
|
|
}
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"})
|
|
sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 1, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
ss1Orphan, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 3, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Orphan.Namespace = ss1.Namespace
|
|
ss1Orphan.OwnerReferences = nil
|
|
|
|
tests := []testcase{
|
|
{
|
|
name: "selects none",
|
|
parent: &ss1.ObjectMeta,
|
|
selector: sel1,
|
|
revisions: nil,
|
|
want: map[string]bool{},
|
|
},
|
|
{
|
|
name: "selects all",
|
|
parent: &ss1.ObjectMeta,
|
|
selector: sel1,
|
|
revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2},
|
|
want: map[string]bool{ss1Rev1.Name: true, ss1Rev2.Name: true},
|
|
},
|
|
{
|
|
name: "doesn't select another Objects history",
|
|
parent: &ss1.ObjectMeta,
|
|
selector: sel1,
|
|
revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2, ss2Rev1},
|
|
want: map[string]bool{ss1Rev1.Name: true, ss1Rev2.Name: true},
|
|
},
|
|
{
|
|
name: "selects orphans",
|
|
parent: &ss1.ObjectMeta,
|
|
selector: sel1,
|
|
revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2, ss1Orphan},
|
|
want: map[string]bool{ss1Rev1.Name: true, ss1Rev2.Name: true, ss1Orphan.Name: true},
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestRealHistory_CreateControllerRevision(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
existing []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}
|
|
rename bool
|
|
}
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
client := fake.NewSimpleClientset()
|
|
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
informerFactory.Start(stop)
|
|
informer := informerFactory.Apps().V1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewHistory(client, informer.Lister())
|
|
|
|
var collisionCount int32
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision, &collisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
// Clear collisionCount before creating the test revision
|
|
collisionCount = 0
|
|
created, err := history.CreateControllerRevision(test.parent, test.revision, &collisionCount)
|
|
if err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
|
|
if test.rename {
|
|
if created.Name == test.revision.Name {
|
|
t.Errorf("%s: wanted rename got %s %s", test.name, created.Name, test.revision.Name)
|
|
}
|
|
expectedName := ControllerRevisionName(test.parent.GetName(), HashControllerRevision(test.revision, &collisionCount))
|
|
if created.Name != expectedName {
|
|
t.Errorf("%s: on name collision wanted new name %s got %s", test.name, expectedName, created.Name)
|
|
}
|
|
|
|
// Second name collision should have incremented collisionCount to 2
|
|
_, err = history.CreateControllerRevision(test.parent, test.revision, &collisionCount)
|
|
if err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
if collisionCount != 2 {
|
|
t.Errorf("%s: on second name collision wanted collisionCount 1 got %d", test.name, collisionCount)
|
|
}
|
|
}
|
|
if !test.rename && created.Name != test.revision.Name {
|
|
t.Errorf("%s: wanted %s got %s", test.name, test.revision.Name, created.Name)
|
|
}
|
|
}
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss1.Status.CollisionCount = new(int32)
|
|
ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"})
|
|
ss2.Status.CollisionCount = new(int32)
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 1, ss2.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
tests := []testcase{
|
|
{
|
|
name: "creates new",
|
|
parent: &ss1.ObjectMeta,
|
|
revision: ss1Rev1,
|
|
existing: nil,
|
|
|
|
rename: false,
|
|
},
|
|
{
|
|
name: "create doesn't conflict when parents differ",
|
|
parent: &ss2.ObjectMeta,
|
|
revision: ss2Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
|
|
rename: false,
|
|
},
|
|
{
|
|
name: "create renames on conflict",
|
|
parent: &ss1.ObjectMeta,
|
|
revision: ss1Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
rename: true,
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestFakeHistory_CreateControllerRevision(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
existing []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}
|
|
rename bool
|
|
}
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
client := fake.NewSimpleClientset()
|
|
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
|
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
informerFactory.Start(stop)
|
|
informer := informerFactory.Apps().V1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewFakeHistory(informer)
|
|
|
|
var collisionCount int32
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision, &collisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
// Clear collisionCount before creating the test revision
|
|
collisionCount = 0
|
|
created, err := history.CreateControllerRevision(test.parent, test.revision, &collisionCount)
|
|
if err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
|
|
if test.rename {
|
|
if created.Name == test.revision.Name {
|
|
t.Errorf("%s: wanted rename got %s %s", test.name, created.Name, test.revision.Name)
|
|
}
|
|
expectedName := ControllerRevisionName(test.parent.GetName(), HashControllerRevision(test.revision, &collisionCount))
|
|
if created.Name != expectedName {
|
|
t.Errorf("%s: on name collision wanted new name %s got %s", test.name, expectedName, created.Name)
|
|
}
|
|
|
|
// Second name collision should have incremented collisionCount to 2
|
|
_, err = history.CreateControllerRevision(test.parent, test.revision, &collisionCount)
|
|
if err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
if collisionCount != 2 {
|
|
t.Errorf("%s: on second name collision wanted collisionCount 1 got %d", test.name, collisionCount)
|
|
}
|
|
}
|
|
if !test.rename && created.Name != test.revision.Name {
|
|
t.Errorf("%s: wanted %s got %s", test.name, test.revision.Name, created.Name)
|
|
}
|
|
}
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss1.Status.CollisionCount = new(int32)
|
|
ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"})
|
|
ss2.Status.CollisionCount = new(int32)
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 1, ss2.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
tests := []testcase{
|
|
{
|
|
name: "creates new",
|
|
parent: &ss1.ObjectMeta,
|
|
revision: ss1Rev1,
|
|
existing: nil,
|
|
|
|
rename: false,
|
|
},
|
|
{
|
|
name: "create doesn't conflict when parents differ",
|
|
parent: &ss2.ObjectMeta,
|
|
revision: ss2Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
|
|
rename: false,
|
|
},
|
|
{
|
|
name: "create renames on conflict",
|
|
parent: &ss1.ObjectMeta,
|
|
revision: ss1Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
rename: true,
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestRealHistory_UpdateControllerRevision(t *testing.T) {
|
|
conflictAttempts := 0
|
|
type testcase struct {
|
|
name string
|
|
revision *apps.ControllerRevision
|
|
newRevision int64
|
|
existing []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}
|
|
reactor core.ReactionFunc
|
|
err bool
|
|
}
|
|
conflictSuccess := func(action core.Action) (bool, runtime.Object, error) {
|
|
defer func() {
|
|
conflictAttempts++
|
|
}()
|
|
switch action.(type) {
|
|
|
|
case core.UpdateActionImpl:
|
|
update := action.(core.UpdateAction)
|
|
if conflictAttempts < 2 {
|
|
return true, update.GetObject(), errors.NewConflict(update.GetResource().GroupResource(), "", fmt.Errorf("conflict"))
|
|
}
|
|
return true, update.GetObject(), nil
|
|
default:
|
|
return false, nil, nil
|
|
}
|
|
}
|
|
internalError := func(action core.Action) (bool, runtime.Object, error) {
|
|
switch action.(type) {
|
|
case core.UpdateActionImpl:
|
|
return true, nil, errors.NewInternalError(fmt.Errorf("internal error"))
|
|
default:
|
|
return false, nil, nil
|
|
}
|
|
}
|
|
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
client := fake.NewSimpleClientset()
|
|
|
|
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
informerFactory.Start(stop)
|
|
informer := informerFactory.Apps().V1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewHistory(client, informer.Lister())
|
|
var collisionCount int32
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision, &collisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if test.reactor != nil {
|
|
client.PrependReactor("*", "*", test.reactor)
|
|
}
|
|
updated, err := history.UpdateControllerRevision(test.revision, test.newRevision)
|
|
if !test.err && err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
if !test.err && updated.Revision != test.newRevision {
|
|
t.Errorf("%s: got %d want %d", test.name, updated.Revision, test.newRevision)
|
|
}
|
|
if test.err && err == nil {
|
|
t.Errorf("%s: expected error", test.name)
|
|
}
|
|
}
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss1.Status.CollisionCount = new(int32)
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
|
|
tests := []testcase{
|
|
{
|
|
name: "update succeeds",
|
|
revision: ss1Rev1,
|
|
newRevision: ss1Rev1.Revision + 1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
reactor: nil,
|
|
err: false,
|
|
},
|
|
{
|
|
name: "update succeeds no noop",
|
|
revision: ss1Rev1,
|
|
newRevision: ss1Rev1.Revision,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
reactor: nil,
|
|
err: false,
|
|
}, {
|
|
name: "update fails on error",
|
|
revision: ss1Rev1,
|
|
newRevision: ss1Rev1.Revision + 10,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
reactor: internalError,
|
|
err: true,
|
|
},
|
|
{
|
|
name: "update on succeeds on conflict",
|
|
revision: ss1Rev1,
|
|
newRevision: ss1Rev1.Revision + 1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
reactor: conflictSuccess,
|
|
err: false,
|
|
},
|
|
}
|
|
for i := range tests {
|
|
conflictAttempts = 0
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestFakeHistory_UpdateControllerRevision(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
revision *apps.ControllerRevision
|
|
newRevision int64
|
|
existing []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}
|
|
err bool
|
|
}
|
|
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
client := fake.NewSimpleClientset()
|
|
|
|
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
informerFactory.Start(stop)
|
|
informer := informerFactory.Apps().V1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewFakeHistory(informer)
|
|
var collisionCount int32
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision, &collisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
updated, err := history.UpdateControllerRevision(test.revision, test.newRevision)
|
|
if !test.err && err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
if !test.err && updated.Revision != test.newRevision {
|
|
t.Errorf("%s: got %d want %d", test.name, updated.Revision, test.newRevision)
|
|
}
|
|
if test.err && err == nil {
|
|
t.Errorf("%s: expected error", test.name)
|
|
}
|
|
}
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss1.Status.CollisionCount = new(int32)
|
|
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
tests := []testcase{
|
|
{
|
|
name: "update succeeds",
|
|
revision: ss1Rev1,
|
|
newRevision: ss1Rev1.Revision + 1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
name: "update succeeds no noop",
|
|
revision: ss1Rev1,
|
|
newRevision: ss1Rev1.Revision,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
err: false,
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestRealHistory_DeleteControllerRevision(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
revision *apps.ControllerRevision
|
|
existing []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}
|
|
err bool
|
|
}
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
client := fake.NewSimpleClientset()
|
|
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
|
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
informerFactory.Start(stop)
|
|
informer := informerFactory.Apps().V1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewHistory(client, informer.Lister())
|
|
var collisionCount int32
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision, &collisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
err := history.DeleteControllerRevision(test.revision)
|
|
if !test.err && err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
if test.err && err == nil {
|
|
t.Errorf("%s: expected error", test.name)
|
|
}
|
|
}
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss1.Status.CollisionCount = new(int32)
|
|
ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"})
|
|
ss2.Status.CollisionCount = new(int32)
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 1, ss2.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
ss2Rev2, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 2, ss2.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev2.Namespace = ss2.Namespace
|
|
tests := []testcase{
|
|
{
|
|
name: "delete empty fails",
|
|
revision: ss1Rev1,
|
|
existing: nil,
|
|
err: true,
|
|
},
|
|
{
|
|
name: "delete existing succeeds",
|
|
revision: ss1Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
err: false,
|
|
}, {
|
|
name: "delete non-existing fails",
|
|
revision: ss1Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss2,
|
|
revision: ss2Rev1,
|
|
},
|
|
{
|
|
parent: ss2,
|
|
revision: ss2Rev2,
|
|
},
|
|
},
|
|
err: true,
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestFakeHistory_DeleteControllerRevision(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
revision *apps.ControllerRevision
|
|
existing []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}
|
|
err bool
|
|
}
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
client := fake.NewSimpleClientset()
|
|
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
|
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
informerFactory.Start(stop)
|
|
informer := informerFactory.Apps().V1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewFakeHistory(informer)
|
|
var collisionCount int32
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision, &collisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
err := history.DeleteControllerRevision(test.revision)
|
|
if !test.err && err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
if test.err && err == nil {
|
|
t.Errorf("%s: expected error", test.name)
|
|
}
|
|
}
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss1.Status.CollisionCount = new(int32)
|
|
ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"})
|
|
ss2.Status.CollisionCount = new(int32)
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 1, ss2.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
ss2Rev2, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 2, ss2.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev2.Namespace = ss2.Namespace
|
|
tests := []testcase{
|
|
{
|
|
name: "delete empty fails",
|
|
revision: ss1Rev1,
|
|
existing: nil,
|
|
err: true,
|
|
},
|
|
{
|
|
name: "delete existing succeeds",
|
|
revision: ss1Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
err: false,
|
|
}, {
|
|
name: "delete non-existing fails",
|
|
revision: ss1Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss2,
|
|
revision: ss2Rev1,
|
|
},
|
|
{
|
|
parent: ss2,
|
|
revision: ss2Rev2,
|
|
},
|
|
},
|
|
err: true,
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestRealHistory_AdoptControllerRevision(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
existing []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}
|
|
err bool
|
|
}
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
client := fake.NewSimpleClientset()
|
|
client.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) {
|
|
switch action := action.(type) {
|
|
case core.PatchActionImpl:
|
|
var found *apps.ControllerRevision
|
|
for i := range test.existing {
|
|
if test.revision.Name == test.existing[i].revision.Name &&
|
|
test.revision.Namespace == test.existing[i].revision.Namespace {
|
|
found = test.existing[i].revision
|
|
break
|
|
}
|
|
}
|
|
if found == nil {
|
|
return true, nil, errors.NewNotFound(apps.Resource("controllerrevisions"), test.revision.Name)
|
|
}
|
|
b, err := strategicpatch.StrategicMergePatch(
|
|
[]byte(runtime.EncodeOrDie(testapi.Apps.Codec(), test.revision)),
|
|
action.GetPatch(), test.revision)
|
|
if err != nil {
|
|
return true, nil, err
|
|
}
|
|
obj, err := runtime.Decode(testapi.Apps.Codec(), b)
|
|
if err != nil {
|
|
return true, nil, err
|
|
}
|
|
patched, err := legacyscheme.Scheme.ConvertToVersion(obj, apps.SchemeGroupVersion)
|
|
if err != nil {
|
|
return true, nil, err
|
|
}
|
|
return true, patched, err
|
|
default:
|
|
return false, nil, nil
|
|
}
|
|
|
|
})
|
|
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
informerFactory.Start(stop)
|
|
informer := informerFactory.Apps().V1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
|
|
history := NewHistory(client, informer.Lister())
|
|
var collisionCount int32
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision, &collisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
adopted, err := history.AdoptControllerRevision(test.parent, parentKind, test.revision)
|
|
if !test.err && err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
if !test.err && !metav1.IsControlledBy(adopted, test.parent) {
|
|
t.Errorf("%s: adoption failed", test.name)
|
|
}
|
|
if test.err && err == nil {
|
|
t.Errorf("%s: expected error", test.name)
|
|
}
|
|
}
|
|
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss1.Status.CollisionCount = new(int32)
|
|
ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"})
|
|
ss2.Status.CollisionCount = new(int32)
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss1Rev2.OwnerReferences = []metav1.OwnerReference{}
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 1, ss2.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
tests := []testcase{
|
|
{
|
|
name: "adopting an orphan succeeds",
|
|
parent: ss1,
|
|
revision: ss1Rev2,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev2,
|
|
},
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
name: "adopting an owned revision fails",
|
|
parent: ss1,
|
|
revision: ss2Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss2,
|
|
revision: ss2Rev1,
|
|
},
|
|
},
|
|
err: true,
|
|
},
|
|
{
|
|
name: "adopting a non-existent revision fails",
|
|
parent: ss1,
|
|
revision: ss1Rev2,
|
|
existing: nil,
|
|
err: true,
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestFakeHistory_AdoptControllerRevision(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
parent metav1.Object
|
|
parentType *metav1.TypeMeta
|
|
revision *apps.ControllerRevision
|
|
existing []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}
|
|
err bool
|
|
}
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
client := fake.NewSimpleClientset()
|
|
|
|
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
informerFactory.Start(stop)
|
|
informer := informerFactory.Apps().V1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
|
|
history := NewFakeHistory(informer)
|
|
var collisionCount int32
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision, &collisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
adopted, err := history.AdoptControllerRevision(test.parent, parentKind, test.revision)
|
|
if !test.err && err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
if !test.err && !metav1.IsControlledBy(adopted, test.parent) {
|
|
t.Errorf("%s: adoption failed", test.name)
|
|
}
|
|
if test.err && err == nil {
|
|
t.Errorf("%s: expected error", test.name)
|
|
}
|
|
}
|
|
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss1.Status.CollisionCount = new(int32)
|
|
ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"})
|
|
ss2.Status.CollisionCount = new(int32)
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss1Rev2.OwnerReferences = []metav1.OwnerReference{}
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 1, ss2.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
tests := []testcase{
|
|
{
|
|
name: "adopting an orphan succeeds",
|
|
parent: ss1,
|
|
parentType: &ss1.TypeMeta,
|
|
revision: ss1Rev2,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev2,
|
|
},
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
name: "adopting an owned revision fails",
|
|
parent: ss1,
|
|
parentType: &ss1.TypeMeta,
|
|
revision: ss2Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss2,
|
|
revision: ss2Rev1,
|
|
},
|
|
},
|
|
err: true,
|
|
},
|
|
{
|
|
name: "adopting a non-existent revision fails",
|
|
parent: ss1,
|
|
parentType: &ss1.TypeMeta,
|
|
revision: ss1Rev2,
|
|
existing: nil,
|
|
err: true,
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestRealHistory_ReleaseControllerRevision(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
existing []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}
|
|
err bool
|
|
}
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
client := fake.NewSimpleClientset()
|
|
client.AddReactor("*", "*", func(action core.Action) (bool, runtime.Object, error) {
|
|
switch action := action.(type) {
|
|
case core.PatchActionImpl:
|
|
var found *apps.ControllerRevision
|
|
for i := range test.existing {
|
|
if test.revision.Name == test.existing[i].revision.Name &&
|
|
test.revision.Namespace == test.existing[i].revision.Namespace {
|
|
found = test.existing[i].revision
|
|
break
|
|
}
|
|
}
|
|
if found == nil {
|
|
return true, nil, errors.NewNotFound(apps.Resource("controllerrevisions"), test.revision.Name)
|
|
}
|
|
if !metav1.IsControlledBy(test.revision, test.parent) {
|
|
return true, nil, errors.NewInvalid(
|
|
test.revision.GroupVersionKind().GroupKind(), test.revision.Name, nil)
|
|
}
|
|
b, err := strategicpatch.StrategicMergePatch(
|
|
[]byte(runtime.EncodeOrDie(testapi.Apps.Codec(), test.revision)),
|
|
action.GetPatch(), test.revision)
|
|
if err != nil {
|
|
return true, nil, err
|
|
}
|
|
obj, err := runtime.Decode(testapi.Apps.Codec(), b)
|
|
if err != nil {
|
|
return true, nil, err
|
|
}
|
|
patched, err := legacyscheme.Scheme.ConvertToVersion(obj, apps.SchemeGroupVersion)
|
|
if err != nil {
|
|
return true, nil, err
|
|
}
|
|
return true, patched, err
|
|
default:
|
|
return false, nil, nil
|
|
}
|
|
|
|
})
|
|
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
informerFactory.Start(stop)
|
|
informer := informerFactory.Apps().V1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
|
|
history := NewHistory(client, informer.Lister())
|
|
var collisionCount int32
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision, &collisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
adopted, err := history.ReleaseControllerRevision(test.parent, test.revision)
|
|
if !test.err {
|
|
if err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
if adopted == nil {
|
|
return
|
|
}
|
|
if metav1.IsControlledBy(adopted, test.parent) {
|
|
t.Errorf("%s: release failed", test.name)
|
|
}
|
|
}
|
|
if test.err && err == nil {
|
|
t.Errorf("%s: expected error", test.name)
|
|
}
|
|
}
|
|
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"})
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss1Rev2.OwnerReferences = []metav1.OwnerReference{}
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 1, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
tests := []testcase{
|
|
{
|
|
name: "releasing an owned revision succeeds",
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
name: "releasing an orphan succeeds",
|
|
parent: ss1,
|
|
revision: ss1Rev2,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev2,
|
|
},
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
name: "releasing a revision owned by another controller succeeds",
|
|
parent: ss1,
|
|
revision: ss2Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss2,
|
|
revision: ss2Rev1,
|
|
},
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
name: "releasing a non-existent revision succeeds",
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
existing: nil,
|
|
err: false,
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestFakeHistory_ReleaseControllerRevision(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
existing []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}
|
|
err bool
|
|
}
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
client := fake.NewSimpleClientset()
|
|
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
|
|
stop := make(chan struct{})
|
|
defer close(stop)
|
|
informerFactory.Start(stop)
|
|
informer := informerFactory.Apps().V1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewFakeHistory(informer)
|
|
var collisionCount int32
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision, &collisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
adopted, err := history.ReleaseControllerRevision(test.parent, test.revision)
|
|
if !test.err {
|
|
if err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
if adopted == nil {
|
|
return
|
|
}
|
|
if metav1.IsControlledBy(adopted, test.parent) {
|
|
t.Errorf("%s: release failed", test.name)
|
|
}
|
|
}
|
|
if test.err && err == nil {
|
|
t.Errorf("%s: expected error", test.name)
|
|
}
|
|
}
|
|
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss1.Status.CollisionCount = new(int32)
|
|
ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"})
|
|
ss2.Status.CollisionCount = new(int32)
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss1Rev2.OwnerReferences = []metav1.OwnerReference{}
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 1, ss2.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
tests := []testcase{
|
|
{
|
|
name: "releasing an owned revision succeeds",
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
},
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
name: "releasing an orphan succeeds",
|
|
parent: ss1,
|
|
revision: ss1Rev2,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss1,
|
|
revision: ss1Rev2,
|
|
},
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
name: "releasing a revision owned by another controller succeeds",
|
|
parent: ss1,
|
|
revision: ss2Rev1,
|
|
existing: []struct {
|
|
parent metav1.Object
|
|
revision *apps.ControllerRevision
|
|
}{
|
|
{
|
|
parent: ss2,
|
|
revision: ss2Rev1,
|
|
},
|
|
},
|
|
err: false,
|
|
},
|
|
{
|
|
name: "releasing a non-existent revision succeeds",
|
|
parent: ss1,
|
|
revision: ss1Rev1,
|
|
existing: nil,
|
|
err: false,
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestFindEqualRevisions(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
revision *apps.ControllerRevision
|
|
revisions []*apps.ControllerRevision
|
|
want map[string]bool
|
|
}
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
found := FindEqualRevisions(test.revisions, test.revision)
|
|
if len(found) != len(test.want) {
|
|
t.Errorf("%s: want %d revisions found %d", test.name, len(test.want), len(found))
|
|
}
|
|
for i := range found {
|
|
if !test.want[found[i].Name] {
|
|
t.Errorf("%s: wanted %s not found", test.name, found[i].Name)
|
|
}
|
|
|
|
}
|
|
}
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss1.Status.CollisionCount = new(int32)
|
|
ss2 := newStatefulSet(3, "ss2", types.UID("ss2"), map[string]string{"goo": "car"})
|
|
ss2.Status.CollisionCount = new(int32)
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss1Rev2.OwnerReferences = []metav1.OwnerReference{}
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 1, ss2.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
ss2Rev2, err := NewControllerRevision(ss2, parentKind, ss2.Spec.Template.Labels, rawTemplate(&ss2.Spec.Template), 2, ss2.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev2.Namespace = ss2.Namespace
|
|
tests := []testcase{
|
|
{
|
|
name: "finds equivalent",
|
|
revision: ss1Rev1,
|
|
revisions: []*apps.ControllerRevision{ss1Rev1, ss2Rev1, ss2Rev2},
|
|
want: map[string]bool{ss1Rev1.Name: true},
|
|
},
|
|
{
|
|
name: "finds nothing when empty",
|
|
revision: ss1Rev1,
|
|
revisions: nil,
|
|
want: map[string]bool{},
|
|
},
|
|
{
|
|
name: "finds nothing with no matches",
|
|
revision: ss1Rev1,
|
|
revisions: []*apps.ControllerRevision{ss2Rev2, ss2Rev1},
|
|
want: map[string]bool{},
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func TestSortControllerRevisions(t *testing.T) {
|
|
type testcase struct {
|
|
name string
|
|
revisions []*apps.ControllerRevision
|
|
want []string
|
|
}
|
|
testFn := func(test *testcase, t *testing.T) {
|
|
SortControllerRevisions(test.revisions)
|
|
for i := range test.revisions {
|
|
if test.revisions[i].Name != test.want[i] {
|
|
t.Errorf("%s: want %s at %d got %s", test.name, test.want[i], i, test.revisions[i].Name)
|
|
}
|
|
}
|
|
}
|
|
ss1 := newStatefulSet(3, "ss1", types.UID("ss1"), map[string]string{"foo": "bar"})
|
|
ss1.Status.CollisionCount = new(int32)
|
|
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 1, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 2, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss1Rev3, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(&ss1.Spec.Template), 3, ss1.Status.CollisionCount)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev3.Namespace = ss1.Namespace
|
|
|
|
tests := []testcase{
|
|
{
|
|
name: "out of order",
|
|
revisions: []*apps.ControllerRevision{ss1Rev2, ss1Rev1, ss1Rev3},
|
|
want: []string{ss1Rev1.Name, ss1Rev2.Name, ss1Rev3.Name},
|
|
},
|
|
{
|
|
name: "sorted",
|
|
revisions: []*apps.ControllerRevision{ss1Rev1, ss1Rev2, ss1Rev3},
|
|
want: []string{ss1Rev1.Name, ss1Rev2.Name, ss1Rev3.Name},
|
|
},
|
|
{
|
|
name: "reversed",
|
|
revisions: []*apps.ControllerRevision{ss1Rev3, ss1Rev2, ss1Rev1},
|
|
want: []string{ss1Rev1.Name, ss1Rev2.Name, ss1Rev3.Name},
|
|
},
|
|
{
|
|
name: "empty",
|
|
revisions: nil,
|
|
want: nil,
|
|
},
|
|
}
|
|
for i := range tests {
|
|
testFn(&tests[i], t)
|
|
}
|
|
}
|
|
|
|
func newStatefulSet(replicas int, name string, uid types.UID, labels map[string]string) *apps.StatefulSet {
|
|
// Converting all the map-only selectors to set-based selectors.
|
|
var testMatchExpressions []metav1.LabelSelectorRequirement
|
|
for key, value := range labels {
|
|
sel := metav1.LabelSelectorRequirement{
|
|
Key: key,
|
|
Operator: metav1.LabelSelectorOpIn,
|
|
Values: []string{value},
|
|
}
|
|
testMatchExpressions = append(testMatchExpressions, sel)
|
|
}
|
|
return &apps.StatefulSet{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "StatefulSet",
|
|
APIVersion: "apps/v1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
Namespace: v1.NamespaceDefault,
|
|
UID: uid,
|
|
},
|
|
Spec: apps.StatefulSetSpec{
|
|
Selector: &metav1.LabelSelector{
|
|
// Purposely leaving MatchLabels nil, so to ensure it will break if any link
|
|
// in the chain ignores the set-based MatchExpressions.
|
|
MatchLabels: nil,
|
|
MatchExpressions: testMatchExpressions,
|
|
},
|
|
Replicas: func() *int32 { i := int32(replicas); return &i }(),
|
|
Template: v1.PodTemplateSpec{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Labels: labels,
|
|
},
|
|
Spec: v1.PodSpec{
|
|
Containers: []v1.Container{
|
|
{
|
|
Name: "nginx",
|
|
Image: "nginx",
|
|
VolumeMounts: []v1.VolumeMount{
|
|
{Name: "datadir", MountPath: "/tmp/"},
|
|
{Name: "home", MountPath: "/home"},
|
|
},
|
|
},
|
|
},
|
|
Volumes: []v1.Volume{{
|
|
Name: "home",
|
|
VolumeSource: v1.VolumeSource{
|
|
HostPath: &v1.HostPathVolumeSource{
|
|
Path: fmt.Sprintf("/tmp/%v", "home"),
|
|
},
|
|
}}},
|
|
},
|
|
},
|
|
VolumeClaimTemplates: []v1.PersistentVolumeClaim{
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{Name: "datadir"},
|
|
Spec: v1.PersistentVolumeClaimSpec{
|
|
Resources: v1.ResourceRequirements{
|
|
Requests: v1.ResourceList{
|
|
v1.ResourceStorage: *resource.NewQuantity(1, resource.BinarySI),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ServiceName: "governingsvc",
|
|
},
|
|
}
|
|
}
|
|
|
|
var parentKind = apps.SchemeGroupVersion.WithKind("StatefulSet")
|
|
|
|
func rawTemplate(template *v1.PodTemplateSpec) runtime.RawExtension {
|
|
buf := new(bytes.Buffer)
|
|
enc := json.NewEncoder(buf)
|
|
if err := enc.Encode(template); err != nil {
|
|
panic(err)
|
|
}
|
|
return runtime.RawExtension{Raw: buf.Bytes()}
|
|
}
|