Merged by openshift-bot
OpenShift Bot authored on 2015/05/19 10:16:101 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,155 @@ |
0 |
+package prune |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "fmt" |
|
4 |
+ "time" |
|
5 |
+ |
|
6 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
7 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache" |
|
8 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
9 |
+ |
|
10 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
11 |
+ deployutil "github.com/openshift/origin/pkg/deploy/util" |
|
12 |
+) |
|
13 |
+ |
|
14 |
+// DeploymentByDeploymentConfigIndexFunc indexes Deployment items by their associated DeploymentConfig, if none, index with key "orphan" |
|
15 |
+func DeploymentByDeploymentConfigIndexFunc(obj interface{}) (string, error) { |
|
16 |
+ controller, ok := obj.(*kapi.ReplicationController) |
|
17 |
+ if !ok { |
|
18 |
+ return "", fmt.Errorf("not a replication controller: %v", obj) |
|
19 |
+ } |
|
20 |
+ name := deployutil.DeploymentConfigNameFor(controller) |
|
21 |
+ if len(name) == 0 { |
|
22 |
+ return "orphan", nil |
|
23 |
+ } |
|
24 |
+ return controller.Namespace + "/" + name, nil |
|
25 |
+} |
|
26 |
+ |
|
27 |
+// Filter filters the set of objects |
|
28 |
+type Filter interface { |
|
29 |
+ Filter(items []*kapi.ReplicationController) []*kapi.ReplicationController |
|
30 |
+} |
|
31 |
+ |
|
32 |
+// andFilter ands a set of predicate functions to know if it should be included in the return set |
|
33 |
+type andFilter struct { |
|
34 |
+ filterPredicates []FilterPredicate |
|
35 |
+} |
|
36 |
+ |
|
37 |
+// Filter ands the set of predicates evaluated against each item to make a filtered set |
|
38 |
+func (a *andFilter) Filter(items []*kapi.ReplicationController) []*kapi.ReplicationController { |
|
39 |
+ results := []*kapi.ReplicationController{} |
|
40 |
+ for _, item := range items { |
|
41 |
+ include := true |
|
42 |
+ for _, filterPredicate := range a.filterPredicates { |
|
43 |
+ include = include && filterPredicate(item) |
|
44 |
+ } |
|
45 |
+ if include { |
|
46 |
+ results = append(results, item) |
|
47 |
+ } |
|
48 |
+ } |
|
49 |
+ return results |
|
50 |
+} |
|
51 |
+ |
|
52 |
+// FilterPredicate is a function that returns true if the object should be included in the filtered set |
|
53 |
+type FilterPredicate func(item *kapi.ReplicationController) bool |
|
54 |
+ |
|
55 |
+// NewFilterBeforePredicate is a function that returns true if the build was created before the current time minus specified duration |
|
56 |
+func NewFilterBeforePredicate(d time.Duration) FilterPredicate { |
|
57 |
+ now := util.Now() |
|
58 |
+ before := util.NewTime(now.Time.Add(-1 * d)) |
|
59 |
+ return func(item *kapi.ReplicationController) bool { |
|
60 |
+ return item.CreationTimestamp.Before(before) |
|
61 |
+ } |
|
62 |
+} |
|
63 |
+ |
|
64 |
+// FilterDeploymentsPredicate is a function that returns true if the replication controller is associated with a DeploymentConfig |
|
65 |
+func FilterDeploymentsPredicate(item *kapi.ReplicationController) bool { |
|
66 |
+ return len(deployutil.DeploymentConfigNameFor(item)) > 0 |
|
67 |
+} |
|
68 |
+ |
|
69 |
+// FilterZeroReplicaSize is a function that returns true if the replication controller size is 0 |
|
70 |
+func FilterZeroReplicaSize(item *kapi.ReplicationController) bool { |
|
71 |
+ return item.Spec.Replicas == 0 && item.Status.Replicas == 0 |
|
72 |
+} |
|
73 |
+ |
|
74 |
+// DataSet provides functions for working with deployment data |
|
75 |
+type DataSet interface { |
|
76 |
+ GetDeploymentConfig(deployment *kapi.ReplicationController) (*deployapi.DeploymentConfig, bool, error) |
|
77 |
+ ListDeploymentConfigs() ([]*deployapi.DeploymentConfig, error) |
|
78 |
+ ListDeployments() ([]*kapi.ReplicationController, error) |
|
79 |
+ ListDeploymentsByDeploymentConfig(config *deployapi.DeploymentConfig) ([]*kapi.ReplicationController, error) |
|
80 |
+} |
|
81 |
+ |
|
82 |
+type dataSet struct { |
|
83 |
+ deploymentConfigStore cache.Store |
|
84 |
+ deploymentIndexer cache.Indexer |
|
85 |
+} |
|
86 |
+ |
|
87 |
+// NewDataSet returns a DataSet over the specified items |
|
88 |
+func NewDataSet(deploymentConfigs []*deployapi.DeploymentConfig, deployments []*kapi.ReplicationController) DataSet { |
|
89 |
+ deploymentConfigStore := cache.NewStore(cache.MetaNamespaceKeyFunc) |
|
90 |
+ for _, deploymentConfig := range deploymentConfigs { |
|
91 |
+ deploymentConfigStore.Add(deploymentConfig) |
|
92 |
+ } |
|
93 |
+ |
|
94 |
+ deploymentIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{ |
|
95 |
+ "deploymentConfig": DeploymentByDeploymentConfigIndexFunc, |
|
96 |
+ }) |
|
97 |
+ for _, deployment := range deployments { |
|
98 |
+ deploymentIndexer.Add(deployment) |
|
99 |
+ } |
|
100 |
+ |
|
101 |
+ return &dataSet{ |
|
102 |
+ deploymentConfigStore: deploymentConfigStore, |
|
103 |
+ deploymentIndexer: deploymentIndexer, |
|
104 |
+ } |
|
105 |
+} |
|
106 |
+ |
|
107 |
+func (d *dataSet) GetDeploymentConfig(controller *kapi.ReplicationController) (*deployapi.DeploymentConfig, bool, error) { |
|
108 |
+ name := deployutil.DeploymentConfigNameFor(controller) |
|
109 |
+ if len(name) == 0 { |
|
110 |
+ return nil, false, nil |
|
111 |
+ } |
|
112 |
+ |
|
113 |
+ var deploymentConfig *deployapi.DeploymentConfig |
|
114 |
+ key := &deployapi.DeploymentConfig{ObjectMeta: kapi.ObjectMeta{Name: name, Namespace: controller.Namespace}} |
|
115 |
+ item, exists, err := d.deploymentConfigStore.Get(key) |
|
116 |
+ if exists { |
|
117 |
+ deploymentConfig = item.(*deployapi.DeploymentConfig) |
|
118 |
+ } |
|
119 |
+ return deploymentConfig, exists, err |
|
120 |
+} |
|
121 |
+ |
|
122 |
+func (d *dataSet) ListDeploymentConfigs() ([]*deployapi.DeploymentConfig, error) { |
|
123 |
+ results := []*deployapi.DeploymentConfig{} |
|
124 |
+ for _, item := range d.deploymentConfigStore.List() { |
|
125 |
+ results = append(results, item.(*deployapi.DeploymentConfig)) |
|
126 |
+ } |
|
127 |
+ return results, nil |
|
128 |
+} |
|
129 |
+ |
|
130 |
+func (d *dataSet) ListDeployments() ([]*kapi.ReplicationController, error) { |
|
131 |
+ results := []*kapi.ReplicationController{} |
|
132 |
+ for _, item := range d.deploymentIndexer.List() { |
|
133 |
+ results = append(results, item.(*kapi.ReplicationController)) |
|
134 |
+ } |
|
135 |
+ return results, nil |
|
136 |
+} |
|
137 |
+ |
|
138 |
+func (d *dataSet) ListDeploymentsByDeploymentConfig(deploymentConfig *deployapi.DeploymentConfig) ([]*kapi.ReplicationController, error) { |
|
139 |
+ results := []*kapi.ReplicationController{} |
|
140 |
+ key := &kapi.ReplicationController{ |
|
141 |
+ ObjectMeta: kapi.ObjectMeta{ |
|
142 |
+ Namespace: deploymentConfig.Namespace, |
|
143 |
+ Annotations: map[string]string{deployapi.DeploymentConfigAnnotation: deploymentConfig.Name}, |
|
144 |
+ }, |
|
145 |
+ } |
|
146 |
+ items, err := d.deploymentIndexer.Index("deploymentConfig", key) |
|
147 |
+ if err != nil { |
|
148 |
+ return nil, err |
|
149 |
+ } |
|
150 |
+ for _, item := range items { |
|
151 |
+ results = append(results, item.(*kapi.ReplicationController)) |
|
152 |
+ } |
|
153 |
+ return results, nil |
|
154 |
+} |
0 | 155 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,164 @@ |
0 |
+package prune |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "testing" |
|
4 |
+ "time" |
|
5 |
+ |
|
6 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
7 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
8 |
+ |
|
9 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
10 |
+) |
|
11 |
+ |
|
12 |
+func mockDeploymentConfig(namespace, name string) *deployapi.DeploymentConfig { |
|
13 |
+ return &deployapi.DeploymentConfig{ObjectMeta: kapi.ObjectMeta{Namespace: namespace, Name: name}} |
|
14 |
+} |
|
15 |
+ |
|
16 |
+func withSize(item *kapi.ReplicationController, replicas int) *kapi.ReplicationController { |
|
17 |
+ item.Spec.Replicas = replicas |
|
18 |
+ item.Status.Replicas = replicas |
|
19 |
+ return item |
|
20 |
+} |
|
21 |
+ |
|
22 |
+func withCreated(item *kapi.ReplicationController, creationTimestamp util.Time) *kapi.ReplicationController { |
|
23 |
+ item.CreationTimestamp = creationTimestamp |
|
24 |
+ return item |
|
25 |
+} |
|
26 |
+ |
|
27 |
+func withStatus(item *kapi.ReplicationController, status deployapi.DeploymentStatus) *kapi.ReplicationController { |
|
28 |
+ item.Annotations[deployapi.DeploymentStatusAnnotation] = string(status) |
|
29 |
+ return item |
|
30 |
+} |
|
31 |
+ |
|
32 |
+func mockDeployment(namespace, name string, deploymentConfig *deployapi.DeploymentConfig) *kapi.ReplicationController { |
|
33 |
+ item := &kapi.ReplicationController{ObjectMeta: kapi.ObjectMeta{Namespace: namespace, Name: name, Annotations: map[string]string{}}} |
|
34 |
+ if deploymentConfig != nil { |
|
35 |
+ item.Annotations[deployapi.DeploymentConfigAnnotation] = deploymentConfig.Name |
|
36 |
+ } |
|
37 |
+ item.Annotations[deployapi.DeploymentStatusAnnotation] = string(deployapi.DeploymentStatusNew) |
|
38 |
+ return item |
|
39 |
+} |
|
40 |
+ |
|
41 |
+func TestDeploymentByDeploymentConfigIndexFunc(t *testing.T) { |
|
42 |
+ config := mockDeploymentConfig("a", "b") |
|
43 |
+ deployment := mockDeployment("a", "c", config) |
|
44 |
+ actualKey, err := DeploymentByDeploymentConfigIndexFunc(deployment) |
|
45 |
+ if err != nil { |
|
46 |
+ t.Errorf("Unexpected error %v", err) |
|
47 |
+ } |
|
48 |
+ expectedKey := "a/b" |
|
49 |
+ if actualKey != expectedKey { |
|
50 |
+ t.Errorf("expected %v, actual %v", expectedKey, actualKey) |
|
51 |
+ } |
|
52 |
+ deploymentWithNoConfig := &kapi.ReplicationController{} |
|
53 |
+ actualKey, err = DeploymentByDeploymentConfigIndexFunc(deploymentWithNoConfig) |
|
54 |
+ if err != nil { |
|
55 |
+ t.Errorf("Unexpected error %v", err) |
|
56 |
+ } |
|
57 |
+ expectedKey = "orphan" |
|
58 |
+ if actualKey != expectedKey { |
|
59 |
+ t.Errorf("expected %v, actual %v", expectedKey, actualKey) |
|
60 |
+ } |
|
61 |
+} |
|
62 |
+ |
|
63 |
+func TestFilterBeforePredicate(t *testing.T) { |
|
64 |
+ youngerThan := time.Hour |
|
65 |
+ now := util.Now() |
|
66 |
+ old := util.NewTime(now.Time.Add(-1 * youngerThan)) |
|
67 |
+ items := []*kapi.ReplicationController{} |
|
68 |
+ items = append(items, withCreated(mockDeployment("a", "old", nil), old)) |
|
69 |
+ items = append(items, withCreated(mockDeployment("a", "new", nil), now)) |
|
70 |
+ filter := &andFilter{ |
|
71 |
+ filterPredicates: []FilterPredicate{NewFilterBeforePredicate(youngerThan)}, |
|
72 |
+ } |
|
73 |
+ result := filter.Filter(items) |
|
74 |
+ if len(result) != 1 { |
|
75 |
+ t.Errorf("Unexpected number of results") |
|
76 |
+ } |
|
77 |
+ if expected, actual := "old", result[0].Name; expected != actual { |
|
78 |
+ t.Errorf("expected %v, actual %v", expected, actual) |
|
79 |
+ } |
|
80 |
+} |
|
81 |
+ |
|
82 |
+func TestEmptyDataSet(t *testing.T) { |
|
83 |
+ deployments := []*kapi.ReplicationController{} |
|
84 |
+ deploymentConfigs := []*deployapi.DeploymentConfig{} |
|
85 |
+ dataSet := NewDataSet(deploymentConfigs, deployments) |
|
86 |
+ _, exists, err := dataSet.GetDeploymentConfig(&kapi.ReplicationController{}) |
|
87 |
+ if exists || err != nil { |
|
88 |
+ t.Errorf("Unexpected result %v, %v", exists, err) |
|
89 |
+ } |
|
90 |
+ deploymentConfigResults, err := dataSet.ListDeploymentConfigs() |
|
91 |
+ if err != nil { |
|
92 |
+ t.Errorf("Unexpected result %v", err) |
|
93 |
+ } |
|
94 |
+ if len(deploymentConfigResults) != 0 { |
|
95 |
+ t.Errorf("Unexpected result %v", deploymentConfigResults) |
|
96 |
+ } |
|
97 |
+ deploymentResults, err := dataSet.ListDeployments() |
|
98 |
+ if err != nil { |
|
99 |
+ t.Errorf("Unexpected result %v", err) |
|
100 |
+ } |
|
101 |
+ if len(deploymentResults) != 0 { |
|
102 |
+ t.Errorf("Unexpected result %v", deploymentResults) |
|
103 |
+ } |
|
104 |
+ deploymentResults, err = dataSet.ListDeploymentsByDeploymentConfig(&deployapi.DeploymentConfig{}) |
|
105 |
+ if err != nil { |
|
106 |
+ t.Errorf("Unexpected result %v", err) |
|
107 |
+ } |
|
108 |
+ if len(deploymentResults) != 0 { |
|
109 |
+ t.Errorf("Unexpected result %v", deploymentResults) |
|
110 |
+ } |
|
111 |
+} |
|
112 |
+ |
|
113 |
+func TestPopulatedDataSet(t *testing.T) { |
|
114 |
+ deploymentConfigs := []*deployapi.DeploymentConfig{ |
|
115 |
+ mockDeploymentConfig("a", "deployment-config-1"), |
|
116 |
+ mockDeploymentConfig("b", "deployment-config-2"), |
|
117 |
+ } |
|
118 |
+ deployments := []*kapi.ReplicationController{ |
|
119 |
+ mockDeployment("a", "deployment-1", deploymentConfigs[0]), |
|
120 |
+ mockDeployment("a", "deployment-2", deploymentConfigs[0]), |
|
121 |
+ mockDeployment("b", "deployment-3", deploymentConfigs[1]), |
|
122 |
+ mockDeployment("c", "deployment-4", nil), |
|
123 |
+ } |
|
124 |
+ dataSet := NewDataSet(deploymentConfigs, deployments) |
|
125 |
+ for _, deployment := range deployments { |
|
126 |
+ deploymentConfig, exists, err := dataSet.GetDeploymentConfig(deployment) |
|
127 |
+ config, hasConfig := deployment.Annotations[deployapi.DeploymentConfigAnnotation] |
|
128 |
+ if hasConfig { |
|
129 |
+ if err != nil { |
|
130 |
+ t.Errorf("Item %v, unexpected error: %v", deployment, err) |
|
131 |
+ } |
|
132 |
+ if !exists { |
|
133 |
+ t.Errorf("Item %v, unexpected result: %v", deployment, exists) |
|
134 |
+ } |
|
135 |
+ if expected, actual := config, deploymentConfig.Name; expected != actual { |
|
136 |
+ t.Errorf("expected %v, actual %v", expected, actual) |
|
137 |
+ } |
|
138 |
+ if expected, actual := deployment.Namespace, deploymentConfig.Namespace; expected != actual { |
|
139 |
+ t.Errorf("expected %v, actual %v", expected, actual) |
|
140 |
+ } |
|
141 |
+ } else { |
|
142 |
+ if err != nil { |
|
143 |
+ t.Errorf("Item %v, unexpected error: %v", deployment, err) |
|
144 |
+ } |
|
145 |
+ if exists { |
|
146 |
+ t.Errorf("Item %v, unexpected result: %v", deployment, exists) |
|
147 |
+ } |
|
148 |
+ } |
|
149 |
+ } |
|
150 |
+ expectedNames := util.NewStringSet("deployment-1", "deployment-2") |
|
151 |
+ deploymentResults, err := dataSet.ListDeploymentsByDeploymentConfig(deploymentConfigs[0]) |
|
152 |
+ if err != nil { |
|
153 |
+ t.Errorf("Unexpected result %v", err) |
|
154 |
+ } |
|
155 |
+ if len(deploymentResults) != len(expectedNames) { |
|
156 |
+ t.Errorf("Unexpected result %v", deploymentResults) |
|
157 |
+ } |
|
158 |
+ for _, deployment := range deploymentResults { |
|
159 |
+ if !expectedNames.Has(deployment.Name) { |
|
160 |
+ t.Errorf("Unexpected name: %v", deployment.Name) |
|
161 |
+ } |
|
162 |
+ } |
|
163 |
+} |
0 | 164 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,69 @@ |
0 |
+package prune |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "time" |
|
4 |
+ |
|
5 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
6 |
+ |
|
7 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
8 |
+) |
|
9 |
+ |
|
10 |
+// PruneFunc is a function that is invoked for each item during Prune |
|
11 |
+type PruneFunc func(item *kapi.ReplicationController) error |
|
12 |
+ |
|
13 |
+// PruneTask is an object that knows how to execute a single iteration of a Prune |
|
14 |
+type PruneTasker interface { |
|
15 |
+ PruneTask() error |
|
16 |
+} |
|
17 |
+ |
|
18 |
+// pruneTask is an object that knows how to prune a data set |
|
19 |
+type pruneTask struct { |
|
20 |
+ resolver Resolver |
|
21 |
+ handler PruneFunc |
|
22 |
+} |
|
23 |
+ |
|
24 |
+// NewPruneTasker returns a PruneTasker over specified data using specified flags |
|
25 |
+// keepYoungerThan will filter out all objects from prune data set that are younger than the specified time duration |
|
26 |
+// orphans if true will include inactive orphan deployments in candidate prune set |
|
27 |
+// keepComplete is per DeploymentConfig how many of the most recent deployments should be preserved |
|
28 |
+// keepFailed is per DeploymentConfig how many of the most recent failed deployments should be preserved |
|
29 |
+func NewPruneTasker(deploymentConfigs []*deployapi.DeploymentConfig, deployments []*kapi.ReplicationController, keepYoungerThan time.Duration, orphans bool, keepComplete int, keepFailed int, handler PruneFunc) PruneTasker { |
|
30 |
+ filter := &andFilter{ |
|
31 |
+ filterPredicates: []FilterPredicate{ |
|
32 |
+ FilterDeploymentsPredicate, |
|
33 |
+ FilterZeroReplicaSize, |
|
34 |
+ NewFilterBeforePredicate(keepYoungerThan), |
|
35 |
+ }, |
|
36 |
+ } |
|
37 |
+ deployments = filter.Filter(deployments) |
|
38 |
+ dataSet := NewDataSet(deploymentConfigs, deployments) |
|
39 |
+ |
|
40 |
+ resolvers := []Resolver{} |
|
41 |
+ if orphans { |
|
42 |
+ inactiveDeploymentStatus := []deployapi.DeploymentStatus{ |
|
43 |
+ deployapi.DeploymentStatusComplete, |
|
44 |
+ deployapi.DeploymentStatusFailed, |
|
45 |
+ } |
|
46 |
+ resolvers = append(resolvers, NewOrphanDeploymentResolver(dataSet, inactiveDeploymentStatus)) |
|
47 |
+ } |
|
48 |
+ resolvers = append(resolvers, NewPerDeploymentConfigResolver(dataSet, keepComplete, keepFailed)) |
|
49 |
+ return &pruneTask{ |
|
50 |
+ resolver: &mergeResolver{resolvers: resolvers}, |
|
51 |
+ handler: handler, |
|
52 |
+ } |
|
53 |
+} |
|
54 |
+ |
|
55 |
+// PruneTask will visit each item in the prunable set and invoke the associated handler |
|
56 |
+func (t *pruneTask) PruneTask() error { |
|
57 |
+ deployments, err := t.resolver.Resolve() |
|
58 |
+ if err != nil { |
|
59 |
+ return err |
|
60 |
+ } |
|
61 |
+ for _, deployment := range deployments { |
|
62 |
+ err = t.handler(deployment) |
|
63 |
+ if err != nil { |
|
64 |
+ return err |
|
65 |
+ } |
|
66 |
+ } |
|
67 |
+ return nil |
|
68 |
+} |
0 | 69 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,103 @@ |
0 |
+package prune |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "sort" |
|
4 |
+ "testing" |
|
5 |
+ "time" |
|
6 |
+ |
|
7 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
8 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
9 |
+ |
|
10 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
11 |
+) |
|
12 |
+ |
|
13 |
+type mockPruneRecorder struct { |
|
14 |
+ set util.StringSet |
|
15 |
+ err error |
|
16 |
+} |
|
17 |
+ |
|
18 |
+func (m *mockPruneRecorder) Handler(deployment *kapi.ReplicationController) error { |
|
19 |
+ m.set.Insert(deployment.Name) |
|
20 |
+ return m.err |
|
21 |
+} |
|
22 |
+ |
|
23 |
+func (m *mockPruneRecorder) Verify(t *testing.T, expected util.StringSet) { |
|
24 |
+ if len(m.set) != len(expected) || !m.set.HasAll(expected.List()...) { |
|
25 |
+ expectedValues := expected.List() |
|
26 |
+ actualValues := m.set.List() |
|
27 |
+ sort.Strings(expectedValues) |
|
28 |
+ sort.Strings(actualValues) |
|
29 |
+ t.Errorf("expected \n\t%v\n, actual \n\t%v\n", expectedValues, actualValues) |
|
30 |
+ } |
|
31 |
+} |
|
32 |
+ |
|
33 |
+func TestPruneTask(t *testing.T) { |
|
34 |
+ deploymentStatusOptions := []deployapi.DeploymentStatus{ |
|
35 |
+ deployapi.DeploymentStatusComplete, |
|
36 |
+ deployapi.DeploymentStatusFailed, |
|
37 |
+ deployapi.DeploymentStatusNew, |
|
38 |
+ deployapi.DeploymentStatusPending, |
|
39 |
+ deployapi.DeploymentStatusRunning, |
|
40 |
+ } |
|
41 |
+ deploymentStatusFilter := []deployapi.DeploymentStatus{ |
|
42 |
+ deployapi.DeploymentStatusComplete, |
|
43 |
+ deployapi.DeploymentStatusFailed, |
|
44 |
+ } |
|
45 |
+ deploymentStatusFilterSet := util.StringSet{} |
|
46 |
+ for _, deploymentStatus := range deploymentStatusFilter { |
|
47 |
+ deploymentStatusFilterSet.Insert(string(deploymentStatus)) |
|
48 |
+ } |
|
49 |
+ |
|
50 |
+ for _, orphans := range []bool{true, false} { |
|
51 |
+ for _, deploymentStatusOption := range deploymentStatusOptions { |
|
52 |
+ keepYoungerThan := time.Hour |
|
53 |
+ |
|
54 |
+ now := util.Now() |
|
55 |
+ old := util.NewTime(now.Time.Add(-1 * keepYoungerThan)) |
|
56 |
+ |
|
57 |
+ deploymentConfigs := []*deployapi.DeploymentConfig{} |
|
58 |
+ deployments := []*kapi.ReplicationController{} |
|
59 |
+ |
|
60 |
+ deploymentConfig := mockDeploymentConfig("a", "deployment-config") |
|
61 |
+ deploymentConfigs = append(deploymentConfigs, deploymentConfig) |
|
62 |
+ |
|
63 |
+ deployments = append(deployments, withCreated(withStatus(mockDeployment("a", "build-1", deploymentConfig), deploymentStatusOption), now)) |
|
64 |
+ deployments = append(deployments, withCreated(withStatus(mockDeployment("a", "build-2", deploymentConfig), deploymentStatusOption), old)) |
|
65 |
+ deployments = append(deployments, withSize(withCreated(withStatus(mockDeployment("a", "build-3-with-replicas", deploymentConfig), deploymentStatusOption), old), 4)) |
|
66 |
+ deployments = append(deployments, withCreated(withStatus(mockDeployment("a", "orphan-build-1", nil), deploymentStatusOption), now)) |
|
67 |
+ deployments = append(deployments, withCreated(withStatus(mockDeployment("a", "orphan-build-2", nil), deploymentStatusOption), old)) |
|
68 |
+ deployments = append(deployments, withSize(withCreated(withStatus(mockDeployment("a", "orphan-build-3-with-replicas", nil), deploymentStatusOption), old), 4)) |
|
69 |
+ |
|
70 |
+ keepComplete := 1 |
|
71 |
+ keepFailed := 1 |
|
72 |
+ expectedValues := util.StringSet{} |
|
73 |
+ filter := &andFilter{ |
|
74 |
+ filterPredicates: []FilterPredicate{ |
|
75 |
+ FilterDeploymentsPredicate, |
|
76 |
+ FilterZeroReplicaSize, |
|
77 |
+ NewFilterBeforePredicate(keepYoungerThan), |
|
78 |
+ }, |
|
79 |
+ } |
|
80 |
+ dataSet := NewDataSet(deploymentConfigs, filter.Filter(deployments)) |
|
81 |
+ resolver := NewPerDeploymentConfigResolver(dataSet, keepComplete, keepFailed) |
|
82 |
+ if orphans { |
|
83 |
+ resolver = &mergeResolver{ |
|
84 |
+ resolvers: []Resolver{resolver, NewOrphanDeploymentResolver(dataSet, deploymentStatusFilter)}, |
|
85 |
+ } |
|
86 |
+ } |
|
87 |
+ expectedDeployments, err := resolver.Resolve() |
|
88 |
+ for _, item := range expectedDeployments { |
|
89 |
+ expectedValues.Insert(item.Name) |
|
90 |
+ } |
|
91 |
+ |
|
92 |
+ recorder := &mockPruneRecorder{set: util.StringSet{}} |
|
93 |
+ task := NewPruneTasker(deploymentConfigs, deployments, keepYoungerThan, orphans, keepComplete, keepFailed, recorder.Handler) |
|
94 |
+ err = task.PruneTask() |
|
95 |
+ if err != nil { |
|
96 |
+ t.Errorf("Unexpected error %v", err) |
|
97 |
+ } |
|
98 |
+ recorder.Verify(t, expectedValues) |
|
99 |
+ } |
|
100 |
+ } |
|
101 |
+ |
|
102 |
+} |
0 | 103 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,125 @@ |
0 |
+package prune |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "sort" |
|
4 |
+ |
|
5 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
6 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
7 |
+ |
|
8 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
9 |
+ deployutil "github.com/openshift/origin/pkg/deploy/util" |
|
10 |
+) |
|
11 |
+ |
|
12 |
+// Resolver knows how to resolve the set of candidate objects to prune |
|
13 |
+type Resolver interface { |
|
14 |
+ Resolve() ([]*kapi.ReplicationController, error) |
|
15 |
+} |
|
16 |
+ |
|
17 |
+// mergeResolver merges the set of results from multiple resolvers |
|
18 |
+type mergeResolver struct { |
|
19 |
+ resolvers []Resolver |
|
20 |
+} |
|
21 |
+ |
|
22 |
+func (m *mergeResolver) Resolve() ([]*kapi.ReplicationController, error) { |
|
23 |
+ results := []*kapi.ReplicationController{} |
|
24 |
+ for _, resolver := range m.resolvers { |
|
25 |
+ items, err := resolver.Resolve() |
|
26 |
+ if err != nil { |
|
27 |
+ return nil, err |
|
28 |
+ } |
|
29 |
+ results = append(results, items...) |
|
30 |
+ } |
|
31 |
+ return results, nil |
|
32 |
+} |
|
33 |
+ |
|
34 |
+// NewOrphanDeploymentResolver returns a Resolver that matches objects with no associated DeploymentConfig and has a DeploymentStatus in filter |
|
35 |
+func NewOrphanDeploymentResolver(dataSet DataSet, deploymentStatusFilter []deployapi.DeploymentStatus) Resolver { |
|
36 |
+ filter := util.NewStringSet() |
|
37 |
+ for _, deploymentStatus := range deploymentStatusFilter { |
|
38 |
+ filter.Insert(string(deploymentStatus)) |
|
39 |
+ } |
|
40 |
+ return &orphanDeploymentResolver{ |
|
41 |
+ dataSet: dataSet, |
|
42 |
+ deploymentStatusFilter: filter, |
|
43 |
+ } |
|
44 |
+} |
|
45 |
+ |
|
46 |
+// orphanDeploymentResolver resolves orphan deployments that match the specified filter |
|
47 |
+type orphanDeploymentResolver struct { |
|
48 |
+ dataSet DataSet |
|
49 |
+ deploymentStatusFilter util.StringSet |
|
50 |
+} |
|
51 |
+ |
|
52 |
+// Resolve the matching set of objects |
|
53 |
+func (o *orphanDeploymentResolver) Resolve() ([]*kapi.ReplicationController, error) { |
|
54 |
+ deployments, err := o.dataSet.ListDeployments() |
|
55 |
+ if err != nil { |
|
56 |
+ return nil, err |
|
57 |
+ } |
|
58 |
+ |
|
59 |
+ results := []*kapi.ReplicationController{} |
|
60 |
+ for _, deployment := range deployments { |
|
61 |
+ deploymentStatus := deployutil.DeploymentStatusFor(deployment) |
|
62 |
+ if !o.deploymentStatusFilter.Has(string(deploymentStatus)) { |
|
63 |
+ continue |
|
64 |
+ } |
|
65 |
+ _, exists, _ := o.dataSet.GetDeploymentConfig(deployment) |
|
66 |
+ if !exists { |
|
67 |
+ results = append(results, deployment) |
|
68 |
+ } |
|
69 |
+ } |
|
70 |
+ return results, nil |
|
71 |
+} |
|
72 |
+ |
|
73 |
+type perDeploymentConfigResolver struct { |
|
74 |
+ dataSet DataSet |
|
75 |
+ keepComplete int |
|
76 |
+ keepFailed int |
|
77 |
+} |
|
78 |
+ |
|
79 |
+// NewPerDeploymentConfigResolver returns a Resolver that selects items to prune per config |
|
80 |
+func NewPerDeploymentConfigResolver(dataSet DataSet, keepComplete int, keepFailed int) Resolver { |
|
81 |
+ return &perDeploymentConfigResolver{ |
|
82 |
+ dataSet: dataSet, |
|
83 |
+ keepComplete: keepComplete, |
|
84 |
+ keepFailed: keepFailed, |
|
85 |
+ } |
|
86 |
+} |
|
87 |
+ |
|
88 |
+func (o *perDeploymentConfigResolver) Resolve() ([]*kapi.ReplicationController, error) { |
|
89 |
+ deploymentConfigs, err := o.dataSet.ListDeploymentConfigs() |
|
90 |
+ if err != nil { |
|
91 |
+ return nil, err |
|
92 |
+ } |
|
93 |
+ |
|
94 |
+ completeStates := util.NewStringSet(string(deployapi.DeploymentStatusComplete)) |
|
95 |
+ failedStates := util.NewStringSet(string(deployapi.DeploymentStatusFailed)) |
|
96 |
+ |
|
97 |
+ results := []*kapi.ReplicationController{} |
|
98 |
+ for _, deploymentConfig := range deploymentConfigs { |
|
99 |
+ deployments, err := o.dataSet.ListDeploymentsByDeploymentConfig(deploymentConfig) |
|
100 |
+ if err != nil { |
|
101 |
+ return nil, err |
|
102 |
+ } |
|
103 |
+ |
|
104 |
+ completeDeployments, failedDeployments := []*kapi.ReplicationController{}, []*kapi.ReplicationController{} |
|
105 |
+ for _, deployment := range deployments { |
|
106 |
+ status := deployutil.DeploymentStatusFor(deployment) |
|
107 |
+ if completeStates.Has(string(status)) { |
|
108 |
+ completeDeployments = append(completeDeployments, deployment) |
|
109 |
+ } else if failedStates.Has(string(status)) { |
|
110 |
+ failedDeployments = append(failedDeployments, deployment) |
|
111 |
+ } |
|
112 |
+ } |
|
113 |
+ sort.Sort(sortableReplicationControllers(completeDeployments)) |
|
114 |
+ sort.Sort(sortableReplicationControllers(failedDeployments)) |
|
115 |
+ |
|
116 |
+ if o.keepComplete >= 0 && o.keepComplete < len(completeDeployments) { |
|
117 |
+ results = append(results, completeDeployments[o.keepComplete:]...) |
|
118 |
+ } |
|
119 |
+ if o.keepFailed >= 0 && o.keepFailed < len(failedDeployments) { |
|
120 |
+ results = append(results, failedDeployments[o.keepFailed:]...) |
|
121 |
+ } |
|
122 |
+ } |
|
123 |
+ return results, nil |
|
124 |
+} |
0 | 125 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,187 @@ |
0 |
+package prune |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "fmt" |
|
4 |
+ "sort" |
|
5 |
+ "testing" |
|
6 |
+ "time" |
|
7 |
+ |
|
8 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
9 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
10 |
+ |
|
11 |
+ deployapi "github.com/openshift/origin/pkg/deploy/api" |
|
12 |
+) |
|
13 |
+ |
|
14 |
+type mockResolver struct { |
|
15 |
+ items []*kapi.ReplicationController |
|
16 |
+ err error |
|
17 |
+} |
|
18 |
+ |
|
19 |
+func (m *mockResolver) Resolve() ([]*kapi.ReplicationController, error) { |
|
20 |
+ return m.items, m.err |
|
21 |
+} |
|
22 |
+ |
|
23 |
+func TestMergeResolver(t *testing.T) { |
|
24 |
+ resolverA := &mockResolver{ |
|
25 |
+ items: []*kapi.ReplicationController{ |
|
26 |
+ mockDeployment("a", "b", nil), |
|
27 |
+ }, |
|
28 |
+ } |
|
29 |
+ resolverB := &mockResolver{ |
|
30 |
+ items: []*kapi.ReplicationController{ |
|
31 |
+ mockDeployment("c", "d", nil), |
|
32 |
+ }, |
|
33 |
+ } |
|
34 |
+ resolver := &mergeResolver{resolvers: []Resolver{resolverA, resolverB}} |
|
35 |
+ results, err := resolver.Resolve() |
|
36 |
+ if err != nil { |
|
37 |
+ t.Errorf("Unexpected error %v", err) |
|
38 |
+ } |
|
39 |
+ if len(results) != 2 { |
|
40 |
+ t.Errorf("Unexpected results %v", results) |
|
41 |
+ } |
|
42 |
+ expectedNames := util.NewStringSet("b", "d") |
|
43 |
+ for _, item := range results { |
|
44 |
+ if !expectedNames.Has(item.Name) { |
|
45 |
+ t.Errorf("Unexpected name %v", item.Name) |
|
46 |
+ } |
|
47 |
+ } |
|
48 |
+} |
|
49 |
+ |
|
50 |
+func TestOrphanDeploymentResolver(t *testing.T) { |
|
51 |
+ activeDeploymentConfig := mockDeploymentConfig("a", "active-deployment-config") |
|
52 |
+ inactiveDeploymentConfig := mockDeploymentConfig("a", "inactive-deployment-config") |
|
53 |
+ |
|
54 |
+ deploymentConfigs := []*deployapi.DeploymentConfig{activeDeploymentConfig} |
|
55 |
+ deployments := []*kapi.ReplicationController{} |
|
56 |
+ |
|
57 |
+ expectedNames := util.StringSet{} |
|
58 |
+ deploymentStatusOptions := []deployapi.DeploymentStatus{ |
|
59 |
+ deployapi.DeploymentStatusComplete, |
|
60 |
+ deployapi.DeploymentStatusFailed, |
|
61 |
+ deployapi.DeploymentStatusNew, |
|
62 |
+ deployapi.DeploymentStatusPending, |
|
63 |
+ deployapi.DeploymentStatusRunning, |
|
64 |
+ } |
|
65 |
+ |
|
66 |
+ deploymentStatusFilter := []deployapi.DeploymentStatus{ |
|
67 |
+ deployapi.DeploymentStatusComplete, |
|
68 |
+ deployapi.DeploymentStatusFailed, |
|
69 |
+ } |
|
70 |
+ deploymentStatusFilterSet := util.StringSet{} |
|
71 |
+ for _, deploymentStatus := range deploymentStatusFilter { |
|
72 |
+ deploymentStatusFilterSet.Insert(string(deploymentStatus)) |
|
73 |
+ } |
|
74 |
+ |
|
75 |
+ for _, deploymentStatusOption := range deploymentStatusOptions { |
|
76 |
+ deployments = append(deployments, withStatus(mockDeployment("a", string(deploymentStatusOption)+"-active", activeDeploymentConfig), deploymentStatusOption)) |
|
77 |
+ deployments = append(deployments, withStatus(mockDeployment("a", string(deploymentStatusOption)+"-inactive", inactiveDeploymentConfig), deploymentStatusOption)) |
|
78 |
+ deployments = append(deployments, withStatus(mockDeployment("a", string(deploymentStatusOption)+"-orphan", nil), deploymentStatusOption)) |
|
79 |
+ if deploymentStatusFilterSet.Has(string(deploymentStatusOption)) { |
|
80 |
+ expectedNames.Insert(string(deploymentStatusOption) + "-inactive") |
|
81 |
+ expectedNames.Insert(string(deploymentStatusOption) + "-orphan") |
|
82 |
+ } |
|
83 |
+ } |
|
84 |
+ |
|
85 |
+ dataSet := NewDataSet(deploymentConfigs, deployments) |
|
86 |
+ resolver := NewOrphanDeploymentResolver(dataSet, deploymentStatusFilter) |
|
87 |
+ results, err := resolver.Resolve() |
|
88 |
+ if err != nil { |
|
89 |
+ t.Errorf("Unexpected error %v", err) |
|
90 |
+ } |
|
91 |
+ foundNames := util.StringSet{} |
|
92 |
+ for _, result := range results { |
|
93 |
+ foundNames.Insert(result.Name) |
|
94 |
+ } |
|
95 |
+ if len(foundNames) != len(expectedNames) || !expectedNames.HasAll(foundNames.List()...) { |
|
96 |
+ t.Errorf("expected %v, actual %v", expectedNames, foundNames) |
|
97 |
+ } |
|
98 |
+} |
|
99 |
+ |
|
100 |
+func TestPerDeploymentConfigResolver(t *testing.T) { |
|
101 |
+ deploymentStatusOptions := []deployapi.DeploymentStatus{ |
|
102 |
+ deployapi.DeploymentStatusComplete, |
|
103 |
+ deployapi.DeploymentStatusFailed, |
|
104 |
+ deployapi.DeploymentStatusNew, |
|
105 |
+ deployapi.DeploymentStatusPending, |
|
106 |
+ deployapi.DeploymentStatusRunning, |
|
107 |
+ } |
|
108 |
+ deploymentConfigs := []*deployapi.DeploymentConfig{ |
|
109 |
+ mockDeploymentConfig("a", "deployment-config-1"), |
|
110 |
+ mockDeploymentConfig("b", "deployment-config-2"), |
|
111 |
+ } |
|
112 |
+ deploymentsPerStatus := 100 |
|
113 |
+ deployments := []*kapi.ReplicationController{} |
|
114 |
+ for _, deploymentConfig := range deploymentConfigs { |
|
115 |
+ for _, deploymentStatusOption := range deploymentStatusOptions { |
|
116 |
+ for i := 0; i < deploymentsPerStatus; i++ { |
|
117 |
+ deployment := withStatus(mockDeployment(deploymentConfig.Namespace, fmt.Sprintf("%v-%v-%v", deploymentConfig.Name, deploymentStatusOption, i), deploymentConfig), deploymentStatusOption) |
|
118 |
+ deployments = append(deployments, deployment) |
|
119 |
+ } |
|
120 |
+ } |
|
121 |
+ } |
|
122 |
+ |
|
123 |
+ now := util.Now() |
|
124 |
+ for i := range deployments { |
|
125 |
+ creationTimestamp := util.NewTime(now.Time.Add(-1 * time.Duration(i) * time.Hour)) |
|
126 |
+ deployments[i].CreationTimestamp = creationTimestamp |
|
127 |
+ } |
|
128 |
+ |
|
129 |
+ // test number to keep at varying ranges |
|
130 |
+ for keep := 0; keep < deploymentsPerStatus*2; keep++ { |
|
131 |
+ dataSet := NewDataSet(deploymentConfigs, deployments) |
|
132 |
+ |
|
133 |
+ expectedNames := util.StringSet{} |
|
134 |
+ deploymentCompleteStatusFilterSet := util.NewStringSet(string(deployapi.DeploymentStatusComplete)) |
|
135 |
+ deploymentFailedStatusFilterSet := util.NewStringSet(string(deployapi.DeploymentStatusFailed)) |
|
136 |
+ |
|
137 |
+ for _, deploymentConfig := range deploymentConfigs { |
|
138 |
+ deploymentItems, err := dataSet.ListDeploymentsByDeploymentConfig(deploymentConfig) |
|
139 |
+ if err != nil { |
|
140 |
+ t.Errorf("Unexpected err %v", err) |
|
141 |
+ } |
|
142 |
+ completedDeployments, failedDeployments := []*kapi.ReplicationController{}, []*kapi.ReplicationController{} |
|
143 |
+ for _, deployment := range deploymentItems { |
|
144 |
+ status := deployment.Annotations[deployapi.DeploymentStatusAnnotation] |
|
145 |
+ if deploymentCompleteStatusFilterSet.Has(status) { |
|
146 |
+ completedDeployments = append(completedDeployments, deployment) |
|
147 |
+ } else if deploymentFailedStatusFilterSet.Has(status) { |
|
148 |
+ failedDeployments = append(failedDeployments, deployment) |
|
149 |
+ } |
|
150 |
+ } |
|
151 |
+ sort.Sort(sortableReplicationControllers(completedDeployments)) |
|
152 |
+ sort.Sort(sortableReplicationControllers(failedDeployments)) |
|
153 |
+ purgeCompleted := []*kapi.ReplicationController{} |
|
154 |
+ purgeFailed := []*kapi.ReplicationController{} |
|
155 |
+ if keep >= 0 && keep < len(completedDeployments) { |
|
156 |
+ purgeCompleted = completedDeployments[keep:] |
|
157 |
+ } |
|
158 |
+ if keep >= 0 && keep < len(failedDeployments) { |
|
159 |
+ purgeFailed = failedDeployments[keep:] |
|
160 |
+ } |
|
161 |
+ for _, deployment := range purgeCompleted { |
|
162 |
+ expectedNames.Insert(deployment.Name) |
|
163 |
+ } |
|
164 |
+ for _, deployment := range purgeFailed { |
|
165 |
+ expectedNames.Insert(deployment.Name) |
|
166 |
+ } |
|
167 |
+ } |
|
168 |
+ |
|
169 |
+ resolver := NewPerDeploymentConfigResolver(dataSet, keep, keep) |
|
170 |
+ results, err := resolver.Resolve() |
|
171 |
+ if err != nil { |
|
172 |
+ t.Errorf("Unexpected error %v", err) |
|
173 |
+ } |
|
174 |
+ foundNames := util.StringSet{} |
|
175 |
+ for _, result := range results { |
|
176 |
+ foundNames.Insert(result.Name) |
|
177 |
+ } |
|
178 |
+ if len(foundNames) != len(expectedNames) || !expectedNames.HasAll(foundNames.List()...) { |
|
179 |
+ expectedValues := expectedNames.List() |
|
180 |
+ actualValues := foundNames.List() |
|
181 |
+ sort.Strings(expectedValues) |
|
182 |
+ sort.Strings(actualValues) |
|
183 |
+ t.Errorf("keep %v\n, expected \n\t%v\n, actual \n\t%v\n", keep, expectedValues, actualValues) |
|
184 |
+ } |
|
185 |
+ } |
|
186 |
+} |
0 | 187 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,22 @@ |
0 |
+package prune |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
4 |
+) |
|
5 |
+ |
|
6 |
+// sortableReplicationControllers supports sorting ReplicationController items by most recently created |
|
7 |
+type sortableReplicationControllers []*kapi.ReplicationController |
|
8 |
+ |
|
9 |
+func (s sortableReplicationControllers) Len() int { |
|
10 |
+ return len(s) |
|
11 |
+} |
|
12 |
+ |
|
13 |
+func (s sortableReplicationControllers) Less(i, j int) bool { |
|
14 |
+ return !s[i].CreationTimestamp.Before(s[j].CreationTimestamp) |
|
15 |
+} |
|
16 |
+ |
|
17 |
+func (s sortableReplicationControllers) Swap(i, j int) { |
|
18 |
+ t := s[i] |
|
19 |
+ s[i] = s[j] |
|
20 |
+ s[j] = t |
|
21 |
+} |
0 | 22 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,37 @@ |
0 |
+package prune |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "sort" |
|
4 |
+ "testing" |
|
5 |
+ "time" |
|
6 |
+ |
|
7 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
8 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
9 |
+) |
|
10 |
+ |
|
11 |
+// TestSort verifies that builds are sorted by most recently created |
|
12 |
+func TestSort(t *testing.T) { |
|
13 |
+ present := util.Now() |
|
14 |
+ past := util.NewTime(present.Time.Add(-1 * time.Minute)) |
|
15 |
+ controllers := []*kapi.ReplicationController{ |
|
16 |
+ { |
|
17 |
+ ObjectMeta: kapi.ObjectMeta{ |
|
18 |
+ Name: "past", |
|
19 |
+ CreationTimestamp: past, |
|
20 |
+ }, |
|
21 |
+ }, |
|
22 |
+ { |
|
23 |
+ ObjectMeta: kapi.ObjectMeta{ |
|
24 |
+ Name: "present", |
|
25 |
+ CreationTimestamp: present, |
|
26 |
+ }, |
|
27 |
+ }, |
|
28 |
+ } |
|
29 |
+ sort.Sort(sortableReplicationControllers(controllers)) |
|
30 |
+ if controllers[0].Name != "present" { |
|
31 |
+ t.Errorf("Unexpected sort order") |
|
32 |
+ } |
|
33 |
+ if controllers[1].Name != "past" { |
|
34 |
+ t.Errorf("Unexpected sort order") |
|
35 |
+ } |
|
36 |
+} |