blob: 13aaaf5d092320b6bbf83511ca4b6e369c666727 [file] [log] [blame]
/*
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 _, item := range test.existing {
_, err := client.AppsV1().ControllerRevisions(item.parent.GetNamespace()).Create(item.revision)
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 will be caused by an identical revision, so no need to do anything
_, err = history.CreateControllerRevision(test.parent, test.revision, &collisionCount)
if err != nil {
t.Errorf("%s: %s", test.name, err)
}
if collisionCount != 1 {
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
// Create a new revision with the same name and hash label as an existing revision, but with
// a different template. This could happen as a result of a hash collision, but in this test
// this situation is created by setting name and hash label to values known to be in use by
// an existing revision.
modTemplate := ss1.Spec.Template.DeepCopy()
modTemplate.Labels["foo"] = "not_bar"
ss1Rev2, err := NewControllerRevision(ss1, parentKind, ss1.Spec.Template.Labels, rawTemplate(modTemplate), 2, ss1.Status.CollisionCount)
ss1Rev2.Name = ss1Rev1.Name
ss1Rev2.Labels[ControllerRevisionHashLabel] = ss1Rev1.Labels[ControllerRevisionHashLabel]
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: ss1Rev2,
},
},
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()}
}