
Implements history utilities for ControllerRevision in the controller/history package StatefulSetStatus now has additional fields for consistency with DaemonSet and Deployment StatefulSetStatus.Replicas now represents the current number of createdPods and StatefulSetStatus.ReadyReplicas is the current number of ready Pods
1688 lines
45 KiB
Go
1688 lines
45 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"
|
|
|
|
"k8s.io/kubernetes/pkg/api/testapi"
|
|
"k8s.io/kubernetes/pkg/api/v1"
|
|
apps "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
|
|
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
|
|
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/externalversions"
|
|
"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().V1beta1().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)
|
|
}
|
|
sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
ss1Orphan, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 3)
|
|
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().V1beta1().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)
|
|
}
|
|
sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
ss1Orphan, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 3)
|
|
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().V1beta1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewHistory(client, informer.Lister())
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
created, err := history.CreateControllerRevision(test.parent, test.revision)
|
|
if err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
if test.rename && created.Name == test.revision.Name {
|
|
t.Errorf("%s: wanted rename got %s %s", test.name, created.Name, test.revision.Name)
|
|
|
|
}
|
|
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"})
|
|
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)
|
|
}
|
|
sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1)
|
|
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().V1beta1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewFakeHistory(informer)
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
created, err := history.CreateControllerRevision(test.parent, test.revision)
|
|
if err != nil {
|
|
t.Errorf("%s: %s", test.name, err)
|
|
}
|
|
if test.rename && created.Name == test.revision.Name {
|
|
t.Errorf("%s: wanted rename got %s %s", test.name, created.Name, test.revision.Name)
|
|
|
|
}
|
|
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"})
|
|
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)
|
|
}
|
|
sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1)
|
|
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().V1beta1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewHistory(client, informer.Lister())
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision)
|
|
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"})
|
|
sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
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().V1beta1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewFakeHistory(informer)
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision)
|
|
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"})
|
|
sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
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().V1beta1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewHistory(client, informer.Lister())
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision)
|
|
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"})
|
|
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)
|
|
}
|
|
sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
ss2Rev2, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 2)
|
|
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().V1beta1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewFakeHistory(informer)
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision)
|
|
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"})
|
|
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)
|
|
}
|
|
sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
ss2Rev2, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 2)
|
|
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 := testapi.Apps.Converter().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().V1beta1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
|
|
history := NewHistory(client, informer.Lister())
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision)
|
|
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 && controller.GetControllerOf(adopted).UID != test.parent.GetUID() {
|
|
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"})
|
|
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)
|
|
}
|
|
sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss1Rev2.OwnerReferences = []metav1.OwnerReference{}
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1)
|
|
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().V1beta1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
|
|
history := NewFakeHistory(informer)
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision)
|
|
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 && controller.GetControllerOf(adopted).UID != test.parent.GetUID() {
|
|
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"})
|
|
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)
|
|
}
|
|
sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss1Rev2.OwnerReferences = []metav1.OwnerReference{}
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1)
|
|
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 foundParent := controller.GetControllerOf(test.revision); foundParent == nil ||
|
|
foundParent.UID != test.parent.GetUID() {
|
|
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 := testapi.Apps.Converter().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().V1beta1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
|
|
history := NewHistory(client, informer.Lister())
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision)
|
|
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 owner := controller.GetControllerOf(adopted); owner != nil && owner.UID == test.parent.GetUID() {
|
|
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"})
|
|
sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss1Rev2.OwnerReferences = []metav1.OwnerReference{}
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1)
|
|
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().V1beta1().ControllerRevisions()
|
|
informerFactory.WaitForCacheSync(stop)
|
|
history := NewFakeHistory(informer)
|
|
for i := range test.existing {
|
|
_, err := history.CreateControllerRevision(test.existing[i].parent, test.existing[i].revision)
|
|
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 owner := controller.GetControllerOf(adopted); owner != nil && owner.UID == test.parent.GetUID() {
|
|
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"})
|
|
sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss1Rev2.OwnerReferences = []metav1.OwnerReference{}
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1)
|
|
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"})
|
|
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)
|
|
}
|
|
sel2, err := metav1.LabelSelectorAsSelector(ss2.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss1Rev2.OwnerReferences = []metav1.OwnerReference{}
|
|
ss2Rev1, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss2Rev1.Namespace = ss2.Namespace
|
|
ss2Rev2, err := NewControllerRevision(ss2, parentKind, sel2, rawTemplate(&ss2.Spec.Template), 2)
|
|
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"})
|
|
sel1, err := metav1.LabelSelectorAsSelector(ss1.Spec.Selector)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
ss1Rev1, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev1.Namespace = ss1.Namespace
|
|
ss1Rev2, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ss1Rev2.Namespace = ss1.Namespace
|
|
ss1Rev3, err := NewControllerRevision(ss1, parentKind, sel1, rawTemplate(&ss1.Spec.Template), 2)
|
|
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 {
|
|
return &apps.StatefulSet{
|
|
TypeMeta: metav1.TypeMeta{
|
|
Kind: "StatefulSet",
|
|
APIVersion: "apps/v1beta1",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
Namespace: v1.NamespaceDefault,
|
|
UID: uid,
|
|
},
|
|
Spec: apps.StatefulSetSpec{
|
|
Selector: &metav1.LabelSelector{
|
|
MatchLabels: labels,
|
|
},
|
|
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()}
|
|
}
|