Browse code

Enable extensions storage for batch/autoscaling

Jordan Liggitt authored on 2016/03/22 05:54:17
Showing 3 changed files
... ...
@@ -18,7 +18,6 @@ import (
18 18
 	"k8s.io/kubernetes/pkg/admission"
19 19
 	kapi "k8s.io/kubernetes/pkg/api"
20 20
 	"k8s.io/kubernetes/pkg/api/unversioned"
21
-	"k8s.io/kubernetes/pkg/apimachinery/registered"
22 21
 	"k8s.io/kubernetes/pkg/apis/extensions"
23 22
 	"k8s.io/kubernetes/pkg/apiserver"
24 23
 	"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
... ...
@@ -205,20 +204,20 @@ func BuildKubernetesMasterConfig(options configapi.MasterConfig, requestContextM
205 205
 		storageVersions[configapi.APIGroupKube] = options.EtcdStorageConfig.KubernetesStorageVersion
206 206
 	}
207 207
 
208
-	// TODO: also need to enable this if batch or autoscaling is enabled and doesn't have a storage version set
209
-	enabledExtensionsVersions := configapi.GetEnabledAPIVersionsForGroup(*options.KubernetesMasterConfig, configapi.APIGroupExtensions)
210
-	if len(enabledExtensionsVersions) > 0 {
211
-		groupMeta, err := registered.Group(configapi.APIGroupExtensions)
212
-		if err != nil {
213
-			return nil, fmt.Errorf("Error setting up Kubernetes extensions server storage: %v", err)
214
-		}
215
-		// TODO expose storage version options for api groups
216
-		databaseStorage, err := NewEtcdStorage(etcdClient, groupMeta.GroupVersion, options.EtcdStorageConfig.KubernetesStoragePrefix)
208
+	// enable this if extensions API is enabled (or batch or autoscaling, since they persist to extensions/v1beta1 for now)
209
+	// TODO: replace this with a loop over configured storage versions
210
+	extensionsEnabled := len(configapi.GetEnabledAPIVersionsForGroup(*options.KubernetesMasterConfig, configapi.APIGroupExtensions)) > 0
211
+	batchEnabled := len(configapi.GetEnabledAPIVersionsForGroup(*options.KubernetesMasterConfig, configapi.APIGroupBatch)) > 0
212
+	autoscalingEnabled := len(configapi.GetEnabledAPIVersionsForGroup(*options.KubernetesMasterConfig, configapi.APIGroupAutoscaling)) > 0
213
+	if extensionsEnabled || autoscalingEnabled || batchEnabled {
214
+		// TODO: replace this with a configured storage version for extensions once configuration exposes this
215
+		extensionsStorageVersion := unversioned.GroupVersion{Group: extensions.GroupName, Version: "v1beta1"}
216
+		databaseStorage, err := NewEtcdStorage(etcdClient, extensionsStorageVersion, options.EtcdStorageConfig.KubernetesStoragePrefix)
217 217
 		if err != nil {
218 218
 			return nil, fmt.Errorf("Error setting up Kubernetes extensions server storage: %v", err)
219 219
 		}
220 220
 		storageDestinations.AddAPIGroup(configapi.APIGroupExtensions, databaseStorage)
221
-		storageVersions[configapi.APIGroupExtensions] = unversioned.GroupVersion{Group: extensions.GroupName, Version: enabledExtensionsVersions[0]}.String()
221
+		storageVersions[configapi.APIGroupExtensions] = extensionsStorageVersion.String()
222 222
 	}
223 223
 
224 224
 	// Preserve previous behavior of using the first non-loopback address
... ...
@@ -4,7 +4,6 @@ package integration
4 4
 
5 5
 import (
6 6
 	"testing"
7
-	"time"
8 7
 
9 8
 	testutil "github.com/openshift/origin/test/util"
10 9
 	testserver "github.com/openshift/origin/test/util/server"
... ...
@@ -12,9 +11,130 @@ import (
12 12
 	kapi "k8s.io/kubernetes/pkg/api"
13 13
 	"k8s.io/kubernetes/pkg/api/errors"
14 14
 	expapi "k8s.io/kubernetes/pkg/apis/extensions"
15
-	"k8s.io/kubernetes/pkg/util/wait"
16 15
 )
17 16
 
17
+func TestExtensionsAPIDisabledAutoscaleBatchEnabled(t *testing.T) {
18
+	const projName = "ext-disabled-batch-enabled-proj"
19
+
20
+	testutil.RequireEtcd(t)
21
+	masterConfig, err := testserver.DefaultMasterOptions()
22
+	if err != nil {
23
+		t.Fatalf("unexpected error: %v", err)
24
+	}
25
+
26
+	// Disable all extensions API versions
27
+	// Leave autoscaling/batch APIs enabled
28
+	masterConfig.KubernetesMasterConfig.DisabledAPIGroupVersions = map[string][]string{"extensions": {"*"}}
29
+
30
+	clusterAdminKubeConfig, err := testserver.StartConfiguredMaster(masterConfig)
31
+	if err != nil {
32
+		t.Fatalf("unexpected error: %v", err)
33
+	}
34
+
35
+	clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig)
36
+	if err != nil {
37
+		t.Fatalf("unexpected error: %v", err)
38
+	}
39
+
40
+	clusterAdminKubeClient, err := testutil.GetClusterAdminKubeClient(clusterAdminKubeConfig)
41
+	if err != nil {
42
+		t.Fatalf("unexpected error: %v", err)
43
+	}
44
+
45
+	clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig)
46
+	if err != nil {
47
+		t.Fatalf("unexpected error: %v", err)
48
+	}
49
+
50
+	// create the containing project
51
+	if _, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, projName, "admin"); err != nil {
52
+		t.Fatalf("unexpected error creating the project: %v", err)
53
+	}
54
+	projectAdminClient, projectAdminKubeClient, _, err := testutil.GetClientForUser(*clusterAdminClientConfig, "admin")
55
+	if err != nil {
56
+		t.Fatalf("unexpected error getting project admin client: %v", err)
57
+	}
58
+	if err := testutil.WaitForPolicyUpdate(projectAdminClient, projName, "get", expapi.Resource("horizontalpodautoscalers"), true); err != nil {
59
+		t.Fatalf("unexpected error waiting for policy update: %v", err)
60
+	}
61
+
62
+	validHPA := &expapi.HorizontalPodAutoscaler{
63
+		ObjectMeta: kapi.ObjectMeta{Name: "myjob"},
64
+		Spec: expapi.HorizontalPodAutoscalerSpec{
65
+			ScaleRef:    expapi.SubresourceReference{Name: "foo", Kind: "ReplicationController", Subresource: "scale"},
66
+			MaxReplicas: 1,
67
+		},
68
+	}
69
+	validJob := &expapi.Job{
70
+		ObjectMeta: kapi.ObjectMeta{Name: "myjob"},
71
+		Spec: expapi.JobSpec{
72
+			Template: kapi.PodTemplateSpec{
73
+				Spec: kapi.PodSpec{
74
+					Containers:    []kapi.Container{{Name: "mycontainer", Image: "myimage"}},
75
+					RestartPolicy: kapi.RestartPolicyNever,
76
+				},
77
+			},
78
+		},
79
+	}
80
+
81
+	// make sure extensions API objects cannot be listed or created
82
+	if _, err := projectAdminKubeClient.Extensions().HorizontalPodAutoscalers(projName).List(kapi.ListOptions{}); !errors.IsNotFound(err) {
83
+		t.Fatalf("expected NotFound error listing HPA, got %v", err)
84
+	}
85
+	if _, err := projectAdminKubeClient.Extensions().HorizontalPodAutoscalers(projName).Create(validHPA); !errors.IsNotFound(err) {
86
+		t.Fatalf("expected NotFound error creating HPA, got %v", err)
87
+	}
88
+	if _, err := projectAdminKubeClient.Extensions().Jobs(projName).List(kapi.ListOptions{}); !errors.IsNotFound(err) {
89
+		t.Fatalf("expected NotFound error listing jobs, got %v", err)
90
+	}
91
+	if _, err := projectAdminKubeClient.Extensions().Jobs(projName).Create(validJob); !errors.IsNotFound(err) {
92
+		t.Fatalf("expected NotFound error creating job, got %v", err)
93
+	}
94
+
95
+	// make sure autoscaling and batch API objects can be listed and created
96
+	if _, err := projectAdminKubeClient.Autoscaling().HorizontalPodAutoscalers(projName).List(kapi.ListOptions{}); err != nil {
97
+		t.Fatalf("unexpected error: %#v", err)
98
+	}
99
+	if _, err := projectAdminKubeClient.Autoscaling().HorizontalPodAutoscalers(projName).Create(validHPA); err != nil {
100
+		t.Fatalf("unexpected error: %#v", err)
101
+	}
102
+	if _, err := projectAdminKubeClient.Batch().Jobs(projName).List(kapi.ListOptions{}); err != nil {
103
+		t.Fatalf("unexpected error: %#v", err)
104
+	}
105
+	if _, err := projectAdminKubeClient.Batch().Jobs(projName).Create(validJob); err != nil {
106
+		t.Fatalf("unexpected error: %#v", err)
107
+	}
108
+
109
+	// Delete the containing project
110
+	if err := testutil.DeleteAndWaitForNamespaceTermination(clusterAdminKubeClient, projName); err != nil {
111
+		t.Fatalf("unexpected error: %#v", err)
112
+	}
113
+
114
+	// recreate the containing project
115
+	if _, err := testserver.CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, projName, "admin"); err != nil {
116
+		t.Fatalf("unexpected error creating the project: %v", err)
117
+	}
118
+	projectAdminClient, projectAdminKubeClient, _, err = testutil.GetClientForUser(*clusterAdminClientConfig, "admin")
119
+	if err != nil {
120
+		t.Fatalf("unexpected error getting project admin client: %v", err)
121
+	}
122
+	if err := testutil.WaitForPolicyUpdate(projectAdminClient, projName, "get", expapi.Resource("horizontalpodautoscalers"), true); err != nil {
123
+		t.Fatalf("unexpected error waiting for policy update: %v", err)
124
+	}
125
+
126
+	// make sure the created objects got cleaned up by namespace deletion
127
+	if hpas, err := projectAdminKubeClient.Autoscaling().HorizontalPodAutoscalers(projName).List(kapi.ListOptions{}); err != nil {
128
+		t.Fatalf("unexpected error: %#v", err)
129
+	} else if len(hpas.Items) > 0 {
130
+		t.Fatalf("expected 0 HPA objects, got %#v", hpas.Items)
131
+	}
132
+	if jobs, err := projectAdminKubeClient.Batch().Jobs(projName).List(kapi.ListOptions{}); err != nil {
133
+		t.Fatalf("unexpected error: %#v", err)
134
+	} else if len(jobs.Items) > 0 {
135
+		t.Fatalf("expected 0 Job objects, got %#v", jobs.Items)
136
+	}
137
+}
138
+
18 139
 func TestExtensionsAPIDisabled(t *testing.T) {
19 140
 	const projName = "ext-disabled-proj"
20 141
 
... ...
@@ -73,17 +193,8 @@ func TestExtensionsAPIDisabled(t *testing.T) {
73 73
 		t.Fatalf("expected NotFound error creating job, got %v", err)
74 74
 	}
75 75
 
76
-	if err := clusterAdminClient.Projects().Delete(projName); err != nil {
77
-		t.Fatalf("unexpected error deleting the project: %v", err)
78
-	}
79
-	err = wait.PollImmediate(1*time.Second, 30*time.Second, func() (bool, error) {
80
-		_, err := clusterAdminKubeClient.Namespaces().Get(projName)
81
-		if errors.IsNotFound(err) {
82
-			return true, nil
83
-		}
84
-		return false, err
85
-	})
86
-	if err != nil {
87
-		t.Fatalf("unexpected error while waiting for project to delete: %v", err)
76
+	// Delete the containing project
77
+	if err := testutil.DeleteAndWaitForNamespaceTermination(clusterAdminKubeClient, projName); err != nil {
78
+		t.Fatalf("unexpected error: %#v", err)
88 79
 	}
89 80
 }
... ...
@@ -4,8 +4,12 @@ import (
4 4
 	"fmt"
5 5
 	"time"
6 6
 
7
-	"github.com/openshift/origin/pkg/cmd/util"
8 7
 	kapi "k8s.io/kubernetes/pkg/api"
8
+	kclient "k8s.io/kubernetes/pkg/client/unversioned"
9
+	"k8s.io/kubernetes/pkg/watch"
10
+
11
+	"github.com/openshift/origin/pkg/cmd/cli/cmd"
12
+	"github.com/openshift/origin/pkg/cmd/util"
9 13
 )
10 14
 
11 15
 // Namespace returns the test namespace. The default namespace is set to
... ...
@@ -33,3 +37,24 @@ func CreateNamespace(clusterAdminKubeConfig, name string) (err error) {
33 33
 	})
34 34
 	return err
35 35
 }
36
+
37
+func DeleteAndWaitForNamespaceTermination(c *kclient.Client, name string) error {
38
+	w, err := c.Namespaces().Watch(kapi.ListOptions{})
39
+	if err != nil {
40
+		return err
41
+	}
42
+	if err := c.Namespaces().Delete(name); err != nil {
43
+		return err
44
+	}
45
+	_, err = cmd.Until(30*time.Second, w, func(event watch.Event) (bool, error) {
46
+		if event.Type != watch.Deleted {
47
+			return false, nil
48
+		}
49
+		namespace, ok := event.Object.(*kapi.Namespace)
50
+		if !ok {
51
+			return false, nil
52
+		}
53
+		return namespace.Name == name, nil
54
+	})
55
+	return err
56
+}