... | ... |
@@ -20,10 +20,9 @@ import ( |
20 | 20 |
const NewProjectRecommendedName = "new-project" |
21 | 21 |
|
22 | 22 |
type NewProjectOptions struct { |
23 |
- ProjectName string |
|
24 |
- DisplayName string |
|
25 |
- Description string |
|
26 |
- NodeSelector string |
|
23 |
+ ProjectName string |
|
24 |
+ DisplayName string |
|
25 |
+ Description string |
|
27 | 26 |
|
28 | 27 |
Client client.Interface |
29 | 28 |
|
... | ... |
@@ -60,7 +59,6 @@ func NewCmdNewProject(name, fullName string, f *clientcmd.Factory, out io.Writer |
60 | 60 |
cmd.Flags().StringVar(&options.AdminUser, "admin", "", "project admin username") |
61 | 61 |
cmd.Flags().StringVar(&options.DisplayName, "display-name", "", "project display name") |
62 | 62 |
cmd.Flags().StringVar(&options.Description, "description", "", "project description") |
63 |
- cmd.Flags().StringVar(&options.NodeSelector, "node-selector", "", "Restrict pods onto nodes matching given label selector") |
|
64 | 63 |
|
65 | 64 |
return cmd |
66 | 65 |
} |
... | ... |
@@ -88,7 +86,6 @@ func (o *NewProjectOptions) Run() error { |
88 | 88 |
project.Annotations = make(map[string]string) |
89 | 89 |
project.Annotations["description"] = o.Description |
90 | 90 |
project.Annotations["displayName"] = o.DisplayName |
91 |
- project.Annotations["nodeSelector"] = o.NodeSelector |
|
92 | 91 |
project, err := o.Client.Projects().Create(project) |
93 | 92 |
if err != nil { |
94 | 93 |
return err |
... | ... |
@@ -18,10 +18,9 @@ import ( |
18 | 18 |
) |
19 | 19 |
|
20 | 20 |
type NewProjectOptions struct { |
21 |
- ProjectName string |
|
22 |
- DisplayName string |
|
23 |
- Description string |
|
24 |
- NodeSelector string |
|
21 |
+ ProjectName string |
|
22 |
+ DisplayName string |
|
23 |
+ Description string |
|
25 | 24 |
|
26 | 25 |
Client client.Interface |
27 | 26 |
|
... | ... |
@@ -41,8 +40,8 @@ After your project is created you can switch to it using %[2]s <project name>.` |
41 | 41 |
requestProject_example = ` // Create a new project with minimal information |
42 | 42 |
$ %[1]s web-team-dev |
43 | 43 |
|
44 |
- // Create a new project with a description and node selector |
|
45 |
- $ %[1]s web-team-dev --display-name="Web Team Development" --description="Development project for the web team." --node-selector="env=dev"` |
|
44 |
+ // Create a new project with a description |
|
45 |
+ $ %[1]s web-team-dev --display-name="Web Team Development" --description="Development project for the web team."` |
|
46 | 46 |
) |
47 | 47 |
|
48 | 48 |
func NewCmdRequestProject(name, fullName, oscLoginName, oscProjectName string, f *clientcmd.Factory, out io.Writer) *cobra.Command { |
... | ... |
@@ -50,7 +49,7 @@ func NewCmdRequestProject(name, fullName, oscLoginName, oscProjectName string, f |
50 | 50 |
options.Out = out |
51 | 51 |
|
52 | 52 |
cmd := &cobra.Command{ |
53 |
- Use: fmt.Sprintf("%s NAME [--display-name=DISPLAYNAME] [--description=DESCRIPTION] [--node-selector=<label selector>]", name), |
|
53 |
+ Use: fmt.Sprintf("%s NAME [--display-name=DISPLAYNAME] [--description=DESCRIPTION]", name), |
|
54 | 54 |
Short: "Request a new project", |
55 | 55 |
Long: fmt.Sprintf(requestProject_long, oscLoginName, oscProjectName), |
56 | 56 |
Example: fmt.Sprintf(requestProject_example, fullName), |
... | ... |
@@ -72,7 +71,6 @@ func NewCmdRequestProject(name, fullName, oscLoginName, oscProjectName string, f |
72 | 72 |
|
73 | 73 |
cmd.Flags().StringVar(&options.DisplayName, "display-name", "", "project display name") |
74 | 74 |
cmd.Flags().StringVar(&options.Description, "description", "", "project description") |
75 |
- cmd.Flags().StringVar(&options.NodeSelector, "node-selector", "", "Restrict pods onto nodes matching given label selector") |
|
76 | 75 |
|
77 | 76 |
return cmd |
78 | 77 |
} |
... | ... |
@@ -107,7 +105,6 @@ func (o *NewProjectOptions) Run() error { |
107 | 107 |
projectRequest.DisplayName = o.DisplayName |
108 | 108 |
projectRequest.Annotations = make(map[string]string) |
109 | 109 |
projectRequest.Annotations["description"] = o.Description |
110 |
- projectRequest.Annotations["nodeSelector"] = o.NodeSelector |
|
111 | 110 |
|
112 | 111 |
project, err := o.Client.ProjectRequests().Create(projectRequest) |
113 | 112 |
if err != nil { |
... | ... |
@@ -451,18 +451,11 @@ func (d *ProjectDescriber) Describe(namespace, name string) (string, error) { |
451 | 451 |
if err != nil { |
452 | 452 |
return "", err |
453 | 453 |
} |
454 |
- nodeSelector := "" |
|
455 |
- if len(project.ObjectMeta.Annotations) > 0 { |
|
456 |
- if ns, ok := project.ObjectMeta.Annotations["nodeSelector"]; ok { |
|
457 |
- nodeSelector = ns |
|
458 |
- } |
|
459 |
- } |
|
460 | 454 |
|
461 | 455 |
return tabbedString(func(out *tabwriter.Writer) error { |
462 | 456 |
formatMeta(out, project.ObjectMeta) |
463 | 457 |
formatString(out, "Display Name", project.Annotations["displayName"]) |
464 | 458 |
formatString(out, "Status", project.Status.Phase) |
465 |
- formatString(out, "Node Selector", nodeSelector) |
|
466 | 459 |
return nil |
467 | 460 |
}) |
468 | 461 |
} |
... | ... |
@@ -85,8 +85,6 @@ type MasterConfig struct { |
85 | 85 |
// PolicyConfig holds information about where to locate critical pieces of bootstrapping policy |
86 | 86 |
PolicyConfig PolicyConfig |
87 | 87 |
|
88 |
- // ProjectNodeSelector holds default project node label selector |
|
89 |
- ProjectNodeSelector string `json:"projectNodeSelector,omitempty"` |
|
90 | 88 |
// ProjectRequestConfig holds information about how to handle new project requests |
91 | 89 |
ProjectRequestConfig ProjectRequestConfig |
92 | 90 |
} |
... | ... |
@@ -81,8 +81,6 @@ type MasterConfig struct { |
81 | 81 |
|
82 | 82 |
PolicyConfig PolicyConfig `json:"policyConfig"` |
83 | 83 |
|
84 |
- // ProjectNodeSelector holds default project node label selector |
|
85 |
- ProjectNodeSelector string `json:"projectNodeSelector,omitempty"` |
|
86 | 84 |
// ProjectRequestConfig holds information about how to handle new project requests |
87 | 85 |
ProjectRequestConfig ProjectRequestConfig `json:"projectRequestConfig"` |
88 | 86 |
} |
... | ... |
@@ -6,7 +6,6 @@ import ( |
6 | 6 |
"net/url" |
7 | 7 |
"strings" |
8 | 8 |
|
9 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
10 | 9 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors" |
11 | 10 |
|
12 | 11 |
"github.com/openshift/origin/pkg/cmd/server/api" |
... | ... |
@@ -89,8 +88,6 @@ func ValidateMasterConfig(config *api.MasterConfig) fielderrors.ValidationErrorL |
89 | 89 |
allErrs = append(allErrs, ValidateOAuthConfig(config.OAuthConfig).Prefix("oauthConfig")...) |
90 | 90 |
} |
91 | 91 |
|
92 |
- allErrs = append(allErrs, ValidateProjectNodeSelector(config.ProjectNodeSelector)...) |
|
93 |
- |
|
94 | 92 |
allErrs = append(allErrs, ValidateServingInfo(config.ServingInfo).Prefix("servingInfo")...) |
95 | 93 |
|
96 | 94 |
allErrs = append(allErrs, ValidateProjectRequestConfig(config.ProjectRequestConfig).Prefix("projectRequestConfig")...) |
... | ... |
@@ -111,19 +108,6 @@ func ValidateEtcdStorageConfig(config api.EtcdStorageConfig) fielderrors.Validat |
111 | 111 |
return allErrs |
112 | 112 |
} |
113 | 113 |
|
114 |
-func ValidateProjectNodeSelector(nodeSelector string) fielderrors.ValidationErrorList { |
|
115 |
- allErrs := fielderrors.ValidationErrorList{} |
|
116 |
- |
|
117 |
- if len(nodeSelector) > 0 { |
|
118 |
- _, err := labels.Parse(nodeSelector) |
|
119 |
- if err != nil { |
|
120 |
- allErrs = append(allErrs, fielderrors.NewFieldInvalid("projectNodeSelector", nodeSelector, "must be a valid label selector")) |
|
121 |
- } |
|
122 |
- } |
|
123 |
- |
|
124 |
- return allErrs |
|
125 |
-} |
|
126 |
- |
|
127 | 114 |
func ValidateAssetConfig(config *api.AssetConfig) fielderrors.ValidationErrorList { |
128 | 115 |
allErrs := fielderrors.ValidationErrorList{} |
129 | 116 |
|
... | ... |
@@ -26,7 +26,6 @@ import ( |
26 | 26 |
"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory" |
27 | 27 |
|
28 | 28 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/namespace" |
29 |
- originscheduler "github.com/openshift/origin/pkg/scheduler" |
|
30 | 29 |
) |
31 | 30 |
|
32 | 31 |
const ( |
... | ... |
@@ -172,5 +171,5 @@ func (c *MasterConfig) createSchedulerConfig() (*scheduler.Config, error) { |
172 | 172 |
} |
173 | 173 |
|
174 | 174 |
// if the config file isn't provided, use the default provider |
175 |
- return configFactory.CreateFromProvider(originscheduler.DefaultProvider) |
|
175 |
+ return configFactory.CreateFromProvider(factory.DefaultProvider) |
|
176 | 176 |
} |
... | ... |
@@ -74,7 +74,6 @@ import ( |
74 | 74 |
clientetcd "github.com/openshift/origin/pkg/oauth/registry/oauthclient/etcd" |
75 | 75 |
clientauthetcd "github.com/openshift/origin/pkg/oauth/registry/oauthclientauthorization/etcd" |
76 | 76 |
projectapi "github.com/openshift/origin/pkg/project/api" |
77 |
- projectcache "github.com/openshift/origin/pkg/project/cache" |
|
78 | 77 |
projectcontroller "github.com/openshift/origin/pkg/project/controller" |
79 | 78 |
projectproxy "github.com/openshift/origin/pkg/project/registry/project/proxy" |
80 | 79 |
projectrequeststorage "github.com/openshift/origin/pkg/project/registry/projectrequest/delegated" |
... | ... |
@@ -756,12 +755,6 @@ func (c *MasterConfig) RunDNSServer() { |
756 | 756 |
glog.Infof("OpenShift DNS listening at %s", c.Options.DNSConfig.BindAddress) |
757 | 757 |
} |
758 | 758 |
|
759 |
-// RunProjectCache populates project cache, used by scheduler and project admission controller. |
|
760 |
-func (c *MasterConfig) RunProjectCache() { |
|
761 |
- glog.Infof("Using default project node label selector: %s", c.Options.ProjectNodeSelector) |
|
762 |
- projectcache.RunProjectCache(c.PrivilegedLoopbackKubernetesClient, c.Options.ProjectNodeSelector) |
|
763 |
-} |
|
764 |
- |
|
765 | 759 |
// RunBuildController starts the build sync loop for builds and buildConfig processing. |
766 | 760 |
func (c *MasterConfig) RunBuildController() { |
767 | 761 |
// initialize build controller |
... | ... |
@@ -47,7 +47,6 @@ type MasterArgs struct { |
47 | 47 |
KubeConnectionArgs *KubeConnectionArgs |
48 | 48 |
|
49 | 49 |
SchedulerConfigFile string |
50 |
- ProjectNodeSelector string |
|
51 | 50 |
} |
52 | 51 |
|
53 | 52 |
// BindMasterArgs binds the options to the flags with prefix + default flag names |
... | ... |
@@ -61,7 +60,6 @@ func BindMasterArgs(args *MasterArgs, flags *pflag.FlagSet, prefix string) { |
61 | 61 |
|
62 | 62 |
flags.Var(&args.NodeList, prefix+"nodes", "The hostnames of each node. This currently must be specified up front. Comma delimited list") |
63 | 63 |
flags.Var(&args.CORSAllowedOrigins, prefix+"cors-allowed-origins", "List of allowed origins for CORS, comma separated. An allowed origin can be a regular expression to support subdomain matching. CORS is enabled for localhost, 127.0.0.1, and the asset server by default.") |
64 |
- flags.StringVar(&args.ProjectNodeSelector, prefix+"project-node-selector", "", "Default node label selector for the project if not explicitly specified.") |
|
65 | 64 |
} |
66 | 65 |
|
67 | 66 |
// NewDefaultMasterArgs creates MasterArgs with sub-objects created and default values set. |
... | ... |
@@ -200,10 +198,6 @@ func (args MasterArgs) BuildSerializeableMasterConfig() (*configapi.MasterConfig |
200 | 200 |
}, |
201 | 201 |
} |
202 | 202 |
|
203 |
- if len(args.ProjectNodeSelector) > 0 { |
|
204 |
- config.ProjectNodeSelector = args.ProjectNodeSelector |
|
205 |
- } |
|
206 |
- |
|
207 | 203 |
if args.ListenArg.UseTLS() { |
208 | 204 |
config.ServingInfo.ServerCert = admin.DefaultMasterServingCertInfo(args.ConfigDir.Value()) |
209 | 205 |
config.ServingInfo.ClientCA = admin.DefaultAPIClientCAFile(args.ConfigDir.Value()) |
... | ... |
@@ -8,8 +8,5 @@ import ( |
8 | 8 |
_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/namespace/exists" |
9 | 9 |
_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/namespace/lifecycle" |
10 | 10 |
_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/resourcequota" |
11 |
- _ "github.com/openshift/origin/pkg/project/admission/lifecycle" |
|
12 |
- |
|
13 |
- // Scheduler plugins used by OpenShift |
|
14 |
- _ "github.com/openshift/origin/pkg/scheduler/algorithmprovider/defaults" |
|
11 |
+ _ "github.com/openshift/origin/pkg/project/admission" |
|
15 | 12 |
) |
... | ... |
@@ -308,9 +308,8 @@ func StartMaster(openshiftMasterConfig *configapi.MasterConfig) error { |
308 | 308 |
if err != nil { |
309 | 309 |
return err |
310 | 310 |
} |
311 |
- // Must start policy caching immediately |
|
311 |
+ // must start policy caching immediately |
|
312 | 312 |
openshiftConfig.RunPolicyCache() |
313 |
- openshiftConfig.RunProjectCache() |
|
314 | 313 |
|
315 | 314 |
unprotectedInstallers := []origin.APIInstaller{} |
316 | 315 |
|
317 | 316 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,131 @@ |
0 |
+/* |
|
1 |
+Copyright 2014 Google Inc. All rights reserved. |
|
2 |
+ |
|
3 |
+Licensed under the Apache License, Version 2.0 (the "License"); |
|
4 |
+you may not use this file except in compliance with the License. |
|
5 |
+You may obtain a copy of the License at |
|
6 |
+ |
|
7 |
+ http://www.apache.org/licenses/LICENSE-2.0 |
|
8 |
+ |
|
9 |
+Unless required by applicable law or agreed to in writing, software |
|
10 |
+distributed under the License is distributed on an "AS IS" BASIS, |
|
11 |
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
12 |
+See the License for the specific language governing permissions and |
|
13 |
+limitations under the License. |
|
14 |
+*/ |
|
15 |
+ |
|
16 |
+package admission |
|
17 |
+ |
|
18 |
+import ( |
|
19 |
+ "fmt" |
|
20 |
+ "io" |
|
21 |
+ |
|
22 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/admission" |
|
23 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
24 |
+ apierrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" |
|
25 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" |
|
26 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
|
27 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache" |
|
28 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/fields" |
|
29 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
30 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" |
|
31 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" |
|
32 |
+ "github.com/openshift/origin/pkg/api/latest" |
|
33 |
+) |
|
34 |
+ |
|
35 |
+// TODO: modify the upstream plug-in so this can be collapsed |
|
36 |
+// need ability to specify a RESTMapper on upstream version |
|
37 |
+func init() { |
|
38 |
+ admission.RegisterPlugin("OriginNamespaceLifecycle", func(client client.Interface, config io.Reader) (admission.Interface, error) { |
|
39 |
+ return NewLifecycle(client), nil |
|
40 |
+ }) |
|
41 |
+} |
|
42 |
+ |
|
43 |
+type lifecycle struct { |
|
44 |
+ client client.Interface |
|
45 |
+ store cache.Store |
|
46 |
+} |
|
47 |
+ |
|
48 |
+// Admit enforces that a namespace must exist in order to associate content with it. |
|
49 |
+// Admit enforces that a namespace that is terminating cannot accept new content being associated with it. |
|
50 |
+func (e *lifecycle) Admit(a admission.Attributes) (err error) { |
|
51 |
+ defaultVersion, kind, err := latest.RESTMapper.VersionAndKindForResource(a.GetResource()) |
|
52 |
+ if err != nil { |
|
53 |
+ return err |
|
54 |
+ } |
|
55 |
+ mapping, err := latest.RESTMapper.RESTMapping(kind, defaultVersion) |
|
56 |
+ if err != nil { |
|
57 |
+ return err |
|
58 |
+ } |
|
59 |
+ if mapping.Scope.Name() != meta.RESTScopeNameNamespace { |
|
60 |
+ return nil |
|
61 |
+ } |
|
62 |
+ |
|
63 |
+ // we want to allow someone to delete something in case it was phantom created somehow |
|
64 |
+ if a.GetOperation() == "DELETE" { |
|
65 |
+ return nil |
|
66 |
+ } |
|
67 |
+ |
|
68 |
+ // check for namespace in the cache |
|
69 |
+ namespaceObj, exists, err := e.store.Get(&kapi.Namespace{ |
|
70 |
+ ObjectMeta: kapi.ObjectMeta{ |
|
71 |
+ Name: a.GetNamespace(), |
|
72 |
+ Namespace: "", |
|
73 |
+ }, |
|
74 |
+ Status: kapi.NamespaceStatus{}, |
|
75 |
+ }) |
|
76 |
+ |
|
77 |
+ if err != nil { |
|
78 |
+ return err |
|
79 |
+ } |
|
80 |
+ |
|
81 |
+ name := "Unknown" |
|
82 |
+ obj := a.GetObject() |
|
83 |
+ if obj != nil { |
|
84 |
+ name, _ = meta.NewAccessor().Name(obj) |
|
85 |
+ } |
|
86 |
+ |
|
87 |
+ var namespace *kapi.Namespace |
|
88 |
+ if exists { |
|
89 |
+ namespace = namespaceObj.(*kapi.Namespace) |
|
90 |
+ } else { |
|
91 |
+ // Our watch maybe latent, so we make a best effort to get the object, and only fail if not found |
|
92 |
+ namespace, err = e.client.Namespaces().Get(a.GetNamespace()) |
|
93 |
+ // the namespace does not exist, so prevent create and update in that namespace |
|
94 |
+ if err != nil { |
|
95 |
+ return apierrors.NewForbidden(kind, name, fmt.Errorf("Namespace %s does not exist", a.GetNamespace())) |
|
96 |
+ } |
|
97 |
+ } |
|
98 |
+ |
|
99 |
+ if a.GetOperation() != "CREATE" { |
|
100 |
+ return nil |
|
101 |
+ } |
|
102 |
+ |
|
103 |
+ if namespace.Status.Phase != kapi.NamespaceTerminating { |
|
104 |
+ return nil |
|
105 |
+ } |
|
106 |
+ |
|
107 |
+ return apierrors.NewForbidden(kind, name, fmt.Errorf("Namespace %s is terminating", a.GetNamespace())) |
|
108 |
+} |
|
109 |
+ |
|
110 |
+func NewLifecycle(c client.Interface) admission.Interface { |
|
111 |
+ store := cache.NewStore(cache.MetaNamespaceKeyFunc) |
|
112 |
+ reflector := cache.NewReflector( |
|
113 |
+ &cache.ListWatch{ |
|
114 |
+ ListFunc: func() (runtime.Object, error) { |
|
115 |
+ return c.Namespaces().List(labels.Everything(), fields.Everything()) |
|
116 |
+ }, |
|
117 |
+ WatchFunc: func(resourceVersion string) (watch.Interface, error) { |
|
118 |
+ return c.Namespaces().Watch(labels.Everything(), fields.Everything(), resourceVersion) |
|
119 |
+ }, |
|
120 |
+ }, |
|
121 |
+ &kapi.Namespace{}, |
|
122 |
+ store, |
|
123 |
+ 0, |
|
124 |
+ ) |
|
125 |
+ reflector.Run() |
|
126 |
+ return &lifecycle{ |
|
127 |
+ client: c, |
|
128 |
+ store: store, |
|
129 |
+ } |
|
130 |
+} |
0 | 131 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,116 @@ |
0 |
+package admission |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "fmt" |
|
4 |
+ "testing" |
|
5 |
+ |
|
6 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/admission" |
|
7 |
+ kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
8 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache" |
|
9 |
+ "github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient" |
|
10 |
+ |
|
11 |
+ buildapi "github.com/openshift/origin/pkg/build/api" |
|
12 |
+) |
|
13 |
+ |
|
14 |
+// TestAdmissionExists verifies you cannot create Origin content if namespace is not known |
|
15 |
+func TestAdmissionExists(t *testing.T) { |
|
16 |
+ store := cache.NewStore(cache.MetaNamespaceKeyFunc) |
|
17 |
+ mockClient := &testclient.Fake{ |
|
18 |
+ Err: fmt.Errorf("DOES NOT EXIST"), |
|
19 |
+ } |
|
20 |
+ handler := &lifecycle{ |
|
21 |
+ client: mockClient, |
|
22 |
+ store: store, |
|
23 |
+ } |
|
24 |
+ build := &buildapi.Build{ |
|
25 |
+ ObjectMeta: kapi.ObjectMeta{Name: "buildid"}, |
|
26 |
+ Parameters: buildapi.BuildParameters{ |
|
27 |
+ Source: buildapi.BuildSource{ |
|
28 |
+ Type: buildapi.BuildSourceGit, |
|
29 |
+ Git: &buildapi.GitBuildSource{ |
|
30 |
+ URI: "http://github.com/my/repository", |
|
31 |
+ }, |
|
32 |
+ ContextDir: "context", |
|
33 |
+ }, |
|
34 |
+ Strategy: buildapi.BuildStrategy{ |
|
35 |
+ Type: buildapi.DockerBuildStrategyType, |
|
36 |
+ DockerStrategy: &buildapi.DockerBuildStrategy{}, |
|
37 |
+ }, |
|
38 |
+ Output: buildapi.BuildOutput{ |
|
39 |
+ DockerImageReference: "repository/data", |
|
40 |
+ }, |
|
41 |
+ }, |
|
42 |
+ Status: buildapi.BuildStatusNew, |
|
43 |
+ } |
|
44 |
+ err := handler.Admit(admission.NewAttributesRecord(build, "bogus-ns", "builds", "CREATE")) |
|
45 |
+ if err == nil { |
|
46 |
+ t.Errorf("Expected an error because namespace does not exist") |
|
47 |
+ } |
|
48 |
+} |
|
49 |
+ |
|
50 |
+// TestAdmissionLifecycle verifies you cannot create Origin content if namespace is terminating |
|
51 |
+func TestAdmissionLifecycle(t *testing.T) { |
|
52 |
+ namespaceObj := &kapi.Namespace{ |
|
53 |
+ ObjectMeta: kapi.ObjectMeta{ |
|
54 |
+ Name: "test", |
|
55 |
+ Namespace: "", |
|
56 |
+ }, |
|
57 |
+ Status: kapi.NamespaceStatus{ |
|
58 |
+ Phase: kapi.NamespaceActive, |
|
59 |
+ }, |
|
60 |
+ } |
|
61 |
+ store := cache.NewStore(cache.MetaNamespaceIndexFunc) |
|
62 |
+ store.Add(namespaceObj) |
|
63 |
+ mockClient := &testclient.Fake{} |
|
64 |
+ handler := &lifecycle{ |
|
65 |
+ client: mockClient, |
|
66 |
+ store: store, |
|
67 |
+ } |
|
68 |
+ build := &buildapi.Build{ |
|
69 |
+ ObjectMeta: kapi.ObjectMeta{Name: "buildid"}, |
|
70 |
+ Parameters: buildapi.BuildParameters{ |
|
71 |
+ Source: buildapi.BuildSource{ |
|
72 |
+ Type: buildapi.BuildSourceGit, |
|
73 |
+ Git: &buildapi.GitBuildSource{ |
|
74 |
+ URI: "http://github.com/my/repository", |
|
75 |
+ }, |
|
76 |
+ ContextDir: "context", |
|
77 |
+ }, |
|
78 |
+ Strategy: buildapi.BuildStrategy{ |
|
79 |
+ Type: buildapi.DockerBuildStrategyType, |
|
80 |
+ DockerStrategy: &buildapi.DockerBuildStrategy{}, |
|
81 |
+ }, |
|
82 |
+ Output: buildapi.BuildOutput{ |
|
83 |
+ DockerImageReference: "repository/data", |
|
84 |
+ }, |
|
85 |
+ }, |
|
86 |
+ Status: buildapi.BuildStatusNew, |
|
87 |
+ } |
|
88 |
+ err := handler.Admit(admission.NewAttributesRecord(build, namespaceObj.Namespace, "builds", "CREATE")) |
|
89 |
+ if err != nil { |
|
90 |
+ t.Errorf("Unexpected error returned from admission handler: %v", err) |
|
91 |
+ } |
|
92 |
+ |
|
93 |
+ // change namespace state to terminating |
|
94 |
+ namespaceObj.Status.Phase = kapi.NamespaceTerminating |
|
95 |
+ store.Add(namespaceObj) |
|
96 |
+ |
|
97 |
+ // verify create operations in the namespace cause an error |
|
98 |
+ err = handler.Admit(admission.NewAttributesRecord(build, namespaceObj.Namespace, "builds", "CREATE")) |
|
99 |
+ if err == nil { |
|
100 |
+ t.Errorf("Expected error rejecting creates in a namespace when it is terminating") |
|
101 |
+ } |
|
102 |
+ |
|
103 |
+ // verify update operations in the namespace can proceed |
|
104 |
+ err = handler.Admit(admission.NewAttributesRecord(build, namespaceObj.Namespace, "builds", "UPDATE")) |
|
105 |
+ if err != nil { |
|
106 |
+ t.Errorf("Unexpected error returned from admission handler: %v", err) |
|
107 |
+ } |
|
108 |
+ |
|
109 |
+ // verify delete operations in the namespace can proceed |
|
110 |
+ err = handler.Admit(admission.NewAttributesRecord(nil, namespaceObj.Namespace, "builds", "DELETE")) |
|
111 |
+ if err != nil { |
|
112 |
+ t.Errorf("Unexpected error returned from admission handler: %v", err) |
|
113 |
+ } |
|
114 |
+ |
|
115 |
+} |
0 | 116 |
deleted file mode 100644 |
... | ... |
@@ -1,92 +0,0 @@ |
1 |
-/* |
|
2 |
-Copyright 2014 Google Inc. All rights reserved. |
|
3 |
- |
|
4 |
-Licensed under the Apache License, Version 2.0 (the "License"); |
|
5 |
-you may not use this file except in compliance with the License. |
|
6 |
-You may obtain a copy of the License at |
|
7 |
- |
|
8 |
- http://www.apache.org/licenses/LICENSE-2.0 |
|
9 |
- |
|
10 |
-Unless required by applicable law or agreed to in writing, software |
|
11 |
-distributed under the License is distributed on an "AS IS" BASIS, |
|
12 |
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
13 |
-See the License for the specific language governing permissions and |
|
14 |
-limitations under the License. |
|
15 |
-*/ |
|
16 |
- |
|
17 |
-package admission |
|
18 |
- |
|
19 |
-import ( |
|
20 |
- "fmt" |
|
21 |
- "io" |
|
22 |
- |
|
23 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/admission" |
|
24 |
- kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
25 |
- apierrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" |
|
26 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" |
|
27 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
|
28 |
- |
|
29 |
- "github.com/openshift/origin/pkg/api/latest" |
|
30 |
- "github.com/openshift/origin/pkg/project/cache" |
|
31 |
-) |
|
32 |
- |
|
33 |
-// TODO: modify the upstream plug-in so this can be collapsed |
|
34 |
-// need ability to specify a RESTMapper on upstream version |
|
35 |
-func init() { |
|
36 |
- admission.RegisterPlugin("OriginNamespaceLifecycle", func(client client.Interface, config io.Reader) (admission.Interface, error) { |
|
37 |
- return NewLifecycle() |
|
38 |
- }) |
|
39 |
-} |
|
40 |
- |
|
41 |
-type lifecycle struct { |
|
42 |
-} |
|
43 |
- |
|
44 |
-// Admit enforces that a namespace must exist in order to associate content with it. |
|
45 |
-// Admit enforces that a namespace that is terminating cannot accept new content being associated with it. |
|
46 |
-func (e *lifecycle) Admit(a admission.Attributes) (err error) { |
|
47 |
- defaultVersion, kind, err := latest.RESTMapper.VersionAndKindForResource(a.GetResource()) |
|
48 |
- if err != nil { |
|
49 |
- return err |
|
50 |
- } |
|
51 |
- mapping, err := latest.RESTMapper.RESTMapping(kind, defaultVersion) |
|
52 |
- if err != nil { |
|
53 |
- return err |
|
54 |
- } |
|
55 |
- if mapping.Scope.Name() != meta.RESTScopeNameNamespace { |
|
56 |
- return nil |
|
57 |
- } |
|
58 |
- |
|
59 |
- // we want to allow someone to delete something in case it was phantom created somehow |
|
60 |
- if a.GetOperation() == "DELETE" { |
|
61 |
- return nil |
|
62 |
- } |
|
63 |
- |
|
64 |
- name := "Unknown" |
|
65 |
- obj := a.GetObject() |
|
66 |
- if obj != nil { |
|
67 |
- name, _ = meta.NewAccessor().Name(obj) |
|
68 |
- } |
|
69 |
- |
|
70 |
- projects, err := cache.GetProjectCache() |
|
71 |
- if err != nil { |
|
72 |
- return err |
|
73 |
- } |
|
74 |
- namespace, err := projects.GetNamespaceObject(a.GetNamespace()) |
|
75 |
- if err != nil { |
|
76 |
- return apierrors.NewForbidden(kind, name, err) |
|
77 |
- } |
|
78 |
- |
|
79 |
- if a.GetOperation() != "CREATE" { |
|
80 |
- return nil |
|
81 |
- } |
|
82 |
- |
|
83 |
- if namespace.Status.Phase != kapi.NamespaceTerminating { |
|
84 |
- return nil |
|
85 |
- } |
|
86 |
- |
|
87 |
- return apierrors.NewForbidden(kind, name, fmt.Errorf("Namespace %s is terminating", a.GetNamespace())) |
|
88 |
-} |
|
89 |
- |
|
90 |
-func NewLifecycle() (admission.Interface, error) { |
|
91 |
- return &lifecycle{}, nil |
|
92 |
-} |
93 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,112 +0,0 @@ |
1 |
-package admission |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "fmt" |
|
5 |
- "testing" |
|
6 |
- |
|
7 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/admission" |
|
8 |
- kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
9 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache" |
|
10 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient" |
|
11 |
- |
|
12 |
- buildapi "github.com/openshift/origin/pkg/build/api" |
|
13 |
- projectcache "github.com/openshift/origin/pkg/project/cache" |
|
14 |
-) |
|
15 |
- |
|
16 |
-// TestAdmissionExists verifies you cannot create Origin content if namespace is not known |
|
17 |
-func TestAdmissionExists(t *testing.T) { |
|
18 |
- mockClient := &testclient.Fake{ |
|
19 |
- Err: fmt.Errorf("DOES NOT EXIST"), |
|
20 |
- } |
|
21 |
- projectcache.FakeProjectCache(mockClient, cache.NewStore(cache.MetaNamespaceKeyFunc), "") |
|
22 |
- handler := &lifecycle{} |
|
23 |
- build := &buildapi.Build{ |
|
24 |
- ObjectMeta: kapi.ObjectMeta{Name: "buildid"}, |
|
25 |
- Parameters: buildapi.BuildParameters{ |
|
26 |
- Source: buildapi.BuildSource{ |
|
27 |
- Type: buildapi.BuildSourceGit, |
|
28 |
- Git: &buildapi.GitBuildSource{ |
|
29 |
- URI: "http://github.com/my/repository", |
|
30 |
- }, |
|
31 |
- ContextDir: "context", |
|
32 |
- }, |
|
33 |
- Strategy: buildapi.BuildStrategy{ |
|
34 |
- Type: buildapi.DockerBuildStrategyType, |
|
35 |
- DockerStrategy: &buildapi.DockerBuildStrategy{}, |
|
36 |
- }, |
|
37 |
- Output: buildapi.BuildOutput{ |
|
38 |
- DockerImageReference: "repository/data", |
|
39 |
- }, |
|
40 |
- }, |
|
41 |
- Status: buildapi.BuildStatusNew, |
|
42 |
- } |
|
43 |
- err := handler.Admit(admission.NewAttributesRecord(build, "bogus-ns", "builds", "CREATE")) |
|
44 |
- if err == nil { |
|
45 |
- t.Errorf("Expected an error because namespace does not exist") |
|
46 |
- } |
|
47 |
-} |
|
48 |
- |
|
49 |
-// TestAdmissionLifecycle verifies you cannot create Origin content if namespace is terminating |
|
50 |
-func TestAdmissionLifecycle(t *testing.T) { |
|
51 |
- namespaceObj := &kapi.Namespace{ |
|
52 |
- ObjectMeta: kapi.ObjectMeta{ |
|
53 |
- Name: "test", |
|
54 |
- Namespace: "", |
|
55 |
- }, |
|
56 |
- Status: kapi.NamespaceStatus{ |
|
57 |
- Phase: kapi.NamespaceActive, |
|
58 |
- }, |
|
59 |
- } |
|
60 |
- store := cache.NewStore(cache.MetaNamespaceIndexFunc) |
|
61 |
- store.Add(namespaceObj) |
|
62 |
- mockClient := &testclient.Fake{} |
|
63 |
- projectcache.FakeProjectCache(mockClient, store, "") |
|
64 |
- handler := &lifecycle{} |
|
65 |
- build := &buildapi.Build{ |
|
66 |
- ObjectMeta: kapi.ObjectMeta{Name: "buildid"}, |
|
67 |
- Parameters: buildapi.BuildParameters{ |
|
68 |
- Source: buildapi.BuildSource{ |
|
69 |
- Type: buildapi.BuildSourceGit, |
|
70 |
- Git: &buildapi.GitBuildSource{ |
|
71 |
- URI: "http://github.com/my/repository", |
|
72 |
- }, |
|
73 |
- ContextDir: "context", |
|
74 |
- }, |
|
75 |
- Strategy: buildapi.BuildStrategy{ |
|
76 |
- Type: buildapi.DockerBuildStrategyType, |
|
77 |
- DockerStrategy: &buildapi.DockerBuildStrategy{}, |
|
78 |
- }, |
|
79 |
- Output: buildapi.BuildOutput{ |
|
80 |
- DockerImageReference: "repository/data", |
|
81 |
- }, |
|
82 |
- }, |
|
83 |
- Status: buildapi.BuildStatusNew, |
|
84 |
- } |
|
85 |
- err := handler.Admit(admission.NewAttributesRecord(build, namespaceObj.Namespace, "builds", "CREATE")) |
|
86 |
- if err != nil { |
|
87 |
- t.Errorf("Unexpected error returned from admission handler: %v", err) |
|
88 |
- } |
|
89 |
- |
|
90 |
- // change namespace state to terminating |
|
91 |
- namespaceObj.Status.Phase = kapi.NamespaceTerminating |
|
92 |
- store.Add(namespaceObj) |
|
93 |
- |
|
94 |
- // verify create operations in the namespace cause an error |
|
95 |
- err = handler.Admit(admission.NewAttributesRecord(build, namespaceObj.Namespace, "builds", "CREATE")) |
|
96 |
- if err == nil { |
|
97 |
- t.Errorf("Expected error rejecting creates in a namespace when it is terminating") |
|
98 |
- } |
|
99 |
- |
|
100 |
- // verify update operations in the namespace can proceed |
|
101 |
- err = handler.Admit(admission.NewAttributesRecord(build, namespaceObj.Namespace, "builds", "UPDATE")) |
|
102 |
- if err != nil { |
|
103 |
- t.Errorf("Unexpected error returned from admission handler: %v", err) |
|
104 |
- } |
|
105 |
- |
|
106 |
- // verify delete operations in the namespace can proceed |
|
107 |
- err = handler.Admit(admission.NewAttributesRecord(nil, namespaceObj.Namespace, "builds", "DELETE")) |
|
108 |
- if err != nil { |
|
109 |
- t.Errorf("Unexpected error returned from admission handler: %v", err) |
|
110 |
- } |
|
111 |
- |
|
112 |
-} |
... | ... |
@@ -4,7 +4,6 @@ import ( |
4 | 4 |
"strings" |
5 | 5 |
|
6 | 6 |
kvalidation "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation" |
7 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
8 | 7 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
9 | 8 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors" |
10 | 9 |
"github.com/openshift/origin/pkg/project/api" |
... | ... |
@@ -24,7 +23,6 @@ func ValidateProject(project *api.Project) fielderrors.ValidationErrorList { |
24 | 24 |
if !validateNoNewLineOrTab(project.Annotations["displayName"]) { |
25 | 25 |
result = append(result, fielderrors.NewFieldInvalid("displayName", project.Annotations["displayName"], "may not contain a new line or tab")) |
26 | 26 |
} |
27 |
- result = append(result, validateNodeSelector(project)...) |
|
28 | 27 |
return result |
29 | 28 |
} |
30 | 29 |
|
... | ... |
@@ -37,7 +35,6 @@ func validateNoNewLineOrTab(s string) bool { |
37 | 37 |
func ValidateProjectUpdate(newProject *api.Project, oldProject *api.Project) fielderrors.ValidationErrorList { |
38 | 38 |
allErrs := fielderrors.ValidationErrorList{} |
39 | 39 |
allErrs = append(allErrs, kvalidation.ValidateObjectMetaUpdate(&oldProject.ObjectMeta, &newProject.ObjectMeta).Prefix("metadata")...) |
40 |
- allErrs = append(allErrs, validateNodeSelector(newProject)...) |
|
41 | 40 |
newProject.Spec.Finalizers = oldProject.Spec.Finalizers |
42 | 41 |
newProject.Status = oldProject.Status |
43 | 42 |
return allErrs |
... | ... |
@@ -49,16 +46,3 @@ func ValidateProjectRequest(request *api.ProjectRequest) fielderrors.ValidationE |
49 | 49 |
|
50 | 50 |
return ValidateProject(project) |
51 | 51 |
} |
52 |
- |
|
53 |
-func validateNodeSelector(p *api.Project) fielderrors.ValidationErrorList { |
|
54 |
- allErrs := fielderrors.ValidationErrorList{} |
|
55 |
- |
|
56 |
- if len(p.Annotations) > 0 { |
|
57 |
- if selector, ok := p.Annotations["nodeSelector"]; ok { |
|
58 |
- if _, err := labels.Parse(selector); err != nil { |
|
59 |
- allErrs = append(allErrs, fielderrors.NewFieldInvalid("nodeSelector", p.Annotations["nodeSelector"], "must be a valid label selector")) |
|
60 |
- } |
|
61 |
- } |
|
62 |
- } |
|
63 |
- return allErrs |
|
64 |
-} |
... | ... |
@@ -97,33 +97,6 @@ func TestValidateProject(t *testing.T) { |
97 | 97 |
// Should fail because the display name has \t \n |
98 | 98 |
numErrs: 1, |
99 | 99 |
}, |
100 |
- { |
|
101 |
- name: "valid node selector", |
|
102 |
- project: api.Project{ |
|
103 |
- ObjectMeta: kapi.ObjectMeta{ |
|
104 |
- Name: "foo", |
|
105 |
- Namespace: "", |
|
106 |
- Annotations: map[string]string{ |
|
107 |
- "nodeSelector": "infra=true, env in (prod, qa)", |
|
108 |
- }, |
|
109 |
- }, |
|
110 |
- }, |
|
111 |
- numErrs: 0, |
|
112 |
- }, |
|
113 |
- { |
|
114 |
- name: "invalid node selector", |
|
115 |
- project: api.Project{ |
|
116 |
- ObjectMeta: kapi.ObjectMeta{ |
|
117 |
- Name: "foo", |
|
118 |
- Namespace: "", |
|
119 |
- Annotations: map[string]string{ |
|
120 |
- "nodeSelector": "infra:true,env", |
|
121 |
- }, |
|
122 |
- }, |
|
123 |
- }, |
|
124 |
- // Should fail because infra:true is invalid format |
|
125 |
- numErrs: 1, |
|
126 |
- }, |
|
127 | 100 |
} |
128 | 101 |
|
129 | 102 |
for _, tc := range testCases { |
130 | 103 |
deleted file mode 100644 |
... | ... |
@@ -1,117 +0,0 @@ |
1 |
-package cache |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "fmt" |
|
5 |
- |
|
6 |
- kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
7 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/client" |
|
8 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache" |
|
9 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/fields" |
|
10 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
11 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" |
|
12 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" |
|
13 |
-) |
|
14 |
- |
|
15 |
-type ProjectCache struct { |
|
16 |
- Client client.Interface |
|
17 |
- Store cache.Store |
|
18 |
- DefaultNodeSelector string |
|
19 |
-} |
|
20 |
- |
|
21 |
-var pcache *ProjectCache |
|
22 |
- |
|
23 |
-func (p *ProjectCache) GetNamespaceObject(name string) (*kapi.Namespace, error) { |
|
24 |
- // check for namespace in the cache |
|
25 |
- namespaceObj, exists, err := p.Store.Get(&kapi.Namespace{ |
|
26 |
- ObjectMeta: kapi.ObjectMeta{ |
|
27 |
- Name: name, |
|
28 |
- Namespace: "", |
|
29 |
- }, |
|
30 |
- Status: kapi.NamespaceStatus{}, |
|
31 |
- }) |
|
32 |
- if err != nil { |
|
33 |
- return nil, err |
|
34 |
- } |
|
35 |
- |
|
36 |
- var namespace *kapi.Namespace |
|
37 |
- if exists { |
|
38 |
- namespace = namespaceObj.(*kapi.Namespace) |
|
39 |
- } else { |
|
40 |
- // Our watch maybe latent, so we make a best effort to get the object, and only fail if not found |
|
41 |
- namespace, err = p.Client.Namespaces().Get(name) |
|
42 |
- // the namespace does not exist, so prevent create and update in that namespace |
|
43 |
- if err != nil { |
|
44 |
- return nil, fmt.Errorf("Namespace %s does not exist", name) |
|
45 |
- } |
|
46 |
- } |
|
47 |
- return namespace, nil |
|
48 |
-} |
|
49 |
- |
|
50 |
-func (p *ProjectCache) GetNodeSelector(namespace *kapi.Namespace) string { |
|
51 |
- selector := "" |
|
52 |
- if len(namespace.ObjectMeta.Annotations) > 0 { |
|
53 |
- if ns, ok := namespace.ObjectMeta.Annotations["nodeSelector"]; ok { |
|
54 |
- selector = ns |
|
55 |
- } |
|
56 |
- } |
|
57 |
- if len(selector) == 0 { |
|
58 |
- selector = p.DefaultNodeSelector |
|
59 |
- } |
|
60 |
- return selector |
|
61 |
-} |
|
62 |
- |
|
63 |
-func (p *ProjectCache) GetNodeSelectorObject(namespace *kapi.Namespace) (labels.Selector, error) { |
|
64 |
- selector := p.GetNodeSelector(namespace) |
|
65 |
- if len(selector) == 0 { |
|
66 |
- return labels.Everything(), nil |
|
67 |
- } else { |
|
68 |
- selectorObj, err := labels.Parse(selector) |
|
69 |
- if err != nil { |
|
70 |
- return nil, err |
|
71 |
- } |
|
72 |
- return selectorObj, nil |
|
73 |
- } |
|
74 |
-} |
|
75 |
- |
|
76 |
-func GetProjectCache() (*ProjectCache, error) { |
|
77 |
- if pcache == nil { |
|
78 |
- return nil, fmt.Errorf("project cache not initialized") |
|
79 |
- } |
|
80 |
- return pcache, nil |
|
81 |
-} |
|
82 |
- |
|
83 |
-func RunProjectCache(c client.Interface, defaultNodeSelector string) { |
|
84 |
- if pcache != nil { |
|
85 |
- return |
|
86 |
- } |
|
87 |
- |
|
88 |
- store := cache.NewStore(cache.MetaNamespaceKeyFunc) |
|
89 |
- reflector := cache.NewReflector( |
|
90 |
- &cache.ListWatch{ |
|
91 |
- ListFunc: func() (runtime.Object, error) { |
|
92 |
- return c.Namespaces().List(labels.Everything(), fields.Everything()) |
|
93 |
- }, |
|
94 |
- WatchFunc: func(resourceVersion string) (watch.Interface, error) { |
|
95 |
- return c.Namespaces().Watch(labels.Everything(), fields.Everything(), resourceVersion) |
|
96 |
- }, |
|
97 |
- }, |
|
98 |
- &kapi.Namespace{}, |
|
99 |
- store, |
|
100 |
- 0, |
|
101 |
- ) |
|
102 |
- reflector.Run() |
|
103 |
- pcache = &ProjectCache{ |
|
104 |
- Client: c, |
|
105 |
- Store: store, |
|
106 |
- DefaultNodeSelector: defaultNodeSelector, |
|
107 |
- } |
|
108 |
-} |
|
109 |
- |
|
110 |
-// Used for testing purpose only |
|
111 |
-func FakeProjectCache(c client.Interface, store cache.Store, defaultNodeSelector string) { |
|
112 |
- pcache = &ProjectCache{ |
|
113 |
- Client: c, |
|
114 |
- Store: store, |
|
115 |
- DefaultNodeSelector: defaultNodeSelector, |
|
116 |
- } |
|
117 |
-} |
118 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,68 +0,0 @@ |
1 |
-// This is the default algorithm provider for the Origin scheduler. |
|
2 |
-package defaults |
|
3 |
- |
|
4 |
-import ( |
|
5 |
- kscheduler "github.com/GoogleCloudPlatform/kubernetes/pkg/scheduler" |
|
6 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/util" |
|
7 |
- _ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/algorithmprovider/defaults" |
|
8 |
- "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory" |
|
9 |
- |
|
10 |
- "github.com/openshift/origin/pkg/scheduler" |
|
11 |
-) |
|
12 |
- |
|
13 |
-func init() { |
|
14 |
- defaultPredicates, err := defaultPredicates() |
|
15 |
- if err != nil { |
|
16 |
- panic(err) |
|
17 |
- } |
|
18 |
- defaultPriorities, err := defaultPriorities() |
|
19 |
- if err != nil { |
|
20 |
- panic(err) |
|
21 |
- } |
|
22 |
- factory.RegisterAlgorithmProvider(scheduler.DefaultProvider, defaultPredicates, defaultPriorities) |
|
23 |
- |
|
24 |
- // Register non-default origin predicates/priorities here |
|
25 |
- // factory.RegisterFitPredicateFactory(...) |
|
26 |
- // factory.RegisterPriorityConfigFactory(...) |
|
27 |
-} |
|
28 |
- |
|
29 |
-func defaultPredicates() (util.StringSet, error) { |
|
30 |
- // Fit is determined by project node label selector query. |
|
31 |
- matchProjectNodeSelector := "MatchProjectNodeSelector" |
|
32 |
- factory.RegisterFitPredicateFactory( |
|
33 |
- matchProjectNodeSelector, |
|
34 |
- func(args factory.PluginFactoryArgs) kscheduler.FitPredicate { |
|
35 |
- return scheduler.NewProjectSelectorMatchPredicate(args.NodeInfo) |
|
36 |
- }, |
|
37 |
- ) |
|
38 |
- |
|
39 |
- // Get predicates from k8s default provider. |
|
40 |
- // If we decide not to use all the predicates from k8s default provider, |
|
41 |
- // chery-pick individual predicates from <k8s>/plugin/pkg/scheduler/algorithmprovider/defaults/defaults.go |
|
42 |
- kprovider, err := factory.GetAlgorithmProvider(factory.DefaultProvider) |
|
43 |
- if err != nil { |
|
44 |
- return nil, err |
|
45 |
- } |
|
46 |
- |
|
47 |
- originDefaultPredicates := kprovider.FitPredicateKeys |
|
48 |
- // Add default origin predicates |
|
49 |
- originDefaultPredicates.Insert(matchProjectNodeSelector) |
|
50 |
- |
|
51 |
- return originDefaultPredicates, nil |
|
52 |
-} |
|
53 |
- |
|
54 |
-func defaultPriorities() (util.StringSet, error) { |
|
55 |
- // Get priority functions from k8s default provider. |
|
56 |
- // If we decide not to use all the priority functions from k8s default provider, |
|
57 |
- // chery-pick individual priority function from <k8s>/plugin/pkg/scheduler/algorithmprovider/defaults/defaults.go |
|
58 |
- kprovider, err := factory.GetAlgorithmProvider(factory.DefaultProvider) |
|
59 |
- if err != nil { |
|
60 |
- return nil, err |
|
61 |
- } |
|
62 |
- |
|
63 |
- OriginDefaultPriorities := kprovider.PriorityFunctionKeys |
|
64 |
- // Add default origin priority function keys |
|
65 |
- // OriginDefaultPriorities.Insert(...) |
|
66 |
- |
|
67 |
- return OriginDefaultPriorities, nil |
|
68 |
-} |
7 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,51 +0,0 @@ |
1 |
-package algorithmprovider |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "testing" |
|
5 |
- |
|
6 |
- "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory" |
|
7 |
- |
|
8 |
- "github.com/openshift/origin/pkg/scheduler" |
|
9 |
-) |
|
10 |
- |
|
11 |
-var ( |
|
12 |
- algorithmProviderNames = []string{ |
|
13 |
- scheduler.DefaultProvider, |
|
14 |
- } |
|
15 |
-) |
|
16 |
- |
|
17 |
-func TestDefaultConfigExists(t *testing.T) { |
|
18 |
- p, err := factory.GetAlgorithmProvider(scheduler.DefaultProvider) |
|
19 |
- if err != nil { |
|
20 |
- t.Errorf("error retrieving default provider: %v", err) |
|
21 |
- } |
|
22 |
- if p == nil { |
|
23 |
- t.Error("algorithm provider config should not be nil") |
|
24 |
- } |
|
25 |
- if len(p.FitPredicateKeys) == 0 { |
|
26 |
- t.Error("default algorithm provider shouldn't have 0 fit predicates") |
|
27 |
- } |
|
28 |
-} |
|
29 |
- |
|
30 |
-func TestAlgorithmProviders(t *testing.T) { |
|
31 |
- for _, pn := range algorithmProviderNames { |
|
32 |
- p, err := factory.GetAlgorithmProvider(pn) |
|
33 |
- if err != nil { |
|
34 |
- t.Errorf("error retrieving '%s' provider: %v", pn, err) |
|
35 |
- break |
|
36 |
- } |
|
37 |
- if len(p.PriorityFunctionKeys) == 0 { |
|
38 |
- t.Errorf("%s algorithm provider shouldn't have 0 priority functions", pn) |
|
39 |
- } |
|
40 |
- for _, pf := range p.PriorityFunctionKeys.List() { |
|
41 |
- if !factory.IsPriorityFunctionRegistered(pf) { |
|
42 |
- t.Errorf("priority function %s is not registered but is used in the %s algorithm provider", pf, pn) |
|
43 |
- } |
|
44 |
- } |
|
45 |
- for _, fp := range p.FitPredicateKeys.List() { |
|
46 |
- if !factory.IsFitPredicateRegistered(fp) { |
|
47 |
- t.Errorf("fit predicate %s is not registered but is used in the %s algorithm provider", fp, pn) |
|
48 |
- } |
|
49 |
- } |
|
50 |
- } |
|
51 |
-} |
52 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,48 +0,0 @@ |
1 |
-package scheduler |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
5 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" |
|
6 |
- kscheduler "github.com/GoogleCloudPlatform/kubernetes/pkg/scheduler" |
|
7 |
- |
|
8 |
- "github.com/openshift/origin/pkg/project/cache" |
|
9 |
-) |
|
10 |
- |
|
11 |
-const ( |
|
12 |
- DefaultProvider = "OriginDefaultProvider" |
|
13 |
-) |
|
14 |
- |
|
15 |
-func NewProjectSelectorMatchPredicate(info kscheduler.NodeInfo) kscheduler.FitPredicate { |
|
16 |
- selector := &projectNodeSelector{ |
|
17 |
- info: info, |
|
18 |
- } |
|
19 |
- return selector.ProjectSelectorMatches |
|
20 |
-} |
|
21 |
- |
|
22 |
-type projectNodeSelector struct { |
|
23 |
- info kscheduler.NodeInfo |
|
24 |
-} |
|
25 |
- |
|
26 |
-func (p *projectNodeSelector) ProjectSelectorMatches(pod kapi.Pod, existingPods []kapi.Pod, node string) (bool, error) { |
|
27 |
- minion, err := p.info.GetNodeInfo(node) |
|
28 |
- if err != nil { |
|
29 |
- return false, err |
|
30 |
- } |
|
31 |
- return ProjectMatchesNodeLabels(&pod, minion) |
|
32 |
-} |
|
33 |
- |
|
34 |
-func ProjectMatchesNodeLabels(pod *kapi.Pod, node *kapi.Node) (bool, error) { |
|
35 |
- projects, err := cache.GetProjectCache() |
|
36 |
- if err != nil { |
|
37 |
- return false, err |
|
38 |
- } |
|
39 |
- namespace, err := projects.GetNamespaceObject(pod.ObjectMeta.Namespace) |
|
40 |
- if err != nil { |
|
41 |
- return false, err |
|
42 |
- } |
|
43 |
- selector, err := projects.GetNodeSelectorObject(namespace) |
|
44 |
- if err != nil { |
|
45 |
- return false, err |
|
46 |
- } |
|
47 |
- return selector.Matches(labels.Set(node.Labels)), nil |
|
48 |
-} |
49 | 1 |
deleted file mode 100644 |
... | ... |
@@ -1,90 +0,0 @@ |
1 |
-package scheduler |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "testing" |
|
5 |
- |
|
6 |
- kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" |
|
7 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache" |
|
8 |
- "github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient" |
|
9 |
- |
|
10 |
- projectcache "github.com/openshift/origin/pkg/project/cache" |
|
11 |
-) |
|
12 |
- |
|
13 |
-type FakeNodeInfo kapi.Node |
|
14 |
- |
|
15 |
-func (n FakeNodeInfo) GetNodeInfo(nodeName string) (*kapi.Node, error) { |
|
16 |
- node := kapi.Node(n) |
|
17 |
- return &node, nil |
|
18 |
-} |
|
19 |
- |
|
20 |
-func TestPodFitsProjectSelector(t *testing.T) { |
|
21 |
- mockClient := &testclient.Fake{} |
|
22 |
- project := &kapi.Namespace{ |
|
23 |
- ObjectMeta: kapi.ObjectMeta{ |
|
24 |
- Name: "testProject", |
|
25 |
- Namespace: "", |
|
26 |
- }, |
|
27 |
- } |
|
28 |
- projectStore := cache.NewStore(cache.MetaNamespaceIndexFunc) |
|
29 |
- projectStore.Add(project) |
|
30 |
- |
|
31 |
- pod := kapi.Pod{ObjectMeta: kapi.ObjectMeta{Name: "testPod"}} |
|
32 |
- node := kapi.Node{ObjectMeta: kapi.ObjectMeta{Name: "testNode"}} |
|
33 |
- |
|
34 |
- tests := []struct { |
|
35 |
- defaultNodeSelector string |
|
36 |
- projectNodeSelector string |
|
37 |
- nodeLabels map[string]string |
|
38 |
- fits bool |
|
39 |
- testName string |
|
40 |
- }{ |
|
41 |
- { |
|
42 |
- defaultNodeSelector: "", |
|
43 |
- projectNodeSelector: "", |
|
44 |
- nodeLabels: map[string]string{"infra": "false"}, |
|
45 |
- fits: true, |
|
46 |
- testName: "No node selectors", |
|
47 |
- }, |
|
48 |
- { |
|
49 |
- defaultNodeSelector: "infra=false", |
|
50 |
- projectNodeSelector: "", |
|
51 |
- nodeLabels: map[string]string{"infra": "false"}, |
|
52 |
- fits: true, |
|
53 |
- testName: "Matches default node selector", |
|
54 |
- }, |
|
55 |
- { |
|
56 |
- defaultNodeSelector: "env=test", |
|
57 |
- projectNodeSelector: "", |
|
58 |
- nodeLabels: map[string]string{"infra": "false"}, |
|
59 |
- fits: false, |
|
60 |
- testName: "Doesn't match default node selector", |
|
61 |
- }, |
|
62 |
- { |
|
63 |
- defaultNodeSelector: "", |
|
64 |
- projectNodeSelector: "infra=false", |
|
65 |
- nodeLabels: map[string]string{"infra": "false"}, |
|
66 |
- fits: true, |
|
67 |
- testName: "Matches project node selector", |
|
68 |
- }, |
|
69 |
- { |
|
70 |
- defaultNodeSelector: "infra=false", |
|
71 |
- projectNodeSelector: "env=test", |
|
72 |
- nodeLabels: map[string]string{"infra": "false"}, |
|
73 |
- fits: false, |
|
74 |
- testName: "Doesn't match project node selector", |
|
75 |
- }, |
|
76 |
- } |
|
77 |
- for _, test := range tests { |
|
78 |
- node.ObjectMeta.Labels = test.nodeLabels |
|
79 |
- projectcache.FakeProjectCache(mockClient, projectStore, test.defaultNodeSelector) |
|
80 |
- project.ObjectMeta.Annotations = map[string]string{"nodeSelector": test.projectNodeSelector} |
|
81 |
- predicate := projectNodeSelector{FakeNodeInfo(node)} |
|
82 |
- fits, err := predicate.ProjectSelectorMatches(pod, []kapi.Pod{}, "machine") |
|
83 |
- if err != nil { |
|
84 |
- t.Errorf("unexpected error: %v", err) |
|
85 |
- } |
|
86 |
- if fits != test.fits { |
|
87 |
- t.Errorf("%s: expected: %v got %v", test.testName, test.fits, fits) |
|
88 |
- } |
|
89 |
- } |
|
90 |
-} |
... | ... |
@@ -131,8 +131,7 @@ func TestProjectIsNamespace(t *testing.T) { |
131 | 131 |
ObjectMeta: kapi.ObjectMeta{ |
132 | 132 |
Name: "new-project", |
133 | 133 |
Annotations: map[string]string{ |
134 |
- "displayName": "Hello World", |
|
135 |
- "nodeSelector": "env=test", |
|
134 |
+ "displayName": "Hello World", |
|
136 | 135 |
}, |
137 | 136 |
}, |
138 | 137 |
} |
... | ... |
@@ -152,9 +151,7 @@ func TestProjectIsNamespace(t *testing.T) { |
152 | 152 |
if project.Annotations["displayName"] != namespace.Annotations["displayName"] { |
153 | 153 |
t.Fatalf("Project display name did not match namespace annotation, project %v, namespace %v", project.Annotations["displayName"], namespace.Annotations["displayName"]) |
154 | 154 |
} |
155 |
- if project.Annotations["nodeSelector"] != namespace.Annotations["nodeSelector"] { |
|
156 |
- t.Fatalf("Project node selector did not match namespace node selector, project %v, namespace %v", project.Annotations["nodeSelector"], namespace.Annotations["displayname"]) |
|
157 |
- } |
|
155 |
+ |
|
158 | 156 |
} |
159 | 157 |
|
160 | 158 |
// TestProjectMustExist verifies that content cannot be added in a project that does not exist |
... | ... |
@@ -213,4 +210,5 @@ func TestProjectMustExist(t *testing.T) { |
213 | 213 |
if err == nil { |
214 | 214 |
t.Errorf("Expected an error on creation of a Origin resource because namespace does not exist") |
215 | 215 |
} |
216 |
+ |
|
216 | 217 |
} |