Browse code

add nodeselector and annotation build pod overrides and defaulters

Ben Parees authored on 2016/10/15 07:40:47
Showing 50 changed files
... ...
@@ -493,6 +493,12 @@ message CommonSpec {
493 493
   // be active on a node before the system actively tries to terminate the
494 494
   // build; value must be positive integer
495 495
   optional int64 completionDeadlineSeconds = 8;
496
+
497
+  // nodeSelector is a selector which must be true for the build pod to fit on a node
498
+  // If nil, it can be overridden by default build nodeselector values for the cluster.
499
+  // If set to an empty map or a map with any values, default build nodeselector values
500
+  // are ignored.
501
+  optional OptionalNodeSelector nodeSelector = 9;
496 502
 }
497 503
 
498 504
 // CustomBuildStrategy defines input parameters specific to Custom build.
... ...
@@ -695,6 +701,15 @@ message JenkinsPipelineBuildStrategy {
695 695
   optional string jenkinsfile = 2;
696 696
 }
697 697
 
698
+// OptionalNodeSelector is a map that may also be left nil to distinguish between set and unset.
699
+// +protobuf.nullable=true
700
+// +protobuf.options.(gogoproto.goproto_stringer)=false
701
+message OptionalNodeSelector {
702
+  // items, if empty, will result in an empty map
703
+
704
+  map<string, string> items = 1;
705
+}
706
+
698 707
 // ProxyConfig defines what proxies to use for an operation
699 708
 message ProxyConfig {
700 709
   // httpProxy is a proxy used to reach the git repository over http
... ...
@@ -21416,7 +21416,8 @@
21416 21416
     "description": "BuildConfigSpec describes when and how builds are created",
21417 21417
     "required": [
21418 21418
      "triggers",
21419
-     "strategy"
21419
+     "strategy",
21420
+     "nodeSelector"
21420 21421
     ],
21421 21422
     "properties": {
21422 21423
      "triggers": {
... ...
@@ -21462,6 +21463,10 @@
21462 21462
       "type": "integer",
21463 21463
       "format": "int64",
21464 21464
       "description": "completionDeadlineSeconds is an optional duration in seconds, counted from the time when a build pod gets scheduled in the system, that the build may be active on a node before the system actively tries to terminate the build; value must be positive integer"
21465
+     },
21466
+     "nodeSelector": {
21467
+      "type": "object",
21468
+      "description": "nodeSelector is a selector which must be true for the build pod to fit on a node If nil, it can be overridden by default build nodeselector values for the cluster. If set to an empty map or a map with any values, default build nodeselector values are ignored."
21465 21469
      }
21466 21470
     }
21467 21471
    },
... ...
@@ -22477,6 +22482,7 @@
22477 22477
     "description": "BuildSpec has the information to represent a build and also additional information about a build",
22478 22478
     "required": [
22479 22479
      "strategy",
22480
+     "nodeSelector",
22480 22481
      "triggeredBy"
22481 22482
     ],
22482 22483
     "properties": {
... ...
@@ -22513,6 +22519,10 @@
22513 22513
       "format": "int64",
22514 22514
       "description": "completionDeadlineSeconds is an optional duration in seconds, counted from the time when a build pod gets scheduled in the system, that the build may be active on a node before the system actively tries to terminate the build; value must be positive integer"
22515 22515
      },
22516
+     "nodeSelector": {
22517
+      "type": "object",
22518
+      "description": "nodeSelector is a selector which must be true for the build pod to fit on a node If nil, it can be overridden by default build nodeselector values for the cluster. If set to an empty map or a map with any values, default build nodeselector values are ignored."
22519
+     },
22516 22520
      "triggeredBy": {
22517 22521
       "type": "array",
22518 22522
       "items": {
... ...
@@ -44686,7 +44686,8 @@
44686 44686
     "description": "BuildConfigSpec describes when and how builds are created",
44687 44687
     "required": [
44688 44688
      "triggers",
44689
-     "strategy"
44689
+     "strategy",
44690
+     "nodeSelector"
44690 44691
     ],
44691 44692
     "properties": {
44692 44693
      "completionDeadlineSeconds": {
... ...
@@ -44694,6 +44695,10 @@
44694 44694
       "type": "integer",
44695 44695
       "format": "int64"
44696 44696
      },
44697
+     "nodeSelector": {
44698
+      "description": "nodeSelector is a selector which must be true for the build pod to fit on a node If nil, it can be overridden by default build nodeselector values for the cluster. If set to an empty map or a map with any values, default build nodeselector values are ignored.",
44699
+      "type": "object"
44700
+     },
44697 44701
      "output": {
44698 44702
       "$ref": "#/definitions/v1.BuildOutput"
44699 44703
      },
... ...
@@ -44919,6 +44924,7 @@
44919 44919
     "description": "BuildSpec has the information to represent a build and also additional information about a build",
44920 44920
     "required": [
44921 44921
      "strategy",
44922
+     "nodeSelector",
44922 44923
      "triggeredBy"
44923 44924
     ],
44924 44925
     "properties": {
... ...
@@ -44927,6 +44933,10 @@
44927 44927
       "type": "integer",
44928 44928
       "format": "int64"
44929 44929
      },
44930
+     "nodeSelector": {
44931
+      "description": "nodeSelector is a selector which must be true for the build pod to fit on a node If nil, it can be overridden by default build nodeselector values for the cluster. If set to an empty map or a map with any values, default build nodeselector values are ignored.",
44932
+      "type": "object"
44933
+     },
44930 44934
      "output": {
44931 44935
       "$ref": "#/definitions/v1.BuildOutput"
44932 44936
      },
... ...
@@ -26,7 +26,7 @@ This command launches an instance of the Kubernetes apiserver (kube\-apiserver).
26 26
 
27 27
 .PP
28 28
 \fB\-\-admission\-control\fP="AlwaysAdmit"
29
-    Ordered list of plug\-ins to do admission control of resources into cluster. Comma\-delimited list of: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, BuildByStrategy, BuildDefaults, BuildOverrides, ClusterResourceOverride, DefaultStorageClass, DenyEscalatingExec, DenyExecOnPrivileged, ExternalIPRanger, ImagePolicyWebhook, InitialResources, LimitPodHardAntiAffinityTopology, LimitRanger, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, OriginNamespaceLifecycle, OriginPodNodeEnvironment, OwnerReferencesPermissionEnforcement, PersistentVolumeLabel, PodNodeConstraints, PodSecurityPolicy, ProjectRequestLimit, ResourceQuota, RunOnceDuration, SCCExecRestrictions, SecurityContextConstraint, SecurityContextDeny, ServiceAccount, openshift.io/ClusterResourceQuota, openshift.io/ImageLimitRange, openshift.io/ImagePolicy, openshift.io/JenkinsBootstrapper, openshift.io/OriginResourceQuota, openshift.io/RestrictedEndpointsAdmission.
29
+    Ordered list of plug\-ins to do admission control of resources into cluster. Comma\-delimited list of: AlwaysAdmit, AlwaysDeny, AlwaysPullImages, BuildByStrategy, ClusterResourceOverride, DefaultStorageClass, DenyEscalatingExec, DenyExecOnPrivileged, ExternalIPRanger, ImagePolicyWebhook, InitialResources, LimitPodHardAntiAffinityTopology, LimitRanger, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, OriginNamespaceLifecycle, OriginPodNodeEnvironment, OwnerReferencesPermissionEnforcement, PersistentVolumeLabel, PodNodeConstraints, PodSecurityPolicy, ProjectRequestLimit, ResourceQuota, RunOnceDuration, SCCExecRestrictions, SecurityContextConstraint, SecurityContextDeny, ServiceAccount, openshift.io/ClusterResourceQuota, openshift.io/ImageLimitRange, openshift.io/ImagePolicy, openshift.io/JenkinsBootstrapper, openshift.io/OriginResourceQuota, openshift.io/RestrictedEndpointsAdmission.
30 30
 
31 31
 .PP
32 32
 \fB\-\-admission\-control\-config\-file\fP=""
... ...
@@ -4,7 +4,6 @@ import (
4 4
 	"errors"
5 5
 	"fmt"
6 6
 
7
-	"k8s.io/kubernetes/pkg/admission"
8 7
 	kapi "k8s.io/kubernetes/pkg/api"
9 8
 	"k8s.io/kubernetes/pkg/api/unversioned"
10 9
 	"k8s.io/kubernetes/pkg/runtime"
... ...
@@ -12,70 +11,44 @@ import (
12 12
 	buildapi "github.com/openshift/origin/pkg/build/api"
13 13
 )
14 14
 
15
-// IsBuildPod returns true if a pod is a pod generated for a Build
16
-func IsBuildPod(a admission.Attributes) bool {
17
-	if a.GetResource().GroupResource() != kapi.Resource("pods") {
18
-		return false
19
-	}
20
-	if len(a.GetSubresource()) != 0 {
21
-		return false
22
-	}
23
-	pod, err := GetPod(a)
24
-	if err != nil {
25
-		return false
26
-	}
27
-	return hasBuildAnnotation(pod) && hasBuildEnvVar(pod)
28
-}
29
-
30
-// GetBuild returns a build object encoded in a pod's BUILD environment variable along with
15
+// GetBuildFromPod returns a build object encoded in a pod's BUILD environment variable along with
31 16
 // its encoding version
32
-func GetBuild(a admission.Attributes) (*buildapi.Build, unversioned.GroupVersion, error) {
33
-	pod, err := GetPod(a)
17
+func GetBuildFromPod(pod *kapi.Pod) (*buildapi.Build, unversioned.GroupVersion, error) {
18
+	envVar, err := buildEnvVar(pod)
34 19
 	if err != nil {
35
-		return nil, unversioned.GroupVersion{}, err
20
+		return nil, unversioned.GroupVersion{}, fmt.Errorf("unable to get build from pod: %v", err)
36 21
 	}
37
-	build, version, err := getBuildFromPod(pod)
22
+	obj, groupVersionKind, err := kapi.Codecs.UniversalDecoder().Decode([]byte(envVar.Value), nil, nil)
38 23
 	if err != nil {
39
-		return nil, unversioned.GroupVersion{}, admission.NewForbidden(a, fmt.Errorf("unable to get build from pod: %v", err))
24
+		return nil, unversioned.GroupVersion{}, fmt.Errorf("unable to get build from pod: %v", err)
40 25
 	}
41
-	return build, version, nil
42
-}
43
-
44
-// GetPod returns a pod from an admission attributes object
45
-func GetPod(a admission.Attributes) (*kapi.Pod, error) {
46
-	pod, isPod := a.GetObject().(*kapi.Pod)
47
-	if !isPod {
48
-		return nil, admission.NewForbidden(a, fmt.Errorf("unrecognized request object: %#v", a.GetObject()))
26
+	build, ok := obj.(*buildapi.Build)
27
+	if !ok {
28
+		return nil, unversioned.GroupVersion{}, fmt.Errorf("unable to get build from pod: %v", errors.New("decoded object is not of type Build"))
49 29
 	}
50
-	return pod, nil
30
+	return build, groupVersionKind.GroupVersion(), nil
51 31
 }
52 32
 
53
-// SetBuild encodes a build object and sets it in a pod's BUILD environment variable
54
-func SetBuild(a admission.Attributes, build *buildapi.Build, groupVersion unversioned.GroupVersion) error {
55
-	pod, err := GetPod(a)
33
+// SetBuildInPod encodes a build object and sets it in a pod's BUILD environment variable
34
+func SetBuildInPod(pod *kapi.Pod, build *buildapi.Build, groupVersion unversioned.GroupVersion) error {
35
+	envVar, err := buildEnvVar(pod)
56 36
 	if err != nil {
57
-		return err
37
+		return fmt.Errorf("unable to set build in pod: %v", err)
58 38
 	}
59
-
60
-	err = setBuildInPod(build, pod, groupVersion)
39
+	encodedBuild, err := runtime.Encode(kapi.Codecs.LegacyCodec(groupVersion), build)
61 40
 	if err != nil {
62
-		return admission.NewForbidden(a, fmt.Errorf("unable to set build in pod: %v", err))
41
+		return fmt.Errorf("unable to set build in pod: %v", err)
63 42
 	}
64
-
43
+	envVar.Value = string(encodedBuild)
65 44
 	return nil
66 45
 }
67 46
 
68
-// SetBuildLogLevel extracts BUILD_LOGLEVEL from the Build environment
47
+// SetPodLogLevelFromBuild extracts BUILD_LOGLEVEL from the Build environment
69 48
 // and feeds it as an argument to the Pod's entrypoint. The BUILD_LOGLEVEL
70 49
 // environment variable may have been set in multiple ways: a default value,
71 50
 // by a BuildConfig, or by the BuildDefaults admission plugin. In this method
72 51
 // we finally act on the value by injecting it into the Pod.
73
-func SetBuildLogLevel(attributes admission.Attributes, build *buildapi.Build) error {
74
-	pod, err := GetPod(attributes)
75
-	if err != nil {
76
-		return err
77
-	}
78
-
52
+func SetPodLogLevelFromBuild(pod *kapi.Pod, build *buildapi.Build) error {
79 53
 	var envs []kapi.EnvVar
80 54
 
81 55
 	// Check whether the build strategy supports --loglevel parameter.
... ...
@@ -102,39 +75,6 @@ func SetBuildLogLevel(attributes admission.Attributes, build *buildapi.Build) er
102 102
 	return nil
103 103
 }
104 104
 
105
-// getBuildFromPod detects the encoding of a Build in a pod and returns the Build
106
-// object along with its detected version.
107
-func getBuildFromPod(pod *kapi.Pod) (*buildapi.Build, unversioned.GroupVersion, error) {
108
-	envVar, err := buildEnvVar(pod)
109
-	if err != nil {
110
-		return nil, unversioned.GroupVersion{}, err
111
-	}
112
-	obj, groupVersionKind, err := kapi.Codecs.UniversalDecoder().Decode([]byte(envVar.Value), nil, nil)
113
-	if err != nil {
114
-		return nil, unversioned.GroupVersion{}, err
115
-	}
116
-	build, ok := obj.(*buildapi.Build)
117
-	if !ok {
118
-		return nil, unversioned.GroupVersion{}, errors.New("decoded object is not of type Build")
119
-	}
120
-	return build, groupVersionKind.GroupVersion(), nil
121
-}
122
-
123
-// setBuildInPod encodes a build with the given version and sets it in the BUILD environment variable
124
-// of the pod.
125
-func setBuildInPod(build *buildapi.Build, pod *kapi.Pod, groupVersion unversioned.GroupVersion) error {
126
-	envVar, err := buildEnvVar(pod)
127
-	if err != nil {
128
-		return err
129
-	}
130
-	encodedBuild, err := runtime.Encode(kapi.Codecs.LegacyCodec(groupVersion), build)
131
-	if err != nil {
132
-		return err
133
-	}
134
-	envVar.Value = string(encodedBuild)
135
-	return nil
136
-}
137
-
138 105
 func buildEnvVar(pod *kapi.Pod) (*kapi.EnvVar, error) {
139 106
 	if len(pod.Spec.Containers) == 0 {
140 107
 		return nil, errors.New("pod has no containers")
... ...
@@ -8,47 +8,13 @@ import (
8 8
 	"k8s.io/kubernetes/pkg/api/unversioned"
9 9
 
10 10
 	u "github.com/openshift/origin/pkg/build/admission/testutil"
11
-	buildapi "github.com/openshift/origin/pkg/build/api"
12 11
 )
13 12
 
14
-func TestIsBuildPod(t *testing.T) {
15
-	tests := []struct {
16
-		pod      *u.TestPod
17
-		expected bool
18
-	}{
19
-		{
20
-			pod:      u.Pod().WithAnnotation("foo", "bar"),
21
-			expected: false,
22
-		},
23
-		{
24
-			pod:      u.Pod().WithEnvVar("BUILD", "blah"),
25
-			expected: false,
26
-		},
27
-		{
28
-			pod:      u.Pod().WithAnnotation(buildapi.BuildAnnotation, "build"),
29
-			expected: false,
30
-		},
31
-		{
32
-			pod: u.Pod().
33
-				WithAnnotation(buildapi.BuildAnnotation, "build").
34
-				WithEnvVar("BUILD", "true"),
35
-			expected: true,
36
-		},
37
-	}
38
-
39
-	for _, tc := range tests {
40
-		actual := IsBuildPod(tc.pod.ToAttributes())
41
-		if actual != tc.expected {
42
-			t.Errorf("unexpected result (%v) for pod %#v", actual, tc.pod)
43
-		}
44
-	}
45
-}
46
-
47 13
 func TestGetBuild(t *testing.T) {
48 14
 	build := u.Build().WithDockerStrategy()
49 15
 	for _, version := range []string{"v1"} {
50 16
 		pod := u.Pod().WithBuild(t, build.AsBuild(), version)
51
-		resultBuild, resultVersion, err := GetBuild(pod.ToAttributes())
17
+		resultBuild, resultVersion, err := GetBuildFromPod((*kapi.Pod)(pod))
52 18
 		if err != nil {
53 19
 			t.Fatalf("unexpected error: %v", err)
54 20
 		}
... ...
@@ -69,7 +35,7 @@ func TestSetBuild(t *testing.T) {
69 69
 		if err != nil {
70 70
 			t.Fatalf("unexpected error: %v", err)
71 71
 		}
72
-		err = SetBuild(pod.ToAttributes(), build.AsBuild(), groupVersion)
72
+		err = SetBuildInPod((*kapi.Pod)(pod), build.AsBuild(), groupVersion)
73 73
 		if err != nil {
74 74
 			t.Fatalf("unexpected error: %v", err)
75 75
 		}
... ...
@@ -83,7 +49,7 @@ func TestSetBuild(t *testing.T) {
83 83
 func TestSetBuildLogLevel(t *testing.T) {
84 84
 	build := u.Build().WithSourceStrategy()
85 85
 	pod := u.Pod().WithEnvVar("BUILD", "foo")
86
-	SetBuildLogLevel(pod.ToAttributes(), build.AsBuild())
86
+	SetPodLogLevelFromBuild((*kapi.Pod)(pod), build.AsBuild())
87 87
 
88 88
 	if len(pod.Spec.Containers[0].Args) == 0 {
89 89
 		t.Errorf("Builds pod loglevel was not set")
... ...
@@ -96,7 +62,7 @@ func TestSetBuildLogLevel(t *testing.T) {
96 96
 	build = u.Build().WithSourceStrategy()
97 97
 	pod = u.Pod().WithEnvVar("BUILD", "foo")
98 98
 	build.Spec.Strategy.SourceStrategy.Env = []kapi.EnvVar{{Name: "BUILD_LOGLEVEL", Value: "7", ValueFrom: nil}}
99
-	SetBuildLogLevel(pod.ToAttributes(), build.AsBuild())
99
+	SetPodLogLevelFromBuild((*kapi.Pod)(pod), build.AsBuild())
100 100
 
101 101
 	if pod.Spec.Containers[0].Args[0] != "--loglevel=7" {
102 102
 		t.Errorf("Build pod loglevel was not transferred from BUILD_LOGLEVEL environment variable: %#v", pod)
... ...
@@ -1,27 +1,26 @@
1 1
 package admission
2 2
 
3 3
 import (
4
-	"io"
5
-	"io/ioutil"
6
-	"reflect"
4
+	"github.com/golang/glog"
7 5
 
8 6
 	"k8s.io/kubernetes/pkg/runtime"
9 7
 
8
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
10 9
 	configlatest "github.com/openshift/origin/pkg/cmd/server/api/latest"
10
+	"github.com/openshift/origin/pkg/cmd/util/pluginconfig"
11 11
 )
12 12
 
13 13
 // ReadPluginConfig will read a plugin configuration object from a reader stream
14
-func ReadPluginConfig(reader io.Reader, config runtime.Object) error {
15
-	if reader == nil || reflect.ValueOf(reader).IsNil() {
14
+func ReadPluginConfig(pluginConfig map[string]configapi.AdmissionPluginConfig, name string, config runtime.Object) error {
15
+
16
+	configFilePath, err := pluginconfig.GetPluginConfigFile(pluginConfig, name, "")
17
+	if configFilePath == "" {
16 18
 		return nil
17 19
 	}
18 20
 
19
-	configBytes, err := ioutil.ReadAll(reader)
20
-	if err != nil {
21
-		return err
22
-	}
23
-	err = configlatest.ReadYAMLInto(configBytes, config)
21
+	err = configlatest.ReadYAMLFileInto(configFilePath, config)
24 22
 	if err != nil {
23
+		glog.Errorf("couldn't open plugin configuration %s: %#v", configFilePath, err)
25 24
 		return err
26 25
 	}
27 26
 	return nil
... ...
@@ -1,7 +1,6 @@
1 1
 package admission
2 2
 
3 3
 import (
4
-	"bytes"
5 4
 	"reflect"
6 5
 	"testing"
7 6
 
... ...
@@ -48,22 +47,15 @@ func TestReadPluginConfig(t *testing.T) {
48 48
 	configapi.Scheme.AddKnownTypes(configapi.SchemeGroupVersion, &OtherTestConfig2{})
49 49
 	configapi.Scheme.AddKnownTypeWithName(configapiv1.SchemeGroupVersion.WithKind("OtherTestConfig2"), &OtherTestConfig2V2{})
50 50
 
51
-	configString := `apiVersion: v1
52
-kind: TestConfig
53
-item1: hello
54
-item2:
55
-- foo
56
-- bar
57
-`
58
-
59 51
 	config := &TestConfig{}
60 52
 
61 53
 	expected := &TestConfig{
62 54
 		Item1: "hello",
63 55
 		Item2: []string{"foo", "bar"},
64 56
 	}
57
+	pluginCfg := map[string]configapi.AdmissionPluginConfig{"testconfig": {"", expected}}
65 58
 	// The config should match the expected config object
66
-	err := ReadPluginConfig(bytes.NewBufferString(configString), config)
59
+	err := ReadPluginConfig(pluginCfg, "testconfig", config)
67 60
 	if err != nil {
68 61
 		t.Fatalf("unexpected: %v", err)
69 62
 	}
... ...
@@ -71,16 +63,17 @@ item2:
71 71
 		t.Errorf("config does not equal expected: %#v", config)
72 72
 	}
73 73
 
74
-	// Passing a nil reader, should not get an error
75
-	var nilBuffer *bytes.Buffer
76
-	err = ReadPluginConfig(nilBuffer, &TestConfig{})
74
+	// Passing a nil cfg, should not get an error
75
+	pluginCfg = map[string]configapi.AdmissionPluginConfig{}
76
+	err = ReadPluginConfig(pluginCfg, "testconfig", &TestConfig{})
77 77
 	if err != nil {
78 78
 		t.Fatalf("unexpected: %v", err)
79 79
 	}
80 80
 
81 81
 	// Passing the wrong type of destination object should result in an error
82 82
 	config2 := &OtherTestConfig2{}
83
-	err = ReadPluginConfig(bytes.NewBufferString(configString), config2)
83
+	pluginCfg = map[string]configapi.AdmissionPluginConfig{"testconfig": {"", expected}}
84
+	err = ReadPluginConfig(pluginCfg, "testconfig", config2)
84 85
 	if err == nil {
85 86
 		t.Fatalf("expected error")
86 87
 	}
... ...
@@ -1,100 +1,93 @@
1 1
 package defaults
2 2
 
3 3
 import (
4
-	"io"
5
-
6 4
 	"github.com/golang/glog"
7
-	"k8s.io/kubernetes/pkg/admission"
8 5
 	kapi "k8s.io/kubernetes/pkg/api"
9
-	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
10 6
 
11 7
 	buildadmission "github.com/openshift/origin/pkg/build/admission"
12 8
 	defaultsapi "github.com/openshift/origin/pkg/build/admission/defaults/api"
13 9
 	"github.com/openshift/origin/pkg/build/admission/defaults/api/validation"
14 10
 	buildapi "github.com/openshift/origin/pkg/build/api"
11
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
15 12
 )
16 13
 
17
-func init() {
18
-	admission.RegisterPlugin("BuildDefaults", func(c clientset.Interface, config io.Reader) (admission.Interface, error) {
19
-
20
-		defaultsConfig, err := getConfig(config)
21
-		if err != nil {
22
-			return nil, err
23
-		}
24
-
25
-		glog.V(5).Infof("Initializing BuildDefaults plugin with config: %#v", defaultsConfig)
26
-		return NewBuildDefaults(defaultsConfig), nil
27
-	})
14
+type BuildDefaults struct {
15
+	config *defaultsapi.BuildDefaultsConfig
28 16
 }
29 17
 
30
-func getConfig(in io.Reader) (*defaultsapi.BuildDefaultsConfig, error) {
31
-	defaultsConfig := &defaultsapi.BuildDefaultsConfig{}
32
-	err := buildadmission.ReadPluginConfig(in, defaultsConfig)
18
+// NewBuildDefaults creates a new BuildDefaults that will apply the defaults specified in the plugin config
19
+func NewBuildDefaults(pluginConfig map[string]configapi.AdmissionPluginConfig) (BuildDefaults, error) {
20
+	config := &defaultsapi.BuildDefaultsConfig{}
21
+	err := buildadmission.ReadPluginConfig(pluginConfig, defaultsapi.BuildDefaultsPlugin, config)
33 22
 	if err != nil {
34
-		return nil, err
23
+		return BuildDefaults{}, err
35 24
 	}
36
-	errs := validation.ValidateBuildDefaultsConfig(defaultsConfig)
25
+	errs := validation.ValidateBuildDefaultsConfig(config)
37 26
 	if len(errs) > 0 {
38
-		return nil, errs.ToAggregate()
27
+		return BuildDefaults{}, errs.ToAggregate()
39 28
 	}
40
-	return defaultsConfig, nil
29
+	glog.V(4).Infof("Initialized build defaults plugin with config: %#v", *config)
30
+	return BuildDefaults{config: config}, nil
41 31
 }
42 32
 
43
-type buildDefaults struct {
44
-	*admission.Handler
45
-	defaultsConfig *defaultsapi.BuildDefaultsConfig
46
-}
47
-
48
-// NewBuildDefaults returns an admission control for builds that sets build defaults
49
-// based on the plugin configuration
50
-func NewBuildDefaults(defaultsConfig *defaultsapi.BuildDefaultsConfig) admission.Interface {
51
-	return &buildDefaults{
52
-		Handler:        admission.NewHandler(admission.Create),
53
-		defaultsConfig: defaultsConfig,
54
-	}
55
-}
56
-
57
-// Admit applies configured build defaults to a pod that is identified
58
-// as a build pod.
59
-func (a *buildDefaults) Admit(attributes admission.Attributes) error {
60
-	if a.defaultsConfig == nil {
61
-		return nil
62
-	}
63
-	if !buildadmission.IsBuildPod(attributes) {
33
+// ApplyDefaults applies configured build defaults to a build pod
34
+func (b BuildDefaults) ApplyDefaults(pod *kapi.Pod) error {
35
+	if b.config == nil {
64 36
 		return nil
65 37
 	}
66
-	build, version, err := buildadmission.GetBuild(attributes)
38
+
39
+	build, version, err := buildadmission.GetBuildFromPod(pod)
67 40
 	if err != nil {
68 41
 		return nil
69 42
 	}
70 43
 
71
-	glog.V(4).Infof("Handling build %s/%s", build.Namespace, build.Name)
44
+	glog.V(4).Infof("Applying defaults to build %s/%s", build.Namespace, build.Name)
45
+
46
+	b.applyBuildDefaults(build)
72 47
 
73
-	a.applyBuildDefaults(build)
48
+	b.applyPodDefaults(pod)
74 49
 
75
-	err = buildadmission.SetBuildLogLevel(attributes, build)
50
+	err = buildadmission.SetPodLogLevelFromBuild(pod, build)
76 51
 	if err != nil {
77 52
 		return err
78 53
 	}
79 54
 
80
-	return buildadmission.SetBuild(attributes, build, version)
55
+	return buildadmission.SetBuildInPod(pod, build, version)
81 56
 }
82 57
 
83
-func (a *buildDefaults) applyBuildDefaults(build *buildapi.Build) {
58
+func (b BuildDefaults) applyPodDefaults(pod *kapi.Pod) {
59
+	if len(b.config.NodeSelector) != 0 && pod.Spec.NodeSelector == nil {
60
+		// only apply nodeselector defaults if the pod has no nodeselector labels
61
+		// already.
62
+		pod.Spec.NodeSelector = map[string]string{}
63
+		for k, v := range b.config.NodeSelector {
64
+			addDefaultNodeSelector(k, v, pod.Spec.NodeSelector)
65
+		}
66
+	}
67
+
68
+	if len(b.config.Annotations) != 0 && pod.Annotations == nil {
69
+		pod.Annotations = map[string]string{}
70
+	}
71
+	for k, v := range b.config.Annotations {
72
+		addDefaultAnnotations(k, v, pod.Annotations)
73
+	}
74
+}
75
+
76
+func (b BuildDefaults) applyBuildDefaults(build *buildapi.Build) {
84 77
 	// Apply default env
85 78
 	buildEnv := getBuildEnv(build)
86
-	for _, envVar := range a.defaultsConfig.Env {
79
+	for _, envVar := range b.config.Env {
87 80
 		glog.V(5).Infof("Adding default environment variable %s=%s to build %s/%s", envVar.Name, envVar.Value, build.Namespace, build.Name)
88 81
 		addDefaultEnvVar(envVar, buildEnv)
89 82
 	}
90 83
 
91 84
 	// Apply default labels
92
-	for _, lbl := range a.defaultsConfig.ImageLabels {
85
+	for _, lbl := range b.config.ImageLabels {
93 86
 		glog.V(5).Infof("Adding default image label %s=%s to build %s/%s", lbl.Name, lbl.Value, build.Namespace, build.Name)
94 87
 		addDefaultLabel(lbl, &build.Spec.Output.ImageLabels)
95 88
 	}
96 89
 
97
-	sourceDefaults := a.defaultsConfig.SourceStrategyDefaults
90
+	sourceDefaults := b.config.SourceStrategyDefaults
98 91
 	sourceStrategy := build.Spec.Strategy.SourceStrategy
99 92
 	if sourceDefaults != nil && sourceDefaults.Incremental != nil && *sourceDefaults.Incremental &&
100 93
 		sourceStrategy != nil && sourceStrategy.Incremental == nil {
... ...
@@ -107,25 +100,25 @@ func (a *buildDefaults) applyBuildDefaults(build *buildapi.Build) {
107 107
 	if build.Spec.Source.Git == nil {
108 108
 		return
109 109
 	}
110
-	if len(a.defaultsConfig.GitHTTPProxy) != 0 {
110
+	if len(b.config.GitHTTPProxy) != 0 {
111 111
 		if build.Spec.Source.Git.HTTPProxy == nil {
112
-			t := a.defaultsConfig.GitHTTPProxy
112
+			t := b.config.GitHTTPProxy
113 113
 			glog.V(5).Infof("Setting default Git HTTP proxy of build %s/%s to %s", build.Namespace, build.Name, t)
114 114
 			build.Spec.Source.Git.HTTPProxy = &t
115 115
 		}
116 116
 	}
117 117
 
118
-	if len(a.defaultsConfig.GitHTTPSProxy) != 0 {
118
+	if len(b.config.GitHTTPSProxy) != 0 {
119 119
 		if build.Spec.Source.Git.HTTPSProxy == nil {
120
-			t := a.defaultsConfig.GitHTTPSProxy
120
+			t := b.config.GitHTTPSProxy
121 121
 			glog.V(5).Infof("Setting default Git HTTPS proxy of build %s/%s to %s", build.Namespace, build.Name, t)
122 122
 			build.Spec.Source.Git.HTTPSProxy = &t
123 123
 		}
124 124
 	}
125 125
 
126
-	if len(a.defaultsConfig.GitNoProxy) != 0 {
126
+	if len(b.config.GitNoProxy) != 0 {
127 127
 		if build.Spec.Source.Git.NoProxy == nil {
128
-			t := a.defaultsConfig.GitNoProxy
128
+			t := b.config.GitNoProxy
129 129
 			glog.V(5).Infof("Setting default Git no proxy of build %s/%s to %s", build.Namespace, build.Name, t)
130 130
 			build.Spec.Source.Git.NoProxy = &t
131 131
 		}
... ...
@@ -168,3 +161,19 @@ func addDefaultLabel(defaultLabel buildapi.ImageLabel, buildLabels *[]buildapi.I
168 168
 		*buildLabels = append(*buildLabels, defaultLabel)
169 169
 	}
170 170
 }
171
+
172
+func addDefaultNodeSelector(k, v string, selectors map[string]string) bool {
173
+	if _, ok := selectors[k]; !ok {
174
+		selectors[k] = v
175
+		return true
176
+	}
177
+	return false
178
+}
179
+
180
+func addDefaultAnnotations(k, v string, annotations map[string]string) bool {
181
+	if _, ok := annotations[k]; !ok {
182
+		annotations[k] = v
183
+		return true
184
+	}
185
+	return false
186
+}
... ...
@@ -21,13 +21,13 @@ func TestProxyDefaults(t *testing.T) {
21 21
 		GitNoProxy:    "no",
22 22
 	}
23 23
 
24
-	admitter := NewBuildDefaults(defaultsConfig)
24
+	admitter := BuildDefaults{defaultsConfig}
25 25
 	pod := u.Pod().WithBuild(t, u.Build().WithDockerStrategy().AsBuild(), "v1")
26
-	err := admitter.Admit(pod.ToAttributes())
26
+	err := admitter.ApplyDefaults((*kapi.Pod)(pod))
27 27
 	if err != nil {
28 28
 		t.Fatalf("unexpected error: %v", err)
29 29
 	}
30
-	build, _, err := buildadmission.GetBuild(pod.ToAttributes())
30
+	build, _, err := buildadmission.GetBuildFromPod((*kapi.Pod)(pod))
31 31
 	if err != nil {
32 32
 		t.Fatalf("unexpected error: %v", err)
33 33
 	}
... ...
@@ -57,13 +57,13 @@ func TestEnvDefaults(t *testing.T) {
57 57
 		},
58 58
 	}
59 59
 
60
-	admitter := NewBuildDefaults(defaultsConfig)
60
+	admitter := BuildDefaults{defaultsConfig}
61 61
 	pod := u.Pod().WithBuild(t, u.Build().WithSourceStrategy().AsBuild(), "v1")
62
-	err := admitter.Admit(pod.ToAttributes())
62
+	err := admitter.ApplyDefaults((*kapi.Pod)(pod))
63 63
 	if err != nil {
64 64
 		t.Fatalf("unexpected error: %v", err)
65 65
 	}
66
-	build, _, err := buildadmission.GetBuild(pod.ToAttributes())
66
+	build, _, err := buildadmission.GetBuildFromPod((*kapi.Pod)(pod))
67 67
 	if err != nil {
68 68
 		t.Fatalf("unexpected error: %v", err)
69 69
 	}
... ...
@@ -100,14 +100,14 @@ func TestIncrementalDefaults(t *testing.T) {
100 100
 		},
101 101
 	}
102 102
 
103
-	admitter := NewBuildDefaults(defaultsConfig)
103
+	admitter := BuildDefaults{defaultsConfig}
104 104
 
105 105
 	pod := u.Pod().WithBuild(t, u.Build().WithSourceStrategy().AsBuild(), "v1")
106
-	err := admitter.Admit(pod.ToAttributes())
106
+	err := admitter.ApplyDefaults((*kapi.Pod)(pod))
107 107
 	if err != nil {
108 108
 		t.Fatalf("unexpected error: %v", err)
109 109
 	}
110
-	build, _, err := buildadmission.GetBuild(pod.ToAttributes())
110
+	build, _, err := buildadmission.GetBuildFromPod((*kapi.Pod)(pod))
111 111
 	if err != nil {
112 112
 		t.Fatalf("unexpected error: %v", err)
113 113
 	}
... ...
@@ -119,11 +119,11 @@ func TestIncrementalDefaults(t *testing.T) {
119 119
 	bool_f := false
120 120
 	build.Spec.Strategy.SourceStrategy.Incremental = &bool_f
121 121
 	pod = u.Pod().WithBuild(t, build, "v1")
122
-	err = admitter.Admit(pod.ToAttributes())
122
+	err = admitter.ApplyDefaults((*kapi.Pod)(pod))
123 123
 	if err != nil {
124 124
 		t.Fatalf("unexpected error: %v", err)
125 125
 	}
126
-	build, _, err = buildadmission.GetBuild(pod.ToAttributes())
126
+	build, _, err = buildadmission.GetBuildFromPod((*kapi.Pod)(pod))
127 127
 	if err != nil {
128 128
 		t.Fatalf("unexpected error: %v", err)
129 129
 	}
... ...
@@ -249,9 +249,9 @@ func TestLabelDefaults(t *testing.T) {
249 249
 			ImageLabels: test.defaultLabels,
250 250
 		}
251 251
 
252
-		admitter := NewBuildDefaults(defaultsConfig)
252
+		admitter := BuildDefaults{defaultsConfig}
253 253
 		pod := u.Pod().WithBuild(t, u.Build().WithImageLabels(test.buildLabels).AsBuild(), "v1")
254
-		err := admitter.Admit(pod.ToAttributes())
254
+		err := admitter.ApplyDefaults((*kapi.Pod)(pod))
255 255
 		if err != nil {
256 256
 			t.Fatalf("unexpected error: %v", err)
257 257
 		}
... ...
@@ -266,3 +266,101 @@ func TestLabelDefaults(t *testing.T) {
266 266
 		}
267 267
 	}
268 268
 }
269
+
270
+func TestBuildDefaultsNodeSelector(t *testing.T) {
271
+	tests := []struct {
272
+		name     string
273
+		build    *buildapi.Build
274
+		defaults map[string]string
275
+		expected map[string]string
276
+	}{
277
+		{
278
+			name:     "build - full add",
279
+			build:    u.Build().AsBuild(),
280
+			defaults: map[string]string{"key1": "default1", "key2": "default2"},
281
+			expected: map[string]string{"key1": "default1", "key2": "default2"},
282
+		},
283
+		{
284
+			name:     "build - ignored",
285
+			build:    u.Build().WithNodeSelector(map[string]string{"key1": "value1"}).AsBuild(),
286
+			defaults: map[string]string{"key1": "default1", "key2": "default2"},
287
+			expected: map[string]string{"key1": "value1"},
288
+		},
289
+		{
290
+			name:     "build - empty(non-nil) nodeselector",
291
+			build:    u.Build().WithNodeSelector(map[string]string{}).AsBuild(),
292
+			defaults: map[string]string{"key1": "default1"},
293
+			expected: map[string]string{},
294
+		},
295
+	}
296
+
297
+	for _, test := range tests {
298
+		defaults := BuildDefaults{config: &defaultsapi.BuildDefaultsConfig{NodeSelector: test.defaults}}
299
+		pod := u.Pod().WithBuild(t, test.build, "v1")
300
+		// normally the pod will have the nodeselectors from the build, due to the pod creation logic
301
+		// in the build controller flow. fake it out here.
302
+		pod.Spec.NodeSelector = test.build.Spec.NodeSelector
303
+		err := defaults.ApplyDefaults((*kapi.Pod)(pod))
304
+		if err != nil {
305
+			t.Errorf("%s: unexpected error: %v", test.name, err)
306
+		}
307
+		if len(pod.Spec.NodeSelector) != len(test.expected) {
308
+			t.Errorf("%s: incorrect number of selectors, expected %v, got %v", test.name, test.expected, pod.Spec.NodeSelector)
309
+		}
310
+		for k, v := range pod.Spec.NodeSelector {
311
+			if ev, ok := test.expected[k]; !ok || ev != v {
312
+				t.Errorf("%s: incorrect selector value for key %s, expected %s, got %s", test.name, k, ev, v)
313
+			}
314
+		}
315
+	}
316
+}
317
+
318
+func TestBuildDefaultsAnnotations(t *testing.T) {
319
+	tests := []struct {
320
+		name        string
321
+		build       *buildapi.Build
322
+		annotations map[string]string
323
+		defaults    map[string]string
324
+		expected    map[string]string
325
+	}{
326
+		{
327
+			name:        "build - nil annotations",
328
+			build:       u.Build().AsBuild(),
329
+			annotations: nil,
330
+			defaults:    map[string]string{"key1": "default1", "key2": "default2"},
331
+			expected:    map[string]string{"key1": "default1", "key2": "default2"},
332
+		},
333
+		{
334
+			name:        "build - full add",
335
+			build:       u.Build().AsBuild(),
336
+			annotations: map[string]string{"key3": "value3"},
337
+			defaults:    map[string]string{"key1": "default1", "key2": "default2"},
338
+			expected:    map[string]string{"key1": "default1", "key2": "default2", "key3": "value3"},
339
+		},
340
+		{
341
+			name:        "build - partial add",
342
+			build:       u.Build().AsBuild(),
343
+			annotations: map[string]string{"key1": "value1"},
344
+			defaults:    map[string]string{"key1": "default1", "key2": "default2"},
345
+			expected:    map[string]string{"key1": "value1", "key2": "default2"},
346
+		},
347
+	}
348
+
349
+	for _, test := range tests {
350
+		defaults := BuildDefaults{config: &defaultsapi.BuildDefaultsConfig{Annotations: test.defaults}}
351
+		pod := u.Pod().WithBuild(t, test.build, "v1")
352
+		pod.Annotations = test.annotations
353
+		err := defaults.ApplyDefaults((*kapi.Pod)(pod))
354
+		if err != nil {
355
+			t.Errorf("%s: unexpected error: %v", test.name, err)
356
+		}
357
+		if len(pod.Annotations) != len(test.expected) {
358
+			t.Errorf("%s: incorrect number of annotations, expected %v, got %v", test.name, test.expected, pod.Annotations)
359
+		}
360
+		for k, v := range pod.Annotations {
361
+			if ev, ok := test.expected[k]; !ok || ev != v {
362
+				t.Errorf("%s: incorrect annotation value for key %s, expected %s, got %s", test.name, k, ev, v)
363
+			}
364
+		}
365
+	}
366
+}
... ...
@@ -7,31 +7,39 @@ import (
7 7
 	buildapi "github.com/openshift/origin/pkg/build/api"
8 8
 )
9 9
 
10
+const BuildDefaultsPlugin = "BuildDefaults"
11
+
10 12
 // BuildDefaultsConfig controls the default information for Builds
11 13
 type BuildDefaultsConfig struct {
12 14
 	unversioned.TypeMeta
13 15
 
14
-	// GitHTTPProxy is the location of the HTTPProxy for Git source
16
+	// gitHTTPProxy is the location of the HTTPProxy for Git source
15 17
 	GitHTTPProxy string
16 18
 
17
-	// GitHTTPSProxy is the location of the HTTPSProxy for Git source
19
+	// gitHTTPSProxy is the location of the HTTPSProxy for Git source
18 20
 	GitHTTPSProxy string
19 21
 
20
-	// GitNoProxy is the list of domains for which the proxy should not be used
22
+	// gitNoProxy is the list of domains for which the proxy should not be used
21 23
 	GitNoProxy string
22 24
 
23
-	// Env is a set of default environment variables that will be applied to the
25
+	// env is a set of default environment variables that will be applied to the
24 26
 	// build if the specified variables do not exist on the build
25 27
 	Env []kapi.EnvVar
26 28
 
27
-	// SourceStrategyDefaults are default values that apply to builds using the
29
+	// sourceStrategyDefaults are default values that apply to builds using the
28 30
 	// source strategy.
29 31
 	SourceStrategyDefaults *SourceStrategyDefaultsConfig
30 32
 
31
-	// ImageLabels is a list of docker labels that are applied to the resulting image.
33
+	// imageLabels is a list of docker labels that are applied to the resulting image.
32 34
 	// User can override a default label by providing a label with the same name in their
33 35
 	// Build/BuildConfig.
34 36
 	ImageLabels []buildapi.ImageLabel
37
+
38
+	// nodeSelector is a selector which must be true for the build pod to fit on a node
39
+	NodeSelector map[string]string
40
+
41
+	// annotations are annotations that will be added to the build pod
42
+	Annotations map[string]string
35 43
 }
36 44
 
37 45
 // SourceStrategyDefaultsConfig contains values that apply to builds using the
... ...
@@ -7,12 +7,14 @@ package v1
7 7
 
8 8
 var map_BuildDefaultsConfig = map[string]string{
9 9
 	"":              "BuildDefaultsConfig controls the default information for Builds",
10
-	"gitHTTPProxy":  "GitHTTPProxy is the location of the HTTPProxy for Git source",
11
-	"gitHTTPSProxy": "GitHTTPSProxy is the location of the HTTPSProxy for Git source",
12
-	"gitNoProxy":    "GitNoProxy is the list of domains for which the proxy should not be used",
13
-	"env":           "Env is a set of default environment variables that will be applied to the build if the specified variables do not exist on the build",
14
-	"sourceStrategyDefaults": "SourceStrategyDefaults are default values that apply to builds using the source strategy.",
15
-	"imageLabels":            "ImageLabels is a list of docker labels that are applied to the resulting image. User can override a default label by providing a label with the same name in their Build/BuildConfig.",
10
+	"gitHTTPProxy":  "gitHTTPProxy is the location of the HTTPProxy for Git source",
11
+	"gitHTTPSProxy": "gitHTTPSProxy is the location of the HTTPSProxy for Git source",
12
+	"gitNoProxy":    "gitNoProxy is the list of domains for which the proxy should not be used",
13
+	"env":           "env is a set of default environment variables that will be applied to the build if the specified variables do not exist on the build",
14
+	"sourceStrategyDefaults": "sourceStrategyDefaults are default values that apply to builds using the source strategy.",
15
+	"imageLabels":            "imageLabels is a list of docker labels that are applied to the resulting image. User can override a default label by providing a label with the same name in their Build/BuildConfig.",
16
+	"nodeSelector":           "nodeSelector is a selector which must be true for the build pod to fit on a node",
17
+	"annotations":            "annotations are annotations that will be added to the build pod",
16 18
 }
17 19
 
18 20
 func (BuildDefaultsConfig) SwaggerDoc() map[string]string {
... ...
@@ -21,7 +23,7 @@ func (BuildDefaultsConfig) SwaggerDoc() map[string]string {
21 21
 
22 22
 var map_SourceStrategyDefaultsConfig = map[string]string{
23 23
 	"":            "SourceStrategyDefaultsConfig contains values that apply to builds using the source strategy.",
24
-	"incremental": "Incremental indicates if s2i build strategies should perform an incremental build or not",
24
+	"incremental": "incremental indicates if s2i build strategies should perform an incremental build or not",
25 25
 }
26 26
 
27 27
 func (SourceStrategyDefaultsConfig) SwaggerDoc() map[string]string {
... ...
@@ -11,34 +11,40 @@ import (
11 11
 type BuildDefaultsConfig struct {
12 12
 	unversioned.TypeMeta `json:",inline"`
13 13
 
14
-	// GitHTTPProxy is the location of the HTTPProxy for Git source
14
+	// gitHTTPProxy is the location of the HTTPProxy for Git source
15 15
 	GitHTTPProxy string `json:"gitHTTPProxy,omitempty"`
16 16
 
17
-	// GitHTTPSProxy is the location of the HTTPSProxy for Git source
17
+	// gitHTTPSProxy is the location of the HTTPSProxy for Git source
18 18
 	GitHTTPSProxy string `json:"gitHTTPSProxy,omitempty"`
19 19
 
20
-	// GitNoProxy is the list of domains for which the proxy should not be used
20
+	// gitNoProxy is the list of domains for which the proxy should not be used
21 21
 	GitNoProxy string `json:"gitNoProxy,omitempty"`
22 22
 
23
-	// Env is a set of default environment variables that will be applied to the
23
+	// env is a set of default environment variables that will be applied to the
24 24
 	// build if the specified variables do not exist on the build
25 25
 	Env []kapi.EnvVar `json:"env,omitempty"`
26 26
 
27
-	// SourceStrategyDefaults are default values that apply to builds using the
27
+	// sourceStrategyDefaults are default values that apply to builds using the
28 28
 	// source strategy.
29 29
 	SourceStrategyDefaults *SourceStrategyDefaultsConfig `json:"sourceStrategyDefaults,omitempty"`
30 30
 
31
-	// ImageLabels is a list of docker labels that are applied to the resulting image.
31
+	// imageLabels is a list of docker labels that are applied to the resulting image.
32 32
 	// User can override a default label by providing a label with the same name in their
33 33
 	// Build/BuildConfig.
34 34
 	ImageLabels []buildapi.ImageLabel `json:"imageLabels,omitempty"`
35
+
36
+	// nodeSelector is a selector which must be true for the build pod to fit on a node
37
+	NodeSelector map[string]string `json:"nodeSelector,omitempty"`
38
+
39
+	// annotations are annotations that will be added to the build pod
40
+	Annotations map[string]string `json:"annotations,omitempty"`
35 41
 }
36 42
 
37 43
 // SourceStrategyDefaultsConfig contains values that apply to builds using the
38 44
 // source strategy.
39 45
 type SourceStrategyDefaultsConfig struct {
40 46
 
41
-	// Incremental indicates if s2i build strategies should perform an incremental
47
+	// incremental indicates if s2i build strategies should perform an incremental
42 48
 	// build or not
43 49
 	Incremental *bool `json:"incremental,omitempty"`
44 50
 }
... ...
@@ -1,18 +1,22 @@
1 1
 package validation
2 2
 
3 3
 import (
4
+	"k8s.io/kubernetes/pkg/api/validation"
4 5
 	"k8s.io/kubernetes/pkg/util/validation/field"
5 6
 
6 7
 	"github.com/openshift/origin/pkg/build/admission/defaults/api"
7 8
 	buildvalidation "github.com/openshift/origin/pkg/build/api/validation"
8 9
 )
9 10
 
11
+// ValidateBuildDefaultsConfig tests required fields for a Build.
10 12
 func ValidateBuildDefaultsConfig(config *api.BuildDefaultsConfig) field.ErrorList {
11 13
 	allErrs := field.ErrorList{}
12 14
 	allErrs = append(allErrs, validateURL(config.GitHTTPProxy, field.NewPath("gitHTTPProxy"))...)
13 15
 	allErrs = append(allErrs, validateURL(config.GitHTTPSProxy, field.NewPath("gitHTTPSProxy"))...)
14 16
 	allErrs = append(allErrs, buildvalidation.ValidateStrategyEnv(config.Env, field.NewPath("env"))...)
15 17
 	allErrs = append(allErrs, buildvalidation.ValidateImageLabels(config.ImageLabels, field.NewPath("imageLabels"))...)
18
+	allErrs = append(allErrs, buildvalidation.ValidateNodeSelector(config.NodeSelector, field.NewPath("nodeSelector"))...)
19
+	allErrs = append(allErrs, validation.ValidateAnnotations(config.Annotations, field.NewPath("annotations"))...)
16 20
 	return allErrs
17 21
 }
18 22
 
... ...
@@ -144,6 +144,38 @@ func TestValidateBuildDefaultsConfig(t *testing.T) {
144 144
 			errField:    "imageLabels[1].name",
145 145
 			errType:     field.ErrorTypeInvalid,
146 146
 		},
147
+		// 9: valid nodeselector
148
+		{
149
+			config: &defaultsapi.BuildDefaultsConfig{
150
+				NodeSelector: map[string]string{"A": "B"},
151
+			},
152
+			errExpected: false,
153
+		},
154
+		// 10: invalid nodeselector
155
+		{
156
+			config: &defaultsapi.BuildDefaultsConfig{
157
+				NodeSelector: map[string]string{"A@B!": "C"},
158
+			},
159
+			errExpected: true,
160
+			errField:    "nodeSelector[A@B!]",
161
+			errType:     field.ErrorTypeInvalid,
162
+		},
163
+		// 11: valid annotation
164
+		{
165
+			config: &defaultsapi.BuildDefaultsConfig{
166
+				Annotations: map[string]string{"A": "B"},
167
+			},
168
+			errExpected: false,
169
+		},
170
+		// 12: invalid annotation
171
+		{
172
+			config: &defaultsapi.BuildDefaultsConfig{
173
+				Annotations: map[string]string{"A B": "C"},
174
+			},
175
+			errExpected: true,
176
+			errField:    "annotations",
177
+			errType:     field.ErrorTypeInvalid,
178
+		},
147 179
 	}
148 180
 
149 181
 	for i, tc := range tests {
... ...
@@ -1,116 +1,93 @@
1 1
 package overrides
2 2
 
3 3
 import (
4
-	"io"
5
-
6 4
 	"github.com/golang/glog"
7
-	"k8s.io/kubernetes/pkg/admission"
8 5
 	kapi "k8s.io/kubernetes/pkg/api"
9
-	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
10 6
 
11 7
 	buildadmission "github.com/openshift/origin/pkg/build/admission"
12 8
 	overridesapi "github.com/openshift/origin/pkg/build/admission/overrides/api"
13 9
 	"github.com/openshift/origin/pkg/build/admission/overrides/api/validation"
14 10
 	buildapi "github.com/openshift/origin/pkg/build/api"
11
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
15 12
 )
16 13
 
17
-func init() {
18
-	admission.RegisterPlugin("BuildOverrides", func(c clientset.Interface, config io.Reader) (admission.Interface, error) {
19
-		overridesConfig, err := getConfig(config)
20
-		if err != nil {
21
-			return nil, err
22
-		}
23
-
24
-		glog.V(5).Infof("Initializing BuildOverrides plugin with config: %#v", overridesConfig)
25
-		return NewBuildOverrides(overridesConfig), nil
26
-	})
14
+type BuildOverrides struct {
15
+	config *overridesapi.BuildOverridesConfig
27 16
 }
28 17
 
29
-func getConfig(in io.Reader) (*overridesapi.BuildOverridesConfig, error) {
30
-	overridesConfig := &overridesapi.BuildOverridesConfig{}
31
-	err := buildadmission.ReadPluginConfig(in, overridesConfig)
18
+// NewBuildOverrides creates a new BuildOverrides that will apply the overrides specified in the plugin config
19
+func NewBuildOverrides(pluginConfig map[string]configapi.AdmissionPluginConfig) (BuildOverrides, error) {
20
+	config := &overridesapi.BuildOverridesConfig{}
21
+	err := buildadmission.ReadPluginConfig(pluginConfig, overridesapi.BuildOverridesPlugin, config)
32 22
 	if err != nil {
33
-		return nil, err
23
+		return BuildOverrides{}, err
34 24
 	}
35
-	errs := validation.ValidateBuildOverridesConfig(overridesConfig)
25
+	errs := validation.ValidateBuildOverridesConfig(config)
36 26
 	if len(errs) > 0 {
37
-		return nil, errs.ToAggregate()
38
-	}
39
-	return overridesConfig, nil
40
-}
41
-
42
-type buildOverrides struct {
43
-	*admission.Handler
44
-	overridesConfig *overridesapi.BuildOverridesConfig
45
-}
46
-
47
-// NewBuildOverrides returns an admission control for builds that overrides
48
-// settings on builds
49
-func NewBuildOverrides(overridesConfig *overridesapi.BuildOverridesConfig) admission.Interface {
50
-	return &buildOverrides{
51
-		Handler:         admission.NewHandler(admission.Create, admission.Update),
52
-		overridesConfig: overridesConfig,
27
+		return BuildOverrides{}, errs.ToAggregate()
53 28
 	}
29
+	glog.V(4).Infof("Initialized build overrides plugin with config: %#v", *config)
30
+	return BuildOverrides{config: config}, nil
54 31
 }
55 32
 
56
-// Admit appplies configured overrides to a build in a build pod
57
-func (a *buildOverrides) Admit(attributes admission.Attributes) error {
58
-	if a.overridesConfig == nil {
59
-		return nil
60
-	}
61
-	if !buildadmission.IsBuildPod(attributes) {
33
+// ApplyOverrides applies configured overrides to a build in a build pod
34
+func (b BuildOverrides) ApplyOverrides(pod *kapi.Pod) error {
35
+	if b.config == nil {
62 36
 		return nil
63 37
 	}
64
-	return a.applyOverrides(attributes)
65
-}
66 38
 
67
-func (a *buildOverrides) applyOverrides(attributes admission.Attributes) error {
68
-	build, version, err := buildadmission.GetBuild(attributes)
39
+	build, version, err := buildadmission.GetBuildFromPod(pod)
69 40
 	if err != nil {
70 41
 		return err
71 42
 	}
72
-	glog.V(4).Infof("Handling build %s/%s", build.Namespace, build.Name)
73 43
 
74
-	if a.overridesConfig.ForcePull {
75
-		if err := applyForcePullToBuild(build, attributes); err != nil {
76
-			return err
44
+	glog.V(4).Infof("Applying overrides to build %s/%s", build.Namespace, build.Name)
45
+
46
+	if b.config.ForcePull {
47
+		if build.Spec.Strategy.DockerStrategy != nil {
48
+			glog.V(5).Infof("Setting docker strategy ForcePull to true in build %s/%s", build.Namespace, build.Name)
49
+			build.Spec.Strategy.DockerStrategy.ForcePull = true
50
+		}
51
+		if build.Spec.Strategy.SourceStrategy != nil {
52
+			glog.V(5).Infof("Setting source strategy ForcePull to true in build %s/%s", build.Namespace, build.Name)
53
+			build.Spec.Strategy.SourceStrategy.ForcePull = true
54
+		}
55
+		if build.Spec.Strategy.CustomStrategy != nil {
56
+			err := applyForcePullToPod(pod)
57
+			if err != nil {
58
+				return err
59
+			}
60
+			glog.V(5).Infof("Setting custom strategy ForcePull to true in build %s/%s", build.Namespace, build.Name)
61
+			build.Spec.Strategy.CustomStrategy.ForcePull = true
77 62
 		}
78 63
 	}
79 64
 
80 65
 	// Apply label overrides
81
-	for _, lbl := range a.overridesConfig.ImageLabels {
66
+	for _, lbl := range b.config.ImageLabels {
82 67
 		glog.V(5).Infof("Overriding image label %s=%s in build %s/%s", lbl.Name, lbl.Value, build.Namespace, build.Name)
83 68
 		overrideLabel(lbl, &build.Spec.Output.ImageLabels)
84 69
 	}
85 70
 
86
-	return buildadmission.SetBuild(attributes, build, version)
87
-}
88
-
89
-func applyForcePullToBuild(build *buildapi.Build, attributes admission.Attributes) error {
90
-	if build.Spec.Strategy.DockerStrategy != nil {
91
-		glog.V(5).Infof("Setting docker strategy ForcePull to true in build %s/%s", build.Namespace, build.Name)
92
-		build.Spec.Strategy.DockerStrategy.ForcePull = true
71
+	if len(b.config.NodeSelector) != 0 && pod.Spec.NodeSelector == nil {
72
+		pod.Spec.NodeSelector = map[string]string{}
93 73
 	}
94
-	if build.Spec.Strategy.SourceStrategy != nil {
95
-		glog.V(5).Infof("Setting source strategy ForcePull to true in build %s/%s", build.Namespace, build.Name)
96
-		build.Spec.Strategy.SourceStrategy.ForcePull = true
74
+	for k, v := range b.config.NodeSelector {
75
+		glog.V(5).Infof("Adding override nodeselector %s=%s to build pod %s/%s", k, v, pod.Namespace, pod.Name)
76
+		pod.Spec.NodeSelector[k] = v
97 77
 	}
98
-	if build.Spec.Strategy.CustomStrategy != nil {
99
-		err := applyForcePullToPod(attributes)
100
-		if err != nil {
101
-			return err
102
-		}
103
-		glog.V(5).Infof("Setting custom strategy ForcePull to true in build %s/%s", build.Namespace, build.Name)
104
-		build.Spec.Strategy.CustomStrategy.ForcePull = true
78
+
79
+	if len(b.config.Annotations) != 0 && pod.Annotations == nil {
80
+		pod.Annotations = map[string]string{}
105 81
 	}
106
-	return nil
82
+	for k, v := range b.config.Annotations {
83
+		glog.V(5).Infof("Adding override annotation %s=%s to build pod %s/%s", k, v, pod.Namespace, pod.Name)
84
+		pod.Annotations[k] = v
85
+	}
86
+
87
+	return buildadmission.SetBuildInPod(pod, build, version)
107 88
 }
108 89
 
109
-func applyForcePullToPod(attributes admission.Attributes) error {
110
-	pod, err := buildadmission.GetPod(attributes)
111
-	if err != nil {
112
-		return err
113
-	}
90
+func applyForcePullToPod(pod *kapi.Pod) error {
114 91
 	for i := range pod.Spec.InitContainers {
115 92
 		glog.V(5).Infof("Setting ImagePullPolicy to PullAlways on init container %s of pod %s/%s", pod.Spec.InitContainers[i].Name, pod.Namespace, pod.Name)
116 93
 		pod.Spec.InitContainers[i].ImagePullPolicy = kapi.PullAlways
... ...
@@ -36,9 +36,9 @@ func TestBuildOverrideForcePull(t *testing.T) {
36 36
 	ops := []admission.Operation{admission.Create, admission.Update}
37 37
 	for _, test := range tests {
38 38
 		for _, op := range ops {
39
-			overrides := NewBuildOverrides(&overridesapi.BuildOverridesConfig{ForcePull: true})
39
+			overrides := BuildOverrides{config: &overridesapi.BuildOverridesConfig{ForcePull: true}}
40 40
 			pod := u.Pod().WithBuild(t, test.build, "v1")
41
-			err := overrides.Admit(pod.ToAttributes())
41
+			err := overrides.ApplyOverrides((*kapi.Pod)(pod))
42 42
 			if err != nil {
43 43
 				t.Errorf("%s: unexpected error: %v", test.name, err)
44 44
 			}
... ...
@@ -184,9 +184,9 @@ func TestLabelOverrides(t *testing.T) {
184 184
 			ImageLabels: test.overrideLabels,
185 185
 		}
186 186
 
187
-		admitter := NewBuildOverrides(overridesConfig)
187
+		admitter := BuildOverrides{overridesConfig}
188 188
 		pod := u.Pod().WithBuild(t, u.Build().WithImageLabels(test.buildLabels).AsBuild(), "v1")
189
-		err := admitter.Admit(pod.ToAttributes())
189
+		err := admitter.ApplyOverrides((*kapi.Pod)(pod))
190 190
 		if err != nil {
191 191
 			t.Fatalf("unexpected error: %v", err)
192 192
 		}
... ...
@@ -198,3 +198,95 @@ func TestLabelOverrides(t *testing.T) {
198 198
 		}
199 199
 	}
200 200
 }
201
+
202
+func TestBuildOverrideNodeSelector(t *testing.T) {
203
+	tests := []struct {
204
+		name      string
205
+		build     *buildapi.Build
206
+		overrides map[string]string
207
+		expected  map[string]string
208
+	}{
209
+		{
210
+			name:      "build - full override",
211
+			build:     u.Build().WithNodeSelector(map[string]string{"key1": "value1"}).AsBuild(),
212
+			overrides: map[string]string{"key1": "override1", "key2": "override2"},
213
+			expected:  map[string]string{"key1": "override1", "key2": "override2"},
214
+		},
215
+		{
216
+			name:      "build - partial override",
217
+			build:     u.Build().WithNodeSelector(map[string]string{"key1": "value1"}).AsBuild(),
218
+			overrides: map[string]string{"key2": "override2"},
219
+			expected:  map[string]string{"key1": "value1", "key2": "override2"},
220
+		},
221
+	}
222
+
223
+	for _, test := range tests {
224
+		overrides := BuildOverrides{config: &overridesapi.BuildOverridesConfig{NodeSelector: test.overrides}}
225
+		pod := u.Pod().WithBuild(t, test.build, "v1")
226
+		// normally the pod will have the nodeselectors from the build, due to the pod creation logic
227
+		// in the build controller flow. fake it out here.
228
+		pod.Spec.NodeSelector = test.build.Spec.NodeSelector
229
+		err := overrides.ApplyOverrides((*kapi.Pod)(pod))
230
+		if err != nil {
231
+			t.Errorf("%s: unexpected error: %v", test.name, err)
232
+		}
233
+		if len(pod.Spec.NodeSelector) != len(test.expected) {
234
+			t.Errorf("%s: incorrect number of selectors, expected %v, got %v", test.name, test.expected, pod.Spec.NodeSelector)
235
+		}
236
+		for k, v := range pod.Spec.NodeSelector {
237
+			if ev, ok := test.expected[k]; !ok || ev != v {
238
+				t.Errorf("%s: incorrect selector value for key %s, expected %s, got %s", test.name, k, ev, v)
239
+			}
240
+		}
241
+	}
242
+}
243
+
244
+func TestBuildOverrideAnnotations(t *testing.T) {
245
+	tests := []struct {
246
+		name        string
247
+		build       *buildapi.Build
248
+		annotations map[string]string
249
+		overrides   map[string]string
250
+		expected    map[string]string
251
+	}{
252
+		{
253
+			name:        "build - nil annotations",
254
+			build:       u.Build().AsBuild(),
255
+			annotations: nil,
256
+			overrides:   map[string]string{"key1": "override1", "key2": "override2"},
257
+			expected:    map[string]string{"key1": "override1", "key2": "override2"},
258
+		},
259
+		{
260
+			name:        "build - full override",
261
+			build:       u.Build().AsBuild(),
262
+			annotations: map[string]string{"key1": "value1"},
263
+			overrides:   map[string]string{"key1": "override1", "key2": "override2"},
264
+			expected:    map[string]string{"key1": "override1", "key2": "override2"},
265
+		},
266
+		{
267
+			name:        "build - partial override",
268
+			build:       u.Build().AsBuild(),
269
+			annotations: map[string]string{"key1": "value1"},
270
+			overrides:   map[string]string{"key2": "override2"},
271
+			expected:    map[string]string{"key1": "value1", "key2": "override2"},
272
+		},
273
+	}
274
+
275
+	for _, test := range tests {
276
+		overrides := BuildOverrides{config: &overridesapi.BuildOverridesConfig{Annotations: test.overrides}}
277
+		pod := u.Pod().WithBuild(t, test.build, "v1")
278
+		pod.Annotations = test.annotations
279
+		err := overrides.ApplyOverrides((*kapi.Pod)(pod))
280
+		if err != nil {
281
+			t.Errorf("%s: unexpected error: %v", test.name, err)
282
+		}
283
+		if len(pod.Annotations) != len(test.expected) {
284
+			t.Errorf("%s: incorrect number of annotations, expected %v, got %v", test.name, test.expected, pod.Annotations)
285
+		}
286
+		for k, v := range pod.Annotations {
287
+			if ev, ok := test.expected[k]; !ok || ev != v {
288
+				t.Errorf("%s: incorrect annotation value for key %s, expected %s, got %s", test.name, k, ev, v)
289
+			}
290
+		}
291
+	}
292
+}
... ...
@@ -6,15 +6,23 @@ import (
6 6
 	buildapi "github.com/openshift/origin/pkg/build/api"
7 7
 )
8 8
 
9
+const BuildOverridesPlugin = "BuildOverrides"
10
+
9 11
 // BuildOverridesConfig controls override settings for builds
10 12
 type BuildOverridesConfig struct {
11 13
 	unversioned.TypeMeta
12 14
 
13
-	// ForcePull indicates whether the build strategy should always be set to ForcePull=true
15
+	// forcePull indicates whether the build strategy should always be set to ForcePull=true
14 16
 	ForcePull bool
15 17
 
16
-	// ImageLabels is a list of docker labels that are applied to the resulting image.
18
+	// imageLabels is a list of docker labels that are applied to the resulting image.
17 19
 	// If user provided a label in their Build/BuildConfig with the same name as one in this
18 20
 	// list, the user's label will be overwritten.
19 21
 	ImageLabels []buildapi.ImageLabel
22
+
23
+	// nodeSelector is a selector which must be true for the build pod to fit on a node
24
+	NodeSelector map[string]string
25
+
26
+	// annotations are annotations that will be added to the build pod
27
+	Annotations map[string]string
20 28
 }
... ...
@@ -6,9 +6,11 @@ package v1
6 6
 // ==== DO NOT EDIT THIS FILE MANUALLY ====
7 7
 
8 8
 var map_BuildOverridesConfig = map[string]string{
9
-	"":            "BuildOverridesConfig controls override settings for builds",
10
-	"forcePull":   "ForcePull indicates whether the build strategy should always be set to ForcePull=true",
11
-	"imageLabels": "ImageLabels is a list of docker labels that are applied to the resulting image. If user provided a label in their Build/BuildConfig with the same name as one in this list, the user's label will be overwritten.",
9
+	"":             "BuildOverridesConfig controls override settings for builds",
10
+	"forcePull":    "forcePull indicates whether the build strategy should always be set to ForcePull=true",
11
+	"imageLabels":  "imageLabels is a list of docker labels that are applied to the resulting image. If user provided a label in their Build/BuildConfig with the same name as one in this list, the user's label will be overwritten.",
12
+	"nodeSelector": "nodeSelector is a selector which must be true for the build pod to fit on a node",
13
+	"annotations":  "annotations are annotations that will be added to the build pod",
12 14
 }
13 15
 
14 16
 func (BuildOverridesConfig) SwaggerDoc() map[string]string {
... ...
@@ -10,11 +10,17 @@ import (
10 10
 type BuildOverridesConfig struct {
11 11
 	unversioned.TypeMeta `json:",inline"`
12 12
 
13
-	// ForcePull indicates whether the build strategy should always be set to ForcePull=true
13
+	// forcePull indicates whether the build strategy should always be set to ForcePull=true
14 14
 	ForcePull bool `json:"forcePull"`
15 15
 
16
-	// ImageLabels is a list of docker labels that are applied to the resulting image.
16
+	// imageLabels is a list of docker labels that are applied to the resulting image.
17 17
 	// If user provided a label in their Build/BuildConfig with the same name as one in this
18 18
 	// list, the user's label will be overwritten.
19 19
 	ImageLabels []buildapi.ImageLabel `json:"imageLabels,omitempty"`
20
+
21
+	// nodeSelector is a selector which must be true for the build pod to fit on a node
22
+	NodeSelector map[string]string `json:"nodeSelector,omitempty"`
23
+
24
+	// annotations are annotations that will be added to the build pod
25
+	Annotations map[string]string `json:"annotations,omitempty"`
20 26
 }
... ...
@@ -1,6 +1,7 @@
1 1
 package validation
2 2
 
3 3
 import (
4
+	"k8s.io/kubernetes/pkg/api/validation"
4 5
 	"k8s.io/kubernetes/pkg/util/validation/field"
5 6
 
6 7
 	"github.com/openshift/origin/pkg/build/admission/overrides/api"
... ...
@@ -10,5 +11,8 @@ import (
10 10
 func ValidateBuildOverridesConfig(config *api.BuildOverridesConfig) field.ErrorList {
11 11
 	allErrs := field.ErrorList{}
12 12
 	allErrs = append(allErrs, buildvalidation.ValidateImageLabels(config.ImageLabels, field.NewPath("imageLabels"))...)
13
+	allErrs = append(allErrs, buildvalidation.ValidateNodeSelector(config.NodeSelector, field.NewPath("nodeSelector"))...)
14
+	allErrs = append(allErrs, validation.ValidateAnnotations(config.Annotations, field.NewPath("annotations"))...)
15
+
13 16
 	return allErrs
14 17
 }
15 18
new file mode 100644
... ...
@@ -0,0 +1,131 @@
0
+package validation
1
+
2
+import (
3
+	"testing"
4
+
5
+	"k8s.io/kubernetes/pkg/util/validation/field"
6
+
7
+	overridesapi "github.com/openshift/origin/pkg/build/admission/overrides/api"
8
+	buildapi "github.com/openshift/origin/pkg/build/api"
9
+)
10
+
11
+func TestValidateBuildOverridesConfig(t *testing.T) {
12
+	tests := []struct {
13
+		config      *overridesapi.BuildOverridesConfig
14
+		errExpected bool
15
+		errField    string
16
+		errType     field.ErrorType
17
+	}{
18
+		// 0: label: valid
19
+		{
20
+			config: &overridesapi.BuildOverridesConfig{
21
+				ImageLabels: []buildapi.ImageLabel{
22
+					{
23
+						Name:  "A",
24
+						Value: "B",
25
+					},
26
+				},
27
+			},
28
+			errExpected: false,
29
+		},
30
+		// 1: label: empty name
31
+		{
32
+			config: &overridesapi.BuildOverridesConfig{
33
+				ImageLabels: []buildapi.ImageLabel{
34
+					{
35
+						Name:  "",
36
+						Value: "empty",
37
+					},
38
+				},
39
+			},
40
+			errExpected: true,
41
+			errField:    "imageLabels[0].name",
42
+			errType:     field.ErrorTypeRequired,
43
+		},
44
+		// 2: label: bad name
45
+		{
46
+			config: &overridesapi.BuildOverridesConfig{
47
+				ImageLabels: []buildapi.ImageLabel{
48
+					{
49
+						Name:  "\tč;",
50
+						Value: "????",
51
+					},
52
+				},
53
+			},
54
+			errExpected: true,
55
+			errField:    "imageLabels[0].name",
56
+			errType:     field.ErrorTypeInvalid,
57
+		},
58
+		// 3: duplicate label
59
+		{
60
+			config: &overridesapi.BuildOverridesConfig{
61
+				ImageLabels: []buildapi.ImageLabel{
62
+					{
63
+						Name:  "name",
64
+						Value: "Jan",
65
+					},
66
+					{
67
+						Name:  "name",
68
+						Value: "Elvis",
69
+					},
70
+				},
71
+			},
72
+			errExpected: true,
73
+			errField:    "imageLabels[1].name",
74
+			errType:     field.ErrorTypeInvalid,
75
+		},
76
+		// 4: valid nodeselector
77
+		{
78
+			config: &overridesapi.BuildOverridesConfig{
79
+				NodeSelector: map[string]string{"A": "B"},
80
+			},
81
+			errExpected: false,
82
+		},
83
+		// 5: invalid nodeselector
84
+		{
85
+			config: &overridesapi.BuildOverridesConfig{
86
+				NodeSelector: map[string]string{"A@B!": "C"},
87
+			},
88
+			errExpected: true,
89
+			errField:    "nodeSelector[A@B!]",
90
+			errType:     field.ErrorTypeInvalid,
91
+		},
92
+		// 6: valid annotation
93
+		{
94
+			config: &overridesapi.BuildOverridesConfig{
95
+				Annotations: map[string]string{"A": "B"},
96
+			},
97
+			errExpected: false,
98
+		},
99
+		// 7: invalid annotation
100
+		{
101
+			config: &overridesapi.BuildOverridesConfig{
102
+				Annotations: map[string]string{"A B": "C"},
103
+			},
104
+			errExpected: true,
105
+			errField:    "annotations",
106
+			errType:     field.ErrorTypeInvalid,
107
+		},
108
+	}
109
+
110
+	for i, tc := range tests {
111
+		result := ValidateBuildOverridesConfig(tc.config)
112
+		if !tc.errExpected {
113
+			if len(result) > 0 {
114
+				t.Errorf("%d: unexpected error: %v", i, result.ToAggregate())
115
+			}
116
+			continue
117
+		}
118
+		if tc.errExpected && len(result) == 0 {
119
+			t.Errorf("%d: did not get expected error", i)
120
+			continue
121
+		}
122
+		err := result[0]
123
+		if err.Type != tc.errType {
124
+			t.Errorf("%d: unexpected error type: %v", i, err.Type)
125
+		}
126
+		if err.Field != tc.errField {
127
+			t.Errorf("%d: unexpected error field: %v", i, err.Field)
128
+		}
129
+	}
130
+}
... ...
@@ -54,6 +54,11 @@ func (b *TestBuild) WithImageLabels(labels []buildapi.ImageLabel) *TestBuild {
54 54
 	return b
55 55
 }
56 56
 
57
+func (b *TestBuild) WithNodeSelector(ns map[string]string) *TestBuild {
58
+	b.Spec.NodeSelector = ns
59
+	return b
60
+}
61
+
57 62
 func (b *TestBuild) AsBuild() *buildapi.Build {
58 63
 	return (*buildapi.Build)(b)
59 64
 }
... ...
@@ -112,6 +112,12 @@ type CommonSpec struct {
112 112
 	// be active on a node before the system actively tries to terminate the
113 113
 	// build; value must be positive integer.
114 114
 	CompletionDeadlineSeconds *int64
115
+
116
+	// NodeSelector is a selector which must be true for the build pod to fit on a node
117
+	// If nil, it can be overridden by default build nodeselector values for the cluster.
118
+	// If set to an empty map or a map with any values, default build nodeselector values
119
+	// are ignored.
120
+	NodeSelector map[string]string
115 121
 }
116 122
 
117 123
 // BuildTriggerCause holds information about a triggered build. It is used for
... ...
@@ -43,6 +43,7 @@
43 43
 		ImageSource
44 44
 		ImageSourcePath
45 45
 		JenkinsPipelineBuildStrategy
46
+		OptionalNodeSelector
46 47
 		ProxyConfig
47 48
 		SecretBuildSource
48 49
 		SecretSpec
... ...
@@ -216,33 +217,37 @@ func (*JenkinsPipelineBuildStrategy) Descriptor() ([]byte, []int) {
216 216
 	return fileDescriptorGenerated, []int{33}
217 217
 }
218 218
 
219
+func (m *OptionalNodeSelector) Reset()                    { *m = OptionalNodeSelector{} }
220
+func (*OptionalNodeSelector) ProtoMessage()               {}
221
+func (*OptionalNodeSelector) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{34} }
222
+
219 223
 func (m *ProxyConfig) Reset()                    { *m = ProxyConfig{} }
220 224
 func (*ProxyConfig) ProtoMessage()               {}
221
-func (*ProxyConfig) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{34} }
225
+func (*ProxyConfig) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{35} }
222 226
 
223 227
 func (m *SecretBuildSource) Reset()                    { *m = SecretBuildSource{} }
224 228
 func (*SecretBuildSource) ProtoMessage()               {}
225
-func (*SecretBuildSource) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{35} }
229
+func (*SecretBuildSource) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{36} }
226 230
 
227 231
 func (m *SecretSpec) Reset()                    { *m = SecretSpec{} }
228 232
 func (*SecretSpec) ProtoMessage()               {}
229
-func (*SecretSpec) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{36} }
233
+func (*SecretSpec) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{37} }
230 234
 
231 235
 func (m *SourceBuildStrategy) Reset()                    { *m = SourceBuildStrategy{} }
232 236
 func (*SourceBuildStrategy) ProtoMessage()               {}
233
-func (*SourceBuildStrategy) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{37} }
237
+func (*SourceBuildStrategy) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{38} }
234 238
 
235 239
 func (m *SourceControlUser) Reset()                    { *m = SourceControlUser{} }
236 240
 func (*SourceControlUser) ProtoMessage()               {}
237
-func (*SourceControlUser) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{38} }
241
+func (*SourceControlUser) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{39} }
238 242
 
239 243
 func (m *SourceRevision) Reset()                    { *m = SourceRevision{} }
240 244
 func (*SourceRevision) ProtoMessage()               {}
241
-func (*SourceRevision) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{39} }
245
+func (*SourceRevision) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{40} }
242 246
 
243 247
 func (m *WebHookTrigger) Reset()                    { *m = WebHookTrigger{} }
244 248
 func (*WebHookTrigger) ProtoMessage()               {}
245
-func (*WebHookTrigger) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{40} }
249
+func (*WebHookTrigger) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{41} }
246 250
 
247 251
 func init() {
248 252
 	proto.RegisterType((*BinaryBuildRequestOptions)(nil), "github.com.openshift.origin.pkg.build.api.v1.BinaryBuildRequestOptions")
... ...
@@ -279,6 +284,7 @@ func init() {
279 279
 	proto.RegisterType((*ImageSource)(nil), "github.com.openshift.origin.pkg.build.api.v1.ImageSource")
280 280
 	proto.RegisterType((*ImageSourcePath)(nil), "github.com.openshift.origin.pkg.build.api.v1.ImageSourcePath")
281 281
 	proto.RegisterType((*JenkinsPipelineBuildStrategy)(nil), "github.com.openshift.origin.pkg.build.api.v1.JenkinsPipelineBuildStrategy")
282
+	proto.RegisterType((*OptionalNodeSelector)(nil), "github.com.openshift.origin.pkg.build.api.v1.OptionalNodeSelector")
282 283
 	proto.RegisterType((*ProxyConfig)(nil), "github.com.openshift.origin.pkg.build.api.v1.ProxyConfig")
283 284
 	proto.RegisterType((*SecretBuildSource)(nil), "github.com.openshift.origin.pkg.build.api.v1.SecretBuildSource")
284 285
 	proto.RegisterType((*SecretSpec)(nil), "github.com.openshift.origin.pkg.build.api.v1.SecretSpec")
... ...
@@ -1324,6 +1330,16 @@ func (m *CommonSpec) MarshalTo(data []byte) (int, error) {
1324 1324
 		i++
1325 1325
 		i = encodeVarintGenerated(data, i, uint64(*m.CompletionDeadlineSeconds))
1326 1326
 	}
1327
+	if m.NodeSelector != nil {
1328
+		data[i] = 0x4a
1329
+		i++
1330
+		i = encodeVarintGenerated(data, i, uint64(m.NodeSelector.Size()))
1331
+		n42, err := m.NodeSelector.MarshalTo(data[i:])
1332
+		if err != nil {
1333
+			return 0, err
1334
+		}
1335
+		i += n42
1336
+	}
1327 1337
 	return i, nil
1328 1338
 }
1329 1339
 
... ...
@@ -1345,20 +1361,20 @@ func (m *CustomBuildStrategy) MarshalTo(data []byte) (int, error) {
1345 1345
 	data[i] = 0xa
1346 1346
 	i++
1347 1347
 	i = encodeVarintGenerated(data, i, uint64(m.From.Size()))
1348
-	n42, err := m.From.MarshalTo(data[i:])
1348
+	n43, err := m.From.MarshalTo(data[i:])
1349 1349
 	if err != nil {
1350 1350
 		return 0, err
1351 1351
 	}
1352
-	i += n42
1352
+	i += n43
1353 1353
 	if m.PullSecret != nil {
1354 1354
 		data[i] = 0x12
1355 1355
 		i++
1356 1356
 		i = encodeVarintGenerated(data, i, uint64(m.PullSecret.Size()))
1357
-		n43, err := m.PullSecret.MarshalTo(data[i:])
1357
+		n44, err := m.PullSecret.MarshalTo(data[i:])
1358 1358
 		if err != nil {
1359 1359
 			return 0, err
1360 1360
 		}
1361
-		i += n43
1361
+		i += n44
1362 1362
 	}
1363 1363
 	if len(m.Env) > 0 {
1364 1364
 		for _, msg := range m.Env {
... ...
@@ -1426,21 +1442,21 @@ func (m *DockerBuildStrategy) MarshalTo(data []byte) (int, error) {
1426 1426
 		data[i] = 0xa
1427 1427
 		i++
1428 1428
 		i = encodeVarintGenerated(data, i, uint64(m.From.Size()))
1429
-		n44, err := m.From.MarshalTo(data[i:])
1429
+		n45, err := m.From.MarshalTo(data[i:])
1430 1430
 		if err != nil {
1431 1431
 			return 0, err
1432 1432
 		}
1433
-		i += n44
1433
+		i += n45
1434 1434
 	}
1435 1435
 	if m.PullSecret != nil {
1436 1436
 		data[i] = 0x12
1437 1437
 		i++
1438 1438
 		i = encodeVarintGenerated(data, i, uint64(m.PullSecret.Size()))
1439
-		n45, err := m.PullSecret.MarshalTo(data[i:])
1439
+		n46, err := m.PullSecret.MarshalTo(data[i:])
1440 1440
 		if err != nil {
1441 1441
 			return 0, err
1442 1442
 		}
1443
-		i += n45
1443
+		i += n46
1444 1444
 	}
1445 1445
 	data[i] = 0x18
1446 1446
 	i++
... ...
@@ -1496,11 +1512,11 @@ func (m *GenericWebHookCause) MarshalTo(data []byte) (int, error) {
1496 1496
 		data[i] = 0xa
1497 1497
 		i++
1498 1498
 		i = encodeVarintGenerated(data, i, uint64(m.Revision.Size()))
1499
-		n46, err := m.Revision.MarshalTo(data[i:])
1499
+		n47, err := m.Revision.MarshalTo(data[i:])
1500 1500
 		if err != nil {
1501 1501
 			return 0, err
1502 1502
 		}
1503
-		i += n46
1503
+		i += n47
1504 1504
 	}
1505 1505
 	data[i] = 0x12
1506 1506
 	i++
... ...
@@ -1532,11 +1548,11 @@ func (m *GenericWebHookEvent) MarshalTo(data []byte) (int, error) {
1532 1532
 		data[i] = 0x12
1533 1533
 		i++
1534 1534
 		i = encodeVarintGenerated(data, i, uint64(m.Git.Size()))
1535
-		n47, err := m.Git.MarshalTo(data[i:])
1535
+		n48, err := m.Git.MarshalTo(data[i:])
1536 1536
 		if err != nil {
1537 1537
 			return 0, err
1538 1538
 		}
1539
-		i += n47
1539
+		i += n48
1540 1540
 	}
1541 1541
 	if len(m.Env) > 0 {
1542 1542
 		for _, msg := range m.Env {
... ...
@@ -1579,11 +1595,11 @@ func (m *GitBuildSource) MarshalTo(data []byte) (int, error) {
1579 1579
 	data[i] = 0x1a
1580 1580
 	i++
1581 1581
 	i = encodeVarintGenerated(data, i, uint64(m.ProxyConfig.Size()))
1582
-	n48, err := m.ProxyConfig.MarshalTo(data[i:])
1582
+	n49, err := m.ProxyConfig.MarshalTo(data[i:])
1583 1583
 	if err != nil {
1584 1584
 		return 0, err
1585 1585
 	}
1586
-	i += n48
1586
+	i += n49
1587 1587
 	return i, nil
1588 1588
 }
1589 1589
 
... ...
@@ -1606,11 +1622,11 @@ func (m *GitHubWebHookCause) MarshalTo(data []byte) (int, error) {
1606 1606
 		data[i] = 0xa
1607 1607
 		i++
1608 1608
 		i = encodeVarintGenerated(data, i, uint64(m.Revision.Size()))
1609
-		n49, err := m.Revision.MarshalTo(data[i:])
1609
+		n50, err := m.Revision.MarshalTo(data[i:])
1610 1610
 		if err != nil {
1611 1611
 			return 0, err
1612 1612
 		}
1613
-		i += n49
1613
+		i += n50
1614 1614
 	}
1615 1615
 	data[i] = 0x12
1616 1616
 	i++
... ...
@@ -1637,19 +1653,19 @@ func (m *GitInfo) MarshalTo(data []byte) (int, error) {
1637 1637
 	data[i] = 0xa
1638 1638
 	i++
1639 1639
 	i = encodeVarintGenerated(data, i, uint64(m.GitBuildSource.Size()))
1640
-	n50, err := m.GitBuildSource.MarshalTo(data[i:])
1640
+	n51, err := m.GitBuildSource.MarshalTo(data[i:])
1641 1641
 	if err != nil {
1642 1642
 		return 0, err
1643 1643
 	}
1644
-	i += n50
1644
+	i += n51
1645 1645
 	data[i] = 0x12
1646 1646
 	i++
1647 1647
 	i = encodeVarintGenerated(data, i, uint64(m.GitSourceRevision.Size()))
1648
-	n51, err := m.GitSourceRevision.MarshalTo(data[i:])
1648
+	n52, err := m.GitSourceRevision.MarshalTo(data[i:])
1649 1649
 	if err != nil {
1650 1650
 		return 0, err
1651 1651
 	}
1652
-	i += n51
1652
+	i += n52
1653 1653
 	return i, nil
1654 1654
 }
1655 1655
 
... ...
@@ -1675,19 +1691,19 @@ func (m *GitSourceRevision) MarshalTo(data []byte) (int, error) {
1675 1675
 	data[i] = 0x12
1676 1676
 	i++
1677 1677
 	i = encodeVarintGenerated(data, i, uint64(m.Author.Size()))
1678
-	n52, err := m.Author.MarshalTo(data[i:])
1678
+	n53, err := m.Author.MarshalTo(data[i:])
1679 1679
 	if err != nil {
1680 1680
 		return 0, err
1681 1681
 	}
1682
-	i += n52
1682
+	i += n53
1683 1683
 	data[i] = 0x1a
1684 1684
 	i++
1685 1685
 	i = encodeVarintGenerated(data, i, uint64(m.Committer.Size()))
1686
-	n53, err := m.Committer.MarshalTo(data[i:])
1686
+	n54, err := m.Committer.MarshalTo(data[i:])
1687 1687
 	if err != nil {
1688 1688
 		return 0, err
1689 1689
 	}
1690
-	i += n53
1690
+	i += n54
1691 1691
 	data[i] = 0x22
1692 1692
 	i++
1693 1693
 	i = encodeVarintGenerated(data, i, uint64(len(m.Message)))
... ...
@@ -1718,11 +1734,11 @@ func (m *ImageChangeCause) MarshalTo(data []byte) (int, error) {
1718 1718
 		data[i] = 0x12
1719 1719
 		i++
1720 1720
 		i = encodeVarintGenerated(data, i, uint64(m.FromRef.Size()))
1721
-		n54, err := m.FromRef.MarshalTo(data[i:])
1721
+		n55, err := m.FromRef.MarshalTo(data[i:])
1722 1722
 		if err != nil {
1723 1723
 			return 0, err
1724 1724
 		}
1725
-		i += n54
1725
+		i += n55
1726 1726
 	}
1727 1727
 	return i, nil
1728 1728
 }
... ...
@@ -1750,11 +1766,11 @@ func (m *ImageChangeTrigger) MarshalTo(data []byte) (int, error) {
1750 1750
 		data[i] = 0x12
1751 1751
 		i++
1752 1752
 		i = encodeVarintGenerated(data, i, uint64(m.From.Size()))
1753
-		n55, err := m.From.MarshalTo(data[i:])
1753
+		n56, err := m.From.MarshalTo(data[i:])
1754 1754
 		if err != nil {
1755 1755
 			return 0, err
1756 1756
 		}
1757
-		i += n55
1757
+		i += n56
1758 1758
 	}
1759 1759
 	return i, nil
1760 1760
 }
... ...
@@ -1803,11 +1819,11 @@ func (m *ImageSource) MarshalTo(data []byte) (int, error) {
1803 1803
 	data[i] = 0xa
1804 1804
 	i++
1805 1805
 	i = encodeVarintGenerated(data, i, uint64(m.From.Size()))
1806
-	n56, err := m.From.MarshalTo(data[i:])
1806
+	n57, err := m.From.MarshalTo(data[i:])
1807 1807
 	if err != nil {
1808 1808
 		return 0, err
1809 1809
 	}
1810
-	i += n56
1810
+	i += n57
1811 1811
 	if len(m.Paths) > 0 {
1812 1812
 		for _, msg := range m.Paths {
1813 1813
 			data[i] = 0x12
... ...
@@ -1824,11 +1840,11 @@ func (m *ImageSource) MarshalTo(data []byte) (int, error) {
1824 1824
 		data[i] = 0x1a
1825 1825
 		i++
1826 1826
 		i = encodeVarintGenerated(data, i, uint64(m.PullSecret.Size()))
1827
-		n57, err := m.PullSecret.MarshalTo(data[i:])
1827
+		n58, err := m.PullSecret.MarshalTo(data[i:])
1828 1828
 		if err != nil {
1829 1829
 			return 0, err
1830 1830
 		}
1831
-		i += n57
1831
+		i += n58
1832 1832
 	}
1833 1833
 	return i, nil
1834 1834
 }
... ...
@@ -1885,6 +1901,41 @@ func (m *JenkinsPipelineBuildStrategy) MarshalTo(data []byte) (int, error) {
1885 1885
 	return i, nil
1886 1886
 }
1887 1887
 
1888
+func (m OptionalNodeSelector) Marshal() (data []byte, err error) {
1889
+	size := m.Size()
1890
+	data = make([]byte, size)
1891
+	n, err := m.MarshalTo(data)
1892
+	if err != nil {
1893
+		return nil, err
1894
+	}
1895
+	return data[:n], nil
1896
+}
1897
+
1898
+func (m OptionalNodeSelector) MarshalTo(data []byte) (int, error) {
1899
+	var i int
1900
+	_ = i
1901
+	var l int
1902
+	_ = l
1903
+	if len(m) > 0 {
1904
+		for k := range m {
1905
+			data[i] = 0xa
1906
+			i++
1907
+			v := m[k]
1908
+			mapSize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v)))
1909
+			i = encodeVarintGenerated(data, i, uint64(mapSize))
1910
+			data[i] = 0xa
1911
+			i++
1912
+			i = encodeVarintGenerated(data, i, uint64(len(k)))
1913
+			i += copy(data[i:], k)
1914
+			data[i] = 0x12
1915
+			i++
1916
+			i = encodeVarintGenerated(data, i, uint64(len(v)))
1917
+			i += copy(data[i:], v)
1918
+		}
1919
+	}
1920
+	return i, nil
1921
+}
1922
+
1888 1923
 func (m *ProxyConfig) Marshal() (data []byte, err error) {
1889 1924
 	size := m.Size()
1890 1925
 	data = make([]byte, size)
... ...
@@ -1939,11 +1990,11 @@ func (m *SecretBuildSource) MarshalTo(data []byte) (int, error) {
1939 1939
 	data[i] = 0xa
1940 1940
 	i++
1941 1941
 	i = encodeVarintGenerated(data, i, uint64(m.Secret.Size()))
1942
-	n58, err := m.Secret.MarshalTo(data[i:])
1942
+	n59, err := m.Secret.MarshalTo(data[i:])
1943 1943
 	if err != nil {
1944 1944
 		return 0, err
1945 1945
 	}
1946
-	i += n58
1946
+	i += n59
1947 1947
 	data[i] = 0x12
1948 1948
 	i++
1949 1949
 	i = encodeVarintGenerated(data, i, uint64(len(m.DestinationDir)))
... ...
@@ -1969,11 +2020,11 @@ func (m *SecretSpec) MarshalTo(data []byte) (int, error) {
1969 1969
 	data[i] = 0xa
1970 1970
 	i++
1971 1971
 	i = encodeVarintGenerated(data, i, uint64(m.SecretSource.Size()))
1972
-	n59, err := m.SecretSource.MarshalTo(data[i:])
1972
+	n60, err := m.SecretSource.MarshalTo(data[i:])
1973 1973
 	if err != nil {
1974 1974
 		return 0, err
1975 1975
 	}
1976
-	i += n59
1976
+	i += n60
1977 1977
 	data[i] = 0x12
1978 1978
 	i++
1979 1979
 	i = encodeVarintGenerated(data, i, uint64(len(m.MountPath)))
... ...
@@ -1999,20 +2050,20 @@ func (m *SourceBuildStrategy) MarshalTo(data []byte) (int, error) {
1999 1999
 	data[i] = 0xa
2000 2000
 	i++
2001 2001
 	i = encodeVarintGenerated(data, i, uint64(m.From.Size()))
2002
-	n60, err := m.From.MarshalTo(data[i:])
2002
+	n61, err := m.From.MarshalTo(data[i:])
2003 2003
 	if err != nil {
2004 2004
 		return 0, err
2005 2005
 	}
2006
-	i += n60
2006
+	i += n61
2007 2007
 	if m.PullSecret != nil {
2008 2008
 		data[i] = 0x12
2009 2009
 		i++
2010 2010
 		i = encodeVarintGenerated(data, i, uint64(m.PullSecret.Size()))
2011
-		n61, err := m.PullSecret.MarshalTo(data[i:])
2011
+		n62, err := m.PullSecret.MarshalTo(data[i:])
2012 2012
 		if err != nil {
2013 2013
 			return 0, err
2014 2014
 		}
2015
-		i += n61
2015
+		i += n62
2016 2016
 	}
2017 2017
 	if len(m.Env) > 0 {
2018 2018
 		for _, msg := range m.Env {
... ...
@@ -2052,11 +2103,11 @@ func (m *SourceBuildStrategy) MarshalTo(data []byte) (int, error) {
2052 2052
 		data[i] = 0x3a
2053 2053
 		i++
2054 2054
 		i = encodeVarintGenerated(data, i, uint64(m.RuntimeImage.Size()))
2055
-		n62, err := m.RuntimeImage.MarshalTo(data[i:])
2055
+		n63, err := m.RuntimeImage.MarshalTo(data[i:])
2056 2056
 		if err != nil {
2057 2057
 			return 0, err
2058 2058
 		}
2059
-		i += n62
2059
+		i += n63
2060 2060
 	}
2061 2061
 	if len(m.RuntimeArtifacts) > 0 {
2062 2062
 		for _, msg := range m.RuntimeArtifacts {
... ...
@@ -2122,11 +2173,11 @@ func (m *SourceRevision) MarshalTo(data []byte) (int, error) {
2122 2122
 		data[i] = 0x12
2123 2123
 		i++
2124 2124
 		i = encodeVarintGenerated(data, i, uint64(m.Git.Size()))
2125
-		n63, err := m.Git.MarshalTo(data[i:])
2125
+		n64, err := m.Git.MarshalTo(data[i:])
2126 2126
 		if err != nil {
2127 2127
 			return 0, err
2128 2128
 		}
2129
-		i += n63
2129
+		i += n64
2130 2130
 	}
2131 2131
 	return i, nil
2132 2132
 }
... ...
@@ -2572,6 +2623,10 @@ func (m *CommonSpec) Size() (n int) {
2572 2572
 	if m.CompletionDeadlineSeconds != nil {
2573 2573
 		n += 1 + sovGenerated(uint64(*m.CompletionDeadlineSeconds))
2574 2574
 	}
2575
+	if m.NodeSelector != nil {
2576
+		l = m.NodeSelector.Size()
2577
+		n += 1 + l + sovGenerated(uint64(l))
2578
+	}
2575 2579
 	return n
2576 2580
 }
2577 2581
 
... ...
@@ -2777,6 +2832,20 @@ func (m *JenkinsPipelineBuildStrategy) Size() (n int) {
2777 2777
 	return n
2778 2778
 }
2779 2779
 
2780
+func (m OptionalNodeSelector) Size() (n int) {
2781
+	var l int
2782
+	_ = l
2783
+	if len(m) > 0 {
2784
+		for k, v := range m {
2785
+			_ = k
2786
+			_ = v
2787
+			mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + len(v) + sovGenerated(uint64(len(v)))
2788
+			n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize))
2789
+		}
2790
+	}
2791
+	return n
2792
+}
2793
+
2780 2794
 func (m *ProxyConfig) Size() (n int) {
2781 2795
 	var l int
2782 2796
 	_ = l
... ...
@@ -3156,6 +3225,7 @@ func (this *CommonSpec) String() string {
3156 3156
 		`Resources:` + strings.Replace(strings.Replace(this.Resources.String(), "ResourceRequirements", "k8s_io_kubernetes_pkg_api_v1.ResourceRequirements", 1), `&`, ``, 1) + `,`,
3157 3157
 		`PostCommit:` + strings.Replace(strings.Replace(this.PostCommit.String(), "BuildPostCommitSpec", "BuildPostCommitSpec", 1), `&`, ``, 1) + `,`,
3158 3158
 		`CompletionDeadlineSeconds:` + valueToStringGenerated(this.CompletionDeadlineSeconds) + `,`,
3159
+		`NodeSelector:` + strings.Replace(fmt.Sprintf("%v", this.NodeSelector), "OptionalNodeSelector", "OptionalNodeSelector", 1) + `,`,
3159 3160
 		`}`,
3160 3161
 	}, "")
3161 3162
 	return s
... ...
@@ -6935,6 +7005,39 @@ func (m *CommonSpec) Unmarshal(data []byte) error {
6935 6935
 				}
6936 6936
 			}
6937 6937
 			m.CompletionDeadlineSeconds = &v
6938
+		case 9:
6939
+			if wireType != 2 {
6940
+				return fmt.Errorf("proto: wrong wireType = %d for field NodeSelector", wireType)
6941
+			}
6942
+			var msglen int
6943
+			for shift := uint(0); ; shift += 7 {
6944
+				if shift >= 64 {
6945
+					return ErrIntOverflowGenerated
6946
+				}
6947
+				if iNdEx >= l {
6948
+					return io.ErrUnexpectedEOF
6949
+				}
6950
+				b := data[iNdEx]
6951
+				iNdEx++
6952
+				msglen |= (int(b) & 0x7F) << shift
6953
+				if b < 0x80 {
6954
+					break
6955
+				}
6956
+			}
6957
+			if msglen < 0 {
6958
+				return ErrInvalidLengthGenerated
6959
+			}
6960
+			postIndex := iNdEx + msglen
6961
+			if postIndex > l {
6962
+				return io.ErrUnexpectedEOF
6963
+			}
6964
+			if m.NodeSelector == nil {
6965
+				m.NodeSelector = OptionalNodeSelector{}
6966
+			}
6967
+			if err := m.NodeSelector.Unmarshal(data[iNdEx:postIndex]); err != nil {
6968
+				return err
6969
+			}
6970
+			iNdEx = postIndex
6938 6971
 		default:
6939 6972
 			iNdEx = preIndex
6940 6973
 			skippy, err := skipGenerated(data[iNdEx:])
... ...
@@ -8891,6 +8994,167 @@ func (m *JenkinsPipelineBuildStrategy) Unmarshal(data []byte) error {
8891 8891
 	}
8892 8892
 	return nil
8893 8893
 }
8894
+func (m *OptionalNodeSelector) Unmarshal(data []byte) error {
8895
+	l := len(data)
8896
+	iNdEx := 0
8897
+	for iNdEx < l {
8898
+		preIndex := iNdEx
8899
+		var wire uint64
8900
+		for shift := uint(0); ; shift += 7 {
8901
+			if shift >= 64 {
8902
+				return ErrIntOverflowGenerated
8903
+			}
8904
+			if iNdEx >= l {
8905
+				return io.ErrUnexpectedEOF
8906
+			}
8907
+			b := data[iNdEx]
8908
+			iNdEx++
8909
+			wire |= (uint64(b) & 0x7F) << shift
8910
+			if b < 0x80 {
8911
+				break
8912
+			}
8913
+		}
8914
+		fieldNum := int32(wire >> 3)
8915
+		wireType := int(wire & 0x7)
8916
+		if wireType == 4 {
8917
+			return fmt.Errorf("proto: OptionalNodeSelector: wiretype end group for non-group")
8918
+		}
8919
+		if fieldNum <= 0 {
8920
+			return fmt.Errorf("proto: OptionalNodeSelector: illegal tag %d (wire type %d)", fieldNum, wire)
8921
+		}
8922
+		switch fieldNum {
8923
+		case 1:
8924
+			if wireType != 2 {
8925
+				return fmt.Errorf("proto: wrong wireType = %d for field Items", wireType)
8926
+			}
8927
+			var msglen int
8928
+			for shift := uint(0); ; shift += 7 {
8929
+				if shift >= 64 {
8930
+					return ErrIntOverflowGenerated
8931
+				}
8932
+				if iNdEx >= l {
8933
+					return io.ErrUnexpectedEOF
8934
+				}
8935
+				b := data[iNdEx]
8936
+				iNdEx++
8937
+				msglen |= (int(b) & 0x7F) << shift
8938
+				if b < 0x80 {
8939
+					break
8940
+				}
8941
+			}
8942
+			if msglen < 0 {
8943
+				return ErrInvalidLengthGenerated
8944
+			}
8945
+			postIndex := iNdEx + msglen
8946
+			if postIndex > l {
8947
+				return io.ErrUnexpectedEOF
8948
+			}
8949
+			var keykey uint64
8950
+			for shift := uint(0); ; shift += 7 {
8951
+				if shift >= 64 {
8952
+					return ErrIntOverflowGenerated
8953
+				}
8954
+				if iNdEx >= l {
8955
+					return io.ErrUnexpectedEOF
8956
+				}
8957
+				b := data[iNdEx]
8958
+				iNdEx++
8959
+				keykey |= (uint64(b) & 0x7F) << shift
8960
+				if b < 0x80 {
8961
+					break
8962
+				}
8963
+			}
8964
+			var stringLenmapkey uint64
8965
+			for shift := uint(0); ; shift += 7 {
8966
+				if shift >= 64 {
8967
+					return ErrIntOverflowGenerated
8968
+				}
8969
+				if iNdEx >= l {
8970
+					return io.ErrUnexpectedEOF
8971
+				}
8972
+				b := data[iNdEx]
8973
+				iNdEx++
8974
+				stringLenmapkey |= (uint64(b) & 0x7F) << shift
8975
+				if b < 0x80 {
8976
+					break
8977
+				}
8978
+			}
8979
+			intStringLenmapkey := int(stringLenmapkey)
8980
+			if intStringLenmapkey < 0 {
8981
+				return ErrInvalidLengthGenerated
8982
+			}
8983
+			postStringIndexmapkey := iNdEx + intStringLenmapkey
8984
+			if postStringIndexmapkey > l {
8985
+				return io.ErrUnexpectedEOF
8986
+			}
8987
+			mapkey := string(data[iNdEx:postStringIndexmapkey])
8988
+			iNdEx = postStringIndexmapkey
8989
+			var valuekey uint64
8990
+			for shift := uint(0); ; shift += 7 {
8991
+				if shift >= 64 {
8992
+					return ErrIntOverflowGenerated
8993
+				}
8994
+				if iNdEx >= l {
8995
+					return io.ErrUnexpectedEOF
8996
+				}
8997
+				b := data[iNdEx]
8998
+				iNdEx++
8999
+				valuekey |= (uint64(b) & 0x7F) << shift
9000
+				if b < 0x80 {
9001
+					break
9002
+				}
9003
+			}
9004
+			var stringLenmapvalue uint64
9005
+			for shift := uint(0); ; shift += 7 {
9006
+				if shift >= 64 {
9007
+					return ErrIntOverflowGenerated
9008
+				}
9009
+				if iNdEx >= l {
9010
+					return io.ErrUnexpectedEOF
9011
+				}
9012
+				b := data[iNdEx]
9013
+				iNdEx++
9014
+				stringLenmapvalue |= (uint64(b) & 0x7F) << shift
9015
+				if b < 0x80 {
9016
+					break
9017
+				}
9018
+			}
9019
+			intStringLenmapvalue := int(stringLenmapvalue)
9020
+			if intStringLenmapvalue < 0 {
9021
+				return ErrInvalidLengthGenerated
9022
+			}
9023
+			postStringIndexmapvalue := iNdEx + intStringLenmapvalue
9024
+			if postStringIndexmapvalue > l {
9025
+				return io.ErrUnexpectedEOF
9026
+			}
9027
+			mapvalue := string(data[iNdEx:postStringIndexmapvalue])
9028
+			iNdEx = postStringIndexmapvalue
9029
+			if *m == nil {
9030
+				*m = make(map[string]string)
9031
+			}
9032
+			(*m)[mapkey] = mapvalue
9033
+			iNdEx = postIndex
9034
+		default:
9035
+			iNdEx = preIndex
9036
+			skippy, err := skipGenerated(data[iNdEx:])
9037
+			if err != nil {
9038
+				return err
9039
+			}
9040
+			if skippy < 0 {
9041
+				return ErrInvalidLengthGenerated
9042
+			}
9043
+			if (iNdEx + skippy) > l {
9044
+				return io.ErrUnexpectedEOF
9045
+			}
9046
+			iNdEx += skippy
9047
+		}
9048
+	}
9049
+
9050
+	if iNdEx > l {
9051
+		return io.ErrUnexpectedEOF
9052
+	}
9053
+	return nil
9054
+}
8894 9055
 func (m *ProxyConfig) Unmarshal(data []byte) error {
8895 9056
 	l := len(data)
8896 9057
 	iNdEx := 0
... ...
@@ -9952,202 +10216,208 @@ var (
9952 9952
 )
9953 9953
 
9954 9954
 var fileDescriptorGenerated = []byte{
9955
-	// 3141 bytes of a gzipped FileDescriptorProto
9956
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe4, 0x1a, 0x4b, 0x6c, 0x24, 0x47,
9957
-	0x35, 0x3d, 0x33, 0x1e, 0xcf, 0xd4, 0x78, 0x6d, 0x6f, 0x79, 0x93, 0xf5, 0x3a, 0xc9, 0x26, 0xe9,
9958
-	0x04, 0x14, 0x94, 0x64, 0x46, 0x76, 0x3e, 0x24, 0xca, 0x87, 0x78, 0xc6, 0xbb, 0x89, 0x8d, 0xb3,
9959
-	0x6b, 0x9e, 0x9d, 0x0f, 0x8b, 0x00, 0xb5, 0xc7, 0x35, 0xe3, 0x8e, 0x67, 0xba, 0x87, 0xee, 0x1e,
9960
-	0x27, 0x96, 0x40, 0x8a, 0x40, 0x48, 0xe1, 0xc6, 0xef, 0xc0, 0x05, 0x50, 0x24, 0x3e, 0x42, 0x39,
9961
-	0x20, 0x3e, 0x07, 0x24, 0x2e, 0x20, 0x71, 0xc8, 0x31, 0x47, 0x4e, 0x11, 0x49, 0x0e, 0x9c, 0xb9,
9962
-	0xe6, 0x44, 0x7d, 0x5e, 0x75, 0x57, 0xf7, 0x8c, 0x9d, 0x9d, 0xf6, 0x3a, 0x42, 0xe2, 0x30, 0x96,
9963
-	0xfb, 0xbd, 0x57, 0xef, 0x55, 0xbd, 0x7a, 0xf5, 0x7e, 0x55, 0xe4, 0x99, 0xae, 0x1b, 0xed, 0x0f,
9964
-	0x77, 0xeb, 0x6d, 0xbf, 0xdf, 0xf0, 0x07, 0xcc, 0x0b, 0xf7, 0xdd, 0x4e, 0xd4, 0xf0, 0x03, 0xb7,
9965
-	0xeb, 0x7a, 0x8d, 0xc1, 0x41, 0xb7, 0xb1, 0x3b, 0x74, 0x7b, 0x7b, 0x0d, 0x67, 0xe0, 0x36, 0x0e,
9966
-	0x97, 0x1b, 0x5d, 0xe6, 0xb1, 0xc0, 0x89, 0xd8, 0x5e, 0x7d, 0x10, 0xf8, 0x91, 0x4f, 0x1f, 0x4e,
9967
-	0x46, 0xd7, 0xe3, 0xd1, 0x75, 0x35, 0xba, 0xce, 0x47, 0xd7, 0xe5, 0xe8, 0x3a, 0x1f, 0x5d, 0x3f,
9968
-	0x5c, 0x5e, 0x7a, 0xc4, 0x90, 0xd5, 0xf5, 0xbb, 0x7e, 0x43, 0x32, 0xd9, 0x1d, 0x76, 0xe4, 0x97,
9969
-	0xfc, 0x90, 0xff, 0x29, 0xe6, 0x4b, 0x8f, 0x1f, 0x3c, 0x19, 0xd6, 0x5d, 0xbf, 0x71, 0x30, 0xdc,
9970
-	0x65, 0x81, 0xc7, 0x22, 0x16, 0xca, 0x09, 0x89, 0xa9, 0x0c, 0xbd, 0x43, 0x16, 0x84, 0xae, 0xef,
9971
-	0xb1, 0xbd, 0xec, 0x9c, 0x96, 0x1e, 0x3e, 0x7e, 0xd8, 0xe8, 0x0a, 0x96, 0x1e, 0x19, 0x4f, 0x1d,
9972
-	0x0c, 0xbd, 0xc8, 0xed, 0xb3, 0x11, 0xf2, 0xe5, 0xf1, 0xe4, 0xc3, 0xc8, 0xed, 0x35, 0x5c, 0x2f,
9973
-	0x0a, 0xa3, 0x20, 0x3b, 0xc4, 0xfe, 0x4b, 0x89, 0x5c, 0x6a, 0xba, 0x9e, 0x13, 0x1c, 0x35, 0x85,
9974
-	0x32, 0x80, 0x7d, 0x6b, 0xc8, 0xc2, 0xe8, 0xfa, 0x20, 0xe2, 0xd3, 0x0f, 0xe9, 0x6b, 0xa4, 0xd2,
9975
-	0x67, 0x91, 0xb3, 0xe7, 0x44, 0xce, 0xa2, 0x75, 0xaf, 0xf5, 0x60, 0x6d, 0xe5, 0xc1, 0xba, 0x92,
9976
-	0x51, 0x4f, 0x64, 0x48, 0x55, 0x2a, 0x25, 0xd6, 0xaf, 0xef, 0xbe, 0xce, 0xda, 0xd1, 0x4b, 0x7c,
9977
-	0x4c, 0x93, 0xbe, 0xf7, 0xc1, 0x3d, 0xb7, 0x7d, 0xf4, 0xc1, 0x3d, 0x24, 0x81, 0x41, 0xcc, 0x8d,
9978
-	0x7e, 0x9e, 0x94, 0x9d, 0xf0, 0xaa, 0xdb, 0x63, 0x8b, 0x05, 0xce, 0xb7, 0xda, 0x9c, 0x45, 0xea,
9979
-	0xf2, 0xaa, 0x84, 0x02, 0x62, 0xe9, 0x13, 0x64, 0x36, 0x60, 0x87, 0xae, 0xd0, 0x66, 0xcb, 0xef,
9980
-	0xf7, 0xdd, 0x68, 0xb1, 0x98, 0xa6, 0x57, 0x50, 0xc8, 0x50, 0xd1, 0xa7, 0xc8, 0x9c, 0x86, 0xbc,
9981
-	0xc4, 0xc2, 0xd0, 0xe9, 0xb2, 0xc5, 0x92, 0x1c, 0x38, 0x87, 0x03, 0xa7, 0x11, 0x0c, 0x59, 0x3a,
9982
-	0xda, 0x24, 0x54, 0x83, 0x56, 0x87, 0xd1, 0xbe, 0x1f, 0x5c, 0x73, 0xfa, 0x6c, 0x71, 0x4a, 0x8e,
9983
-	0x8e, 0x17, 0x95, 0x60, 0x60, 0x0c, 0x35, 0xbd, 0x42, 0x16, 0xd2, 0xd0, 0x2b, 0x7d, 0xc7, 0xed,
9984
-	0x2d, 0x96, 0x25, 0x93, 0x05, 0x64, 0x52, 0x33, 0x50, 0x30, 0x8e, 0x9e, 0x7e, 0x99, 0xdc, 0x9e,
9985
-	0x5e, 0x57, 0xc4, 0xd4, 0x6c, 0xa6, 0x25, 0xa3, 0xdb, 0x91, 0xd1, 0xb9, 0x14, 0x12, 0xc6, 0x8f,
9986
-	0xa1, 0xd7, 0xc8, 0x1d, 0x23, 0x08, 0x35, 0xad, 0x8a, 0xe4, 0x76, 0x07, 0x72, 0x9b, 0x4d, 0x63,
9987
-	0xe1, 0x98, 0x51, 0xf6, 0xd3, 0xe4, 0xbc, 0x61, 0x39, 0xdb, 0xfe, 0x30, 0x68, 0x33, 0x63, 0x5f,
9988
-	0xad, 0x93, 0xf6, 0xd5, 0xfe, 0x65, 0x81, 0x4c, 0xc9, 0x71, 0x67, 0x68, 0x63, 0x5f, 0x25, 0xa5,
9989
-	0x70, 0xc0, 0xda, 0xd2, 0xc2, 0x6a, 0x2b, 0x5f, 0xac, 0x4f, 0xe2, 0x0e, 0xea, 0x6a, 0x51, 0x7c,
9990
-	0x78, 0x73, 0x06, 0x85, 0x94, 0xc4, 0x17, 0x48, 0x96, 0xd4, 0x21, 0xe5, 0x30, 0x72, 0xa2, 0x61,
9991
-	0x28, 0xcd, 0xb1, 0xb6, 0xf2, 0x54, 0x1e, 0xe6, 0x92, 0x41, 0xa2, 0x21, 0xf5, 0x0d, 0xc8, 0xd8,
9992
-	0xfe, 0x43, 0x81, 0xd4, 0x24, 0x5d, 0xcb, 0xf7, 0x3a, 0x6e, 0xf7, 0x0c, 0xf5, 0xf4, 0xcd, 0x94,
9993
-	0x9e, 0x9e, 0xcd, 0xb1, 0x14, 0x35, 0xc5, 0x63, 0xb5, 0xd5, 0xcd, 0x68, 0xeb, 0x4b, 0xf9, 0x45,
9994
-	0x9c, 0xac, 0xb3, 0xf7, 0x2d, 0x32, 0x67, 0x50, 0x6f, 0xba, 0x61, 0x44, 0xbf, 0x3e, 0xa2, 0xb7,
9995
-	0xc6, 0x09, 0x7a, 0x33, 0x7c, 0x77, 0x5d, 0x0c, 0x97, 0xea, 0x9b, 0x47, 0x71, 0x15, 0x0d, 0x31,
9996
-	0x94, 0xf7, 0x0d, 0x32, 0xe5, 0x46, 0xac, 0x1f, 0x72, 0xed, 0x15, 0x73, 0x1a, 0x82, 0x9a, 0x6c,
9997
-	0xf3, 0x1c, 0x4a, 0x99, 0x5a, 0x17, 0xfc, 0x40, 0xb1, 0xb5, 0xff, 0x58, 0x48, 0x2d, 0x49, 0x68,
9998
-	0x95, 0x7a, 0xa4, 0x12, 0x71, 0x86, 0x5d, 0x3e, 0x53, 0xbe, 0x24, 0x21, 0xf6, 0xf9, 0x1c, 0x62,
9999
-	0x77, 0x14, 0x8b, 0x2d, 0xbf, 0xe7, 0xb6, 0x8f, 0x92, 0x35, 0x22, 0x38, 0x84, 0x58, 0x06, 0x5d,
10000
-	0x25, 0x55, 0x1e, 0x72, 0x14, 0x21, 0xfa, 0xeb, 0xfb, 0x91, 0xbc, 0x0a, 0x1a, 0xf1, 0x09, 0xf7,
10001
-	0x1c, 0x2a, 0x86, 0x68, 0x08, 0x24, 0xa3, 0x68, 0x8f, 0x10, 0x3e, 0xb5, 0xbe, 0xef, 0x89, 0x05,
10002
-	0xa0, 0x19, 0x3c, 0x39, 0xd9, 0xa4, 0x5b, 0xf1, 0xf8, 0xc4, 0x9e, 0x13, 0x18, 0x18, 0xfc, 0xed,
10003
-	0x0d, 0xee, 0x9a, 0xb2, 0x46, 0x43, 0x1f, 0x27, 0xb5, 0x9e, 0x13, 0x46, 0xaf, 0xa8, 0xfd, 0x95,
10004
-	0xb6, 0x50, 0x4c, 0x7c, 0xf1, 0x66, 0x82, 0x02, 0x93, 0xce, 0xfe, 0x87, 0x45, 0xaa, 0x92, 0xd9,
10005
-	0x67, 0x61, 0x4d, 0xaf, 0xa5, 0xad, 0xe9, 0xd1, 0x1c, 0xdb, 0x7a, 0x8c, 0x1d, 0x11, 0x52, 0x51,
10006
-	0xab, 0xf0, 0xbb, 0xf6, 0xdb, 0x25, 0xb4, 0x29, 0xfe, 0xa1, 0x43, 0x7d, 0x83, 0x54, 0xdb, 0xbe,
10007
-	0x17, 0x39, 0x2e, 0xcf, 0x0f, 0xd0, 0x77, 0x9f, 0xd7, 0x7b, 0xdc, 0xd2, 0x08, 0x48, 0x68, 0x84,
10008
-	0xa7, 0xef, 0xf8, 0xbd, 0x9e, 0xff, 0x86, 0xb4, 0x88, 0x4a, 0x72, 0x26, 0xaf, 0x4a, 0x28, 0x20,
10009
-	0x96, 0x3e, 0x4c, 0x2a, 0x03, 0x11, 0x41, 0x7c, 0x3c, 0xfe, 0x95, 0x44, 0x01, 0x5b, 0x08, 0x87,
10010
-	0x98, 0x82, 0x3e, 0x46, 0x66, 0x42, 0xd7, 0x6b, 0xb3, 0x6d, 0xc6, 0x25, 0xed, 0x85, 0x32, 0x68,
10011
-	0x17, 0x9b, 0xf3, 0x9c, 0x7a, 0x66, 0xdb, 0x80, 0x43, 0x8a, 0x8a, 0xab, 0xad, 0x2a, 0xbf, 0x77,
10012
-	0x5c, 0x8c, 0xd4, 0xb5, 0x95, 0x87, 0x6e, 0x72, 0x5b, 0xc4, 0x90, 0xe6, 0x39, 0xb1, 0xca, 0x6d,
10013
-	0xcd, 0x01, 0x12, 0x66, 0x74, 0x85, 0x10, 0x91, 0x6a, 0x71, 0xff, 0xd2, 0x1f, 0x84, 0x32, 0x7e,
10014
-	0x57, 0x12, 0xeb, 0xdb, 0x89, 0x31, 0x60, 0x50, 0xd1, 0x87, 0x48, 0x95, 0xeb, 0xa8, 0xb7, 0xc9,
10015
-	0xd5, 0x14, 0xca, 0x48, 0x5d, 0x54, 0x02, 0x76, 0x34, 0x10, 0x12, 0x3c, 0xad, 0x13, 0xd2, 0x73,
10016
-	0x79, 0x58, 0x6d, 0x1e, 0xf1, 0x19, 0xca, 0x48, 0x5c, 0x6c, 0xce, 0x0a, 0xe6, 0x9b, 0x31, 0x14,
10017
-	0x0c, 0x0a, 0xa1, 0x76, 0xcf, 0x7f, 0xc3, 0xe1, 0x89, 0x50, 0x35, 0xad, 0xf6, 0x6b, 0xfe, 0xab,
10018
-	0x1c, 0x0a, 0x88, 0xa5, 0x9f, 0x23, 0xd3, 0xb8, 0xc8, 0x45, 0x22, 0x99, 0xd6, 0x44, 0xd2, 0xa3,
10019
-	0x2d, 0x5c, 0xe3, 0xec, 0xdf, 0xe9, 0x28, 0x73, 0x7d, 0x18, 0x0d, 0x86, 0x11, 0x4f, 0x5c, 0x0a,
10020
-	0x91, 0x8f, 0x96, 0xfd, 0xc8, 0xcd, 0xc4, 0x17, 0x60, 0x1d, 0x16, 0x30, 0xae, 0xae, 0x66, 0x99,
10021
-	0x0b, 0x28, 0xec, 0xf8, 0xc0, 0x19, 0xd0, 0x5d, 0x42, 0x06, 0xc3, 0x70, 0x9f, 0xef, 0x4f, 0xc0,
10022
-	0x22, 0x0c, 0x2c, 0x2b, 0x27, 0xb3, 0xdb, 0xf4, 0xdb, 0x4e, 0x2f, 0xcb, 0x53, 0x6a, 0x62, 0x2b,
10023
-	0xe6, 0x04, 0x06, 0x57, 0xea, 0x93, 0x9a, 0xdb, 0xe7, 0x09, 0xdb, 0xa6, 0xb3, 0xcb, 0x7a, 0xc2,
10024
-	0xb6, 0x8a, 0x93, 0xfb, 0x94, 0xf5, 0x98, 0x41, 0xe2, 0x09, 0x12, 0x58, 0x08, 0xa6, 0x04, 0xfb,
10025
-	0xbb, 0x16, 0x59, 0x90, 0xba, 0xda, 0xf2, 0xc3, 0x48, 0x25, 0x43, 0xd2, 0x1d, 0x73, 0x55, 0x0b,
10026
-	0xdf, 0xe3, 0x78, 0x7b, 0xd2, 0x1b, 0x57, 0x95, 0xaa, 0x5b, 0x0a, 0x04, 0x1a, 0x47, 0xef, 0x22,
10027
-	0x25, 0x27, 0xe8, 0xaa, 0xa3, 0x5d, 0x6d, 0x56, 0x44, 0x8c, 0x5c, 0xe5, 0xdf, 0x20, 0xa1, 0x62,
10028
-	0x5f, 0xc3, 0x76, 0xe0, 0x0e, 0x46, 0x12, 0xdc, 0x6d, 0x09, 0x05, 0xc4, 0xda, 0x1f, 0x4f, 0x91,
10029
-	0x19, 0x33, 0x55, 0x3f, 0xc3, 0xbc, 0xa0, 0x43, 0x2a, 0x3a, 0xf5, 0xc3, 0x2d, 0x7c, 0x66, 0x32,
10030
-	0xed, 0xaa, 0x9c, 0x10, 0x90, 0x47, 0x73, 0x46, 0x9c, 0x79, 0xfd, 0x05, 0x31, 0x6f, 0xbe, 0x91,
10031
-	0xf3, 0x18, 0x6a, 0xd8, 0x5e, 0xf3, 0x48, 0xaa, 0x1f, 0x23, 0xc4, 0x84, 0x16, 0x78, 0x81, 0x0b,
10032
-	0x98, 0xdf, 0xc9, 0xb0, 0x82, 0x11, 0xe6, 0x3c, 0xad, 0x2e, 0x75, 0x02, 0xbf, 0x2f, 0x9d, 0xcb,
10033
-	0xc4, 0x42, 0xe4, 0xc6, 0x5d, 0xe5, 0xc3, 0x41, 0x32, 0xa1, 0x6d, 0x52, 0xde, 0x95, 0x69, 0x30,
10034
-	0x3a, 0x9e, 0x49, 0x93, 0x9b, 0x6c, 0x0a, 0xdd, 0x24, 0x62, 0xd7, 0x15, 0x18, 0x90, 0x35, 0x5d,
10035
-	0x4e, 0xc7, 0xae, 0xb2, 0x3c, 0xd1, 0x73, 0x27, 0xc5, 0x2d, 0xda, 0x22, 0x45, 0xe6, 0x1d, 0x72,
10036
-	0xff, 0x23, 0x8e, 0xc5, 0x03, 0x27, 0xaf, 0xf1, 0x8a, 0x77, 0xf8, 0x8a, 0x13, 0x34, 0x6b, 0x68,
10037
-	0x0e, 0x45, 0xfe, 0x0d, 0x62, 0x34, 0x3d, 0x24, 0x35, 0x43, 0x7b, 0xdc, 0x3d, 0x15, 0x73, 0xa6,
10038
-	0x6f, 0xb8, 0x2b, 0x2d, 0x67, 0x18, 0xb2, 0xe4, 0xa8, 0x19, 0x7b, 0x05, 0xa6, 0x20, 0xfb, 0xe7,
10039
-	0x53, 0xe8, 0x96, 0xb0, 0xac, 0x78, 0x94, 0x94, 0xa2, 0xa3, 0x81, 0x2e, 0x2a, 0xee, 0xd1, 0x39,
10040
-	0xe6, 0x0e, 0x87, 0xf1, 0xbc, 0x63, 0xce, 0x20, 0x15, 0x20, 0x90, 0xc4, 0xc6, 0xce, 0x14, 0xce,
10041
-	0x6e, 0x67, 0xb8, 0xff, 0xde, 0xf3, 0xdb, 0x07, 0x2c, 0xe8, 0x88, 0xa2, 0x07, 0xcf, 0xae, 0x38,
10042
-	0x52, 0x6b, 0x31, 0x14, 0x0c, 0x0a, 0xfa, 0x2a, 0x29, 0xf2, 0x59, 0xa0, 0xe9, 0x4d, 0x78, 0x9e,
10043
-	0x5e, 0xe0, 0x41, 0xc0, 0x98, 0xce, 0xb4, 0xd8, 0x2a, 0x0e, 0x03, 0xc1, 0x51, 0x94, 0x24, 0xd2,
10044
-	0x59, 0x85, 0xdc, 0x0e, 0x73, 0x64, 0xa2, 0xf2, 0x64, 0x20, 0xe3, 0xd8, 0xf7, 0x48, 0x20, 0x4f,
10045
-	0xaf, 0x15, 0x63, 0x11, 0x0c, 0x45, 0xfc, 0x67, 0x6f, 0x46, 0x6b, 0x6e, 0x80, 0xc5, 0xac, 0x91,
10046
-	0x8a, 0x69, 0x0c, 0x18, 0x54, 0x74, 0x9f, 0x07, 0x74, 0xc9, 0x15, 0x63, 0xc1, 0x74, 0xee, 0x58,
10047
-	0xa0, 0x92, 0x00, 0x83, 0x17, 0xa4, 0x38, 0xd3, 0xd7, 0xc9, 0x74, 0x28, 0xff, 0x0b, 0xf3, 0xd9,
10048
-	0xa9, 0x62, 0x63, 0x2a, 0x38, 0xee, 0x15, 0x28, 0x54, 0x08, 0x5a, 0x80, 0xfd, 0x1f, 0x9d, 0x14,
10049
-	0xca, 0x00, 0x90, 0x4e, 0x6e, 0xad, 0xb3, 0x4d, 0x6e, 0xb3, 0x67, 0xb2, 0xf0, 0x59, 0x9d, 0xc9,
10050
-	0x77, 0xe3, 0x33, 0xa9, 0xf2, 0xe9, 0x65, 0x32, 0x35, 0xd8, 0x77, 0x42, 0x7d, 0x28, 0xef, 0xd4,
10051
-	0x69, 0xe7, 0x96, 0x00, 0xf2, 0x53, 0x49, 0x54, 0xac, 0x14, 0x5f, 0xa0, 0x28, 0x65, 0x92, 0xe9,
10052
-	0xf0, 0xbd, 0xec, 0xf5, 0xd8, 0x1e, 0xa6, 0x8d, 0x49, 0x92, 0xa9, 0x11, 0x90, 0xd0, 0xd0, 0x27,
10053
-	0x48, 0x39, 0x60, 0x4e, 0xc8, 0x5d, 0x9e, 0x3a, 0x59, 0x97, 0xb5, 0x65, 0x82, 0x84, 0x7e, 0x22,
10054
-	0x2c, 0x42, 0x95, 0x80, 0xf2, 0x1b, 0x90, 0x9a, 0x7e, 0x81, 0x4c, 0xf7, 0x4f, 0x6e, 0xfb, 0x68,
10055
-	0x3c, 0x2f, 0x4e, 0x67, 0x79, 0xde, 0x16, 0x44, 0x71, 0x32, 0x97, 0x27, 0x81, 0xa4, 0xa2, 0x6f,
10056
-	0xb2, 0x9d, 0x62, 0x03, 0x19, 0xb6, 0x7c, 0xdf, 0x16, 0xf8, 0xe6, 0x0c, 0x7a, 0x4c, 0x24, 0xdc,
10057
-	0x89, 0xb4, 0xf2, 0xe4, 0xd2, 0x2e, 0x72, 0x69, 0x0b, 0xad, 0x51, 0x5e, 0x30, 0x4e, 0x00, 0x7d,
10058
-	0x96, 0x54, 0xf6, 0x86, 0x81, 0x23, 0x80, 0x98, 0x8d, 0xde, 0xa7, 0x13, 0xf0, 0x35, 0x84, 0x73,
10059
-	0x3d, 0x9e, 0x13, 0x09, 0x6c, 0x5d, 0x03, 0x20, 0x1e, 0xc2, 0x53, 0xb9, 0x25, 0x5f, 0xe6, 0x86,
10060
-	0xca, 0xa1, 0xa9, 0x98, 0xaa, 0x0f, 0x25, 0xb6, 0x8e, 0x6c, 0x64, 0xb8, 0x74, 0xfd, 0x58, 0x4a,
10061
-	0x38, 0x81, 0x0b, 0xfd, 0x0a, 0x29, 0xb7, 0x65, 0xa9, 0x26, 0x93, 0xda, 0x89, 0x43, 0x32, 0x51,
10062
-	0x8d, 0x40, 0xc1, 0x00, 0x90, 0x91, 0xfd, 0xef, 0x12, 0x39, 0x87, 0xd6, 0x2a, 0xfa, 0x9d, 0xdd,
10063
-	0x23, 0x5e, 0xff, 0x99, 0x31, 0xe4, 0xbe, 0x4c, 0x0c, 0x39, 0x9f, 0x22, 0x36, 0xa2, 0xc8, 0x77,
10064
-	0xc8, 0xac, 0x72, 0xdf, 0x1a, 0x87, 0xd1, 0x64, 0x75, 0xb2, 0x13, 0xa7, 0xd6, 0x9d, 0x12, 0xa2,
10065
-	0xac, 0x66, 0x2d, 0xc5, 0x1c, 0x32, 0xc2, 0x84, 0x78, 0xf4, 0x72, 0x5a, 0x7c, 0x31, 0x8f, 0x78,
10066
-	0xf4, 0x68, 0xa3, 0xe2, 0xb7, 0x53, 0xcc, 0x21, 0x23, 0x4c, 0x88, 0x6f, 0x0f, 0xc3, 0xc8, 0xef,
10067
-	0xc7, 0xe2, 0x4b, 0x79, 0xc4, 0xb7, 0x24, 0x8f, 0x31, 0xe2, 0x5b, 0x29, 0xe6, 0x90, 0x11, 0x46,
10068
-	0xdf, 0xb1, 0xc8, 0xc5, 0xd7, 0x99, 0x77, 0xe0, 0x7a, 0xe1, 0x96, 0x3b, 0x60, 0x3d, 0x5e, 0x32,
10069
-	0xc5, 0x13, 0x51, 0xc7, 0x74, 0x63, 0xb2, 0x89, 0x6c, 0xa4, 0x99, 0xa5, 0x67, 0x74, 0x27, 0x9f,
10070
-	0xd1, 0xc5, 0x8d, 0xf1, 0xe2, 0xe0, 0xb8, 0x79, 0xd8, 0x7f, 0x2d, 0x62, 0xb7, 0xc1, 0xf4, 0xa7,
10071
-	0xa6, 0x07, 0xb2, 0x3e, 0xc5, 0x03, 0x71, 0x1d, 0xcb, 0xb6, 0xbc, 0xdb, 0x7e, 0x95, 0xed, 0xbe,
10072
-	0xe8, 0xfb, 0x07, 0xf9, 0x2c, 0xec, 0x85, 0x14, 0x0f, 0xe5, 0xd5, 0xa5, 0x8e, 0xd3, 0x08, 0xc8,
10073
-	0x08, 0xa3, 0x47, 0xe4, 0x9c, 0x92, 0xa3, 0xa5, 0x2b, 0x03, 0x7b, 0x7e, 0xe2, 0xdc, 0xe4, 0xc5,
10074
-	0x98, 0x85, 0x12, 0x7e, 0x5e, 0xb4, 0xa6, 0x53, 0x70, 0x48, 0x4b, 0xa2, 0x6f, 0x59, 0x64, 0x5e,
10075
-	0xe6, 0x16, 0xad, 0x7d, 0xc7, 0xeb, 0xaa, 0xdd, 0x40, 0x03, 0x7b, 0x2e, 0x47, 0xfa, 0xa2, 0xb8,
10076
-	0x28, 0xe1, 0xb2, 0x16, 0x58, 0xcf, 0xf0, 0x86, 0x11, 0x69, 0xf6, 0x4f, 0x8b, 0x84, 0x8e, 0xb6,
10077
-	0xc3, 0xe8, 0x63, 0x29, 0x67, 0x71, 0x6f, 0xc6, 0x59, 0xcc, 0x9b, 0x23, 0x0c, 0x5f, 0xd1, 0x25,
10078
-	0x65, 0x35, 0xeb, 0x7c, 0xf5, 0x12, 0xaa, 0x05, 0xf9, 0x8e, 0xd3, 0x1f, 0xb2, 0x17, 0xb9, 0x0e,
10079
-	0xee, 0x22, 0xee, 0xd6, 0xe9, 0x24, 0x8d, 0x33, 0x13, 0x2d, 0x80, 0x86, 0x58, 0x67, 0x2b, 0xad,
10080
-	0xe1, 0xf6, 0x3c, 0x9f, 0x7b, 0x7b, 0xb4, 0xcc, 0xb9, 0xb8, 0xd6, 0x56, 0x70, 0x30, 0xa5, 0xd8,
10081
-	0xbf, 0x28, 0x13, 0x23, 0xff, 0xa1, 0xcf, 0x71, 0x2f, 0xc8, 0x82, 0x43, 0xb7, 0xcd, 0x56, 0xdb,
10082
-	0x6d, 0x7f, 0xe8, 0x45, 0xb8, 0x31, 0xf1, 0x9d, 0xc5, 0x76, 0x0a, 0x0b, 0x19, 0x6a, 0xd9, 0xaf,
10083
-	0x97, 0x8e, 0x0d, 0x37, 0x26, 0x57, 0xbf, 0x3e, 0x93, 0x1c, 0x63, 0x75, 0x8b, 0x8c, 0x53, 0xd5,
10084
-	0x72, 0xf1, 0x0c, 0xab, 0x65, 0x97, 0x54, 0xc2, 0xb4, 0x2f, 0x7e, 0x3a, 0xd7, 0xe5, 0x03, 0xfa,
10085
-	0xbc, 0xb8, 0x19, 0x17, 0x3b, 0xba, 0x98, 0xbd, 0xd0, 0x9a, 0x0a, 0xda, 0xe8, 0x6b, 0xf3, 0x68,
10086
-	0x4d, 0x65, 0x04, 0x89, 0xd6, 0xd4, 0x37, 0x20, 0x63, 0x5e, 0xa3, 0x55, 0x03, 0xa6, 0x34, 0x18,
10087
-	0x62, 0x2a, 0xf4, 0x29, 0xb5, 0x01, 0x20, 0xb9, 0xe8, 0x7f, 0xb8, 0x01, 0xeb, 0x33, 0x2f, 0x0a,
10088
-	0x93, 0x2c, 0x52, 0x63, 0x43, 0x48, 0xf8, 0xd2, 0x21, 0x21, 0x83, 0xb8, 0x65, 0x83, 0x15, 0xc8,
10089
-	0x6a, 0x8e, 0xb5, 0xa4, 0xfb, 0x3e, 0x49, 0xa2, 0x9e, 0xc0, 0xc1, 0x10, 0x44, 0xbf, 0x46, 0x2e,
10090
-	0x25, 0xf9, 0xd8, 0x1a, 0x73, 0xf6, 0x64, 0xd8, 0xc0, 0xc6, 0xa6, 0xea, 0xf4, 0xdd, 0xcd, 0x87,
10091
-	0x5f, 0x6a, 0x1d, 0x47, 0x04, 0xc7, 0x8f, 0xb7, 0xff, 0x5c, 0x22, 0x0b, 0x63, 0xa2, 0x2a, 0xbd,
10092
-	0x8e, 0xbd, 0x8d, 0x5c, 0x2d, 0xbc, 0xf8, 0xf2, 0xc6, 0xe8, 0x6f, 0xc8, 0x56, 0x5e, 0xaf, 0x77,
10093
-	0xab, 0x5a, 0x79, 0x9a, 0x13, 0x18, 0x5c, 0x75, 0xaf, 0xa2, 0x78, 0xaa, 0x5e, 0xc5, 0x06, 0xa1,
10094
-	0xec, 0x4d, 0xae, 0x7e, 0x86, 0x19, 0x95, 0xf8, 0xab, 0x0a, 0xed, 0x4a, 0x73, 0x09, 0xa9, 0xe9,
10095
-	0x95, 0x11, 0x0a, 0x18, 0x33, 0x4a, 0x14, 0x2a, 0x1d, 0x9f, 0xdb, 0x8e, 0x98, 0xaf, 0x34, 0x7e,
10096
-	0xa3, 0x50, 0xb9, 0xaa, 0x11, 0x90, 0xd0, 0x70, 0x3b, 0x8e, 0x8b, 0xcf, 0x72, 0x9e, 0x46, 0xa4,
10097
-	0x52, 0x84, 0x34, 0xab, 0x63, 0xab, 0x4e, 0xba, 0x4a, 0xe6, 0xe4, 0xa0, 0xd5, 0xad, 0x75, 0xdd,
10098
-	0x09, 0x52, 0x17, 0xc1, 0x17, 0x71, 0x88, 0x6a, 0x84, 0x24, 0x68, 0xc8, 0xd2, 0xdb, 0xbf, 0x2d,
10099
-	0x92, 0x85, 0x31, 0xa9, 0x68, 0xdc, 0x12, 0xb3, 0x6e, 0x45, 0x4b, 0xec, 0xb3, 0x30, 0x19, 0x9e,
10100
-	0x5f, 0x79, 0x7e, 0xcb, 0x69, 0xef, 0x33, 0xbc, 0x55, 0x88, 0xd5, 0x76, 0x4d, 0x81, 0x41, 0xe3,
10101
-	0xb5, 0x75, 0x95, 0x4e, 0x65, 0x5d, 0x13, 0x5b, 0xc4, 0x73, 0xba, 0x6e, 0x10, 0x6d, 0x9f, 0x2d,
10102
-	0x27, 0xda, 0xc7, 0x86, 0x49, 0x1c, 0xb2, 0xd6, 0x52, 0x58, 0xc8, 0x50, 0xdb, 0xbf, 0xb6, 0xc8,
10103
-	0xc2, 0x98, 0x94, 0x2e, 0x15, 0x67, 0xac, 0x33, 0x8c, 0x33, 0xa2, 0x21, 0x9d, 0x6c, 0xa0, 0xd9,
10104
-	0x90, 0x56, 0x9b, 0x81, 0x58, 0xfb, 0xc3, 0x91, 0x79, 0x5e, 0x39, 0xe4, 0x3e, 0x39, 0x5f, 0xcb,
10105
-	0x6e, 0x4b, 0x75, 0xc7, 0x94, 0xc9, 0x3c, 0x3e, 0x71, 0x06, 0xba, 0xee, 0x75, 0xfc, 0x4c, 0x5b,
10106
-	0xec, 0x56, 0xb8, 0x16, 0xfb, 0x6f, 0x16, 0x99, 0x4d, 0x37, 0xdf, 0xe8, 0xdd, 0xa4, 0x38, 0x0c,
10107
-	0x5c, 0x5c, 0x5d, 0x3c, 0xe2, 0x65, 0x58, 0x07, 0x01, 0x17, 0xe8, 0x80, 0x75, 0x50, 0x75, 0x31,
10108
-	0x9a, 0x9b, 0x36, 0x08, 0x38, 0x1d, 0x90, 0xda, 0x20, 0xf0, 0xdf, 0x3c, 0x52, 0x45, 0x6b, 0xbe,
10109
-	0x47, 0x04, 0x5b, 0x09, 0x83, 0xa4, 0x7b, 0x63, 0x00, 0xc1, 0x14, 0x61, 0xff, 0xca, 0x22, 0x74,
10110
-	0x34, 0x47, 0xff, 0x9f, 0xb3, 0xa6, 0x1f, 0x17, 0xc8, 0x34, 0x6e, 0x24, 0xfd, 0x36, 0xaf, 0x8b,
10111
-	0x52, 0x4a, 0xcf, 0x37, 0xc3, 0x4c, 0xd7, 0x34, 0x3e, 0x7f, 0x69, 0x38, 0x64, 0x64, 0xd1, 0xb7,
10112
-	0x2d, 0x72, 0x9e, 0x83, 0xd2, 0xeb, 0xcb, 0xd7, 0x49, 0x7e, 0x21, 0xcb, 0xa6, 0x79, 0x09, 0x27,
10113
-	0x71, 0x7e, 0x04, 0x05, 0xa3, 0x42, 0xed, 0xbf, 0x17, 0xc8, 0x28, 0xa1, 0x50, 0x69, 0x5b, 0x65,
10114
-	0x34, 0xd6, 0xd8, 0x27, 0x51, 0x88, 0x15, 0x45, 0x89, 0x23, 0xdf, 0x14, 0xe5, 0x9b, 0xbc, 0x92,
10115
-	0x2a, 0xba, 0xbb, 0x81, 0xdf, 0x7b, 0x99, 0xe7, 0xd5, 0xc6, 0x9b, 0x1e, 0xc9, 0x16, 0x90, 0x3d,
10116
-	0x37, 0xea, 0x6a, 0x5b, 0x3f, 0x11, 0xca, 0xf7, 0xd2, 0x63, 0x54, 0x96, 0x71, 0x07, 0x8d, 0x9c,
10117
-	0x21, 0x11, 0x32, 0x41, 0x9b, 0xcf, 0xfe, 0x09, 0x2f, 0x35, 0xb3, 0x45, 0xa2, 0x18, 0x2f, 0x8b,
10118
-	0x8e, 0xf5, 0xb5, 0x6c, 0x91, 0xbe, 0xae, 0xc0, 0xa0, 0xf1, 0x74, 0x87, 0x4c, 0x8b, 0xd8, 0x06,
10119
-	0x78, 0xa8, 0x27, 0x8e, 0x91, 0xf2, 0x4e, 0xf0, 0xaa, 0xe2, 0x00, 0x9a, 0x95, 0xfd, 0x27, 0x7e,
10120
-	0x2a, 0x47, 0x6b, 0x23, 0xee, 0x06, 0x2f, 0x88, 0xab, 0x9c, 0xb8, 0x15, 0xbb, 0x9e, 0x9a, 0xe4,
10121
-	0x5d, 0x38, 0xc9, 0x0b, 0x9b, 0x63, 0x68, 0x60, 0xec, 0xc8, 0x38, 0xbe, 0x17, 0x6e, 0x41, 0x7c,
10122
-	0xb7, 0xb7, 0x09, 0x49, 0x2e, 0x49, 0xe9, 0xbd, 0xa4, 0xe4, 0x89, 0x37, 0x69, 0x6a, 0x72, 0x71,
10123
-	0x0a, 0x29, 0x9f, 0xa2, 0x49, 0x0c, 0xbd, 0x9f, 0x4c, 0x1d, 0x3a, 0xbd, 0xa1, 0x7e, 0xeb, 0x17,
10124
-	0x3f, 0x50, 0x78, 0x45, 0x00, 0x41, 0xe1, 0xec, 0xdf, 0x14, 0x48, 0xcd, 0xb8, 0x84, 0x38, 0x8b,
10125
-	0x44, 0x76, 0x6a, 0xc0, 0x03, 0xab, 0x7e, 0x5b, 0xf1, 0x6c, 0xee, 0xfb, 0x11, 0x11, 0x9e, 0x93,
10126
-	0x45, 0x88, 0xaf, 0x10, 0x14, 0xeb, 0x4c, 0xe6, 0x53, 0x3c, 0x8b, 0xcc, 0xc7, 0xfe, 0xbe, 0x45,
10127
-	0xe6, 0x32, 0xb3, 0x11, 0x37, 0x33, 0x61, 0xfc, 0x85, 0x3b, 0x11, 0x97, 0x27, 0x09, 0x1d, 0x18,
10128
-	0x54, 0x32, 0x41, 0x61, 0x61, 0xe4, 0x7a, 0xb2, 0xcf, 0x2b, 0x6e, 0x74, 0x0a, 0x99, 0x04, 0x25,
10129
-	0x85, 0x85, 0x0c, 0xb5, 0xfd, 0x33, 0x8b, 0xdc, 0x75, 0x52, 0x3b, 0x4d, 0xa4, 0xab, 0xd8, 0x33,
10130
-	0x8b, 0x53, 0x20, 0x2b, 0x9d, 0xae, 0x6e, 0xa4, 0xd1, 0x90, 0xa5, 0x17, 0x6f, 0x76, 0x0c, 0x10,
10131
-	0x4e, 0x30, 0x0e, 0x76, 0xc6, 0x70, 0x30, 0xe9, 0xec, 0x1f, 0x59, 0xc4, 0x8c, 0x84, 0xe2, 0x45,
10132
-	0xc6, 0x7e, 0x14, 0x0d, 0x24, 0x08, 0x6f, 0x12, 0xe4, 0x8b, 0x8c, 0x17, 0x77, 0x76, 0xb6, 0x24,
10133
-	0x10, 0x12, 0xbc, 0xb8, 0xd1, 0x13, 0x1f, 0xa1, 0xa2, 0x2e, 0x25, 0x37, 0x7a, 0x82, 0x7a, 0x5b,
10134
-	0x91, 0x1b, 0x14, 0xe2, 0xfa, 0xdf, 0xf3, 0x15, 0xb1, 0x7a, 0x24, 0x5a, 0x53, 0x59, 0xa8, 0xa2,
10135
-	0xd4, 0x38, 0xfb, 0xf7, 0x3c, 0x9e, 0x8c, 0x5c, 0x31, 0xd1, 0x1b, 0x71, 0x5c, 0xb4, 0xf2, 0x1b,
10136
-	0xcb, 0xf8, 0x58, 0x7a, 0xea, 0x0d, 0x7e, 0xd7, 0x22, 0x24, 0xa9, 0x4b, 0x68, 0x8f, 0xcc, 0x28,
10137
-	0xc6, 0xa9, 0x60, 0x9c, 0x67, 0xc2, 0x17, 0x70, 0x02, 0x33, 0xdb, 0x06, 0x3f, 0x48, 0x71, 0x17,
10138
-	0xf9, 0x76, 0x5f, 0xb4, 0x6e, 0xa4, 0xd9, 0x14, 0xd2, 0xef, 0x91, 0x5e, 0xd2, 0x08, 0x48, 0x68,
10139
-	0xec, 0x1f, 0x4c, 0x91, 0x85, 0x31, 0x5d, 0xee, 0xff, 0xe3, 0x82, 0x98, 0x47, 0x37, 0xf5, 0x68,
10140
-	0x24, 0xcc, 0x46, 0x47, 0xf5, 0xa6, 0x44, 0x54, 0x96, 0xea, 0x1f, 0xf1, 0xbe, 0xc0, 0xf5, 0xda,
10141
-	0xaa, 0x9b, 0xe2, 0xe8, 0xfa, 0x46, 0x75, 0xe8, 0x12, 0x30, 0x98, 0x34, 0xe9, 0x82, 0xa8, 0x7c,
10142
-	0x53, 0x25, 0xf2, 0x0c, 0x3e, 0x5c, 0x57, 0x4f, 0x3c, 0xa6, 0xf3, 0x6c, 0x88, 0xbc, 0x04, 0x06,
10143
-	0x83, 0x0d, 0xa4, 0x98, 0xd2, 0xef, 0xf1, 0x30, 0x8f, 0x80, 0xd5, 0x20, 0x72, 0x3b, 0x4e, 0x3b,
10144
-	0xbe, 0x0e, 0x3e, 0xa5, 0xc3, 0x5f, 0xc4, 0xc5, 0xcd, 0x43, 0x86, 0x3d, 0x8c, 0x08, 0xb4, 0x6f,
10145
-	0xf0, 0xa3, 0x9e, 0x4d, 0x65, 0x6e, 0x2e, 0x4e, 0x32, 0xf9, 0x20, 0x3b, 0x13, 0x27, 0xd5, 0x3b,
10146
-	0x6c, 0x85, 0xb3, 0xdf, 0xe1, 0xb5, 0x48, 0x26, 0x13, 0xcc, 0x55, 0x6a, 0xdd, 0x30, 0x4b, 0xad,
10147
-	0x53, 0x27, 0xb4, 0xa9, 0xa2, 0xcb, 0xee, 0x90, 0xd9, 0x74, 0x87, 0xd9, 0xc8, 0xff, 0xad, 0x93,
10148
-	0xf2, 0x7f, 0xf1, 0x5a, 0xd0, 0x11, 0xcf, 0x06, 0xb9, 0x11, 0xe3, 0x05, 0x71, 0xdc, 0xa0, 0x5c,
10149
-	0x45, 0x38, 0xc4, 0x14, 0xcd, 0x07, 0xde, 0xfb, 0xf0, 0xf2, 0x6d, 0xef, 0xf3, 0xdf, 0x3f, 0xf9,
10150
-	0xef, 0xad, 0x8f, 0x2e, 0x5b, 0xef, 0xf1, 0xdf, 0xfb, 0xfc, 0xf7, 0x2f, 0xfe, 0xfb, 0xe1, 0xc7,
10151
-	0x97, 0x6f, 0xbb, 0x51, 0x38, 0x5c, 0xfe, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x37, 0xef, 0xc2,
10152
-	0xa1, 0x46, 0x32, 0x00, 0x00,
9955
+	// 3248 bytes of a gzipped FileDescriptorProto
9956
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe4, 0x5b, 0x4b, 0x6c, 0x1b, 0xc7,
9957
+	0xf9, 0xf7, 0x92, 0x14, 0x45, 0x0d, 0x65, 0x49, 0x1e, 0x39, 0xb1, 0xac, 0x24, 0xb6, 0xb3, 0xc9,
9958
+	0xff, 0x8f, 0x14, 0x49, 0x28, 0xd8, 0x79, 0xd4, 0x79, 0x36, 0x22, 0x65, 0x27, 0x52, 0x65, 0x5b,
9959
+	0xfd, 0xa4, 0x3c, 0xea, 0xa2, 0x2d, 0x56, 0xe4, 0x90, 0xda, 0x88, 0xdc, 0x65, 0x77, 0x97, 0x8a,
9960
+	0x05, 0xb4, 0x40, 0xd0, 0xa2, 0x40, 0x7a, 0xeb, 0xeb, 0x90, 0x4b, 0xd1, 0x06, 0xe8, 0x03, 0x45,
9961
+	0x0e, 0x45, 0x1f, 0x87, 0x02, 0xbd, 0xb4, 0x40, 0x0f, 0x39, 0x15, 0x39, 0xf6, 0x50, 0x04, 0x4d,
9962
+	0x72, 0xe8, 0xb9, 0xd7, 0x9c, 0x3a, 0x8f, 0x6f, 0x76, 0x67, 0x97, 0x94, 0x62, 0xae, 0xac, 0xa0,
9963
+	0x40, 0x0f, 0x34, 0xb4, 0xdf, 0x7c, 0xf3, 0xfb, 0x66, 0xbf, 0xf9, 0xe6, 0x7b, 0xed, 0x98, 0x3c,
9964
+	0xdb, 0x71, 0xa3, 0x9d, 0xc1, 0x76, 0xad, 0xe9, 0xf7, 0x96, 0xfc, 0x3e, 0xf3, 0xc2, 0x1d, 0xb7,
9965
+	0x1d, 0x2d, 0xf9, 0x81, 0xdb, 0x71, 0xbd, 0xa5, 0xfe, 0x6e, 0x67, 0x69, 0x7b, 0xe0, 0x76, 0x5b,
9966
+	0x4b, 0x4e, 0xdf, 0x5d, 0xda, 0xbb, 0xb8, 0xd4, 0x61, 0x1e, 0x0b, 0x9c, 0x88, 0xb5, 0x6a, 0xfd,
9967
+	0xc0, 0x8f, 0x7c, 0xfa, 0x48, 0x32, 0xbb, 0x16, 0xcf, 0xae, 0xa9, 0xd9, 0x35, 0x3e, 0xbb, 0x26,
9968
+	0x67, 0xd7, 0xf8, 0xec, 0xda, 0xde, 0xc5, 0xc5, 0x47, 0x0d, 0x59, 0x1d, 0xbf, 0xe3, 0x2f, 0x49,
9969
+	0x90, 0xed, 0x41, 0x5b, 0x3e, 0xc9, 0x07, 0xf9, 0x97, 0x02, 0x5f, 0x7c, 0x62, 0xf7, 0x72, 0x58,
9970
+	0x73, 0xfd, 0xa5, 0xdd, 0xc1, 0x36, 0x0b, 0x3c, 0x16, 0xb1, 0x50, 0x2e, 0x48, 0x2c, 0x65, 0xe0,
9971
+	0xed, 0xb1, 0x20, 0x74, 0x7d, 0x8f, 0xb5, 0xb2, 0x6b, 0x5a, 0x7c, 0xe4, 0xe0, 0x69, 0xc3, 0x6f,
9972
+	0xb0, 0xf8, 0xe8, 0x68, 0xee, 0x60, 0xe0, 0x45, 0x6e, 0x8f, 0x0d, 0xb1, 0x5f, 0x1c, 0xcd, 0x3e,
9973
+	0x88, 0xdc, 0xee, 0x92, 0xeb, 0x45, 0x61, 0x14, 0x64, 0xa7, 0xd8, 0x7f, 0x2c, 0x91, 0xb3, 0x75,
9974
+	0xd7, 0x73, 0x82, 0xfd, 0xba, 0x50, 0x06, 0xb0, 0x6f, 0x0c, 0x58, 0x18, 0xdd, 0xe8, 0x47, 0x7c,
9975
+	0xf9, 0x21, 0x7d, 0x8d, 0x54, 0x7a, 0x2c, 0x72, 0x5a, 0x4e, 0xe4, 0x2c, 0x58, 0x17, 0xac, 0x87,
9976
+	0xaa, 0x97, 0x1e, 0xaa, 0x29, 0x19, 0xb5, 0x44, 0x86, 0x54, 0xa5, 0x52, 0x62, 0xed, 0xc6, 0xf6,
9977
+	0xeb, 0xac, 0x19, 0x5d, 0xe3, 0x73, 0xea, 0xf4, 0xbd, 0x0f, 0xce, 0x9f, 0xf8, 0xe8, 0x83, 0xf3,
9978
+	0x24, 0xa1, 0x41, 0x8c, 0x46, 0xff, 0x9f, 0x94, 0x9d, 0xf0, 0xaa, 0xdb, 0x65, 0x0b, 0x05, 0x8e,
9979
+	0x3b, 0x55, 0x9f, 0x41, 0xee, 0xf2, 0xb2, 0xa4, 0x02, 0x8e, 0xd2, 0x27, 0xc9, 0x4c, 0xc0, 0xf6,
9980
+	0x5c, 0xa1, 0xcd, 0x86, 0xdf, 0xeb, 0xb9, 0xd1, 0x42, 0x31, 0xcd, 0xaf, 0xa8, 0x90, 0xe1, 0xa2,
9981
+	0x4f, 0x91, 0x59, 0x4d, 0xb9, 0xc6, 0xc2, 0xd0, 0xe9, 0xb0, 0x85, 0x92, 0x9c, 0x38, 0x8b, 0x13,
9982
+	0x27, 0x91, 0x0c, 0x59, 0x3e, 0x5a, 0x27, 0x54, 0x93, 0x96, 0x07, 0xd1, 0x8e, 0x1f, 0x5c, 0x77,
9983
+	0x7a, 0x6c, 0x61, 0x42, 0xce, 0x8e, 0x5f, 0x2a, 0x19, 0x81, 0x11, 0xdc, 0xf4, 0x0a, 0x99, 0x4f,
9984
+	0x53, 0xaf, 0xf4, 0x1c, 0xb7, 0xbb, 0x50, 0x96, 0x20, 0xf3, 0x08, 0x52, 0x35, 0x86, 0x60, 0x14,
9985
+	0x3f, 0xfd, 0x22, 0xb9, 0x2b, 0xfd, 0x5e, 0x11, 0x53, 0xab, 0x99, 0x94, 0x40, 0x77, 0x21, 0xd0,
9986
+	0xc9, 0xd4, 0x20, 0x8c, 0x9e, 0x43, 0xaf, 0x93, 0xbb, 0x87, 0x06, 0xd4, 0xb2, 0x2a, 0x12, 0xed,
9987
+	0x6e, 0x44, 0x9b, 0x49, 0x8f, 0xc2, 0x01, 0xb3, 0xec, 0x67, 0xc8, 0x29, 0xc3, 0x72, 0x36, 0xfd,
9988
+	0x41, 0xd0, 0x64, 0xc6, 0xbe, 0x5a, 0x87, 0xed, 0xab, 0xfd, 0xd3, 0x02, 0x99, 0x90, 0xf3, 0x8e,
9989
+	0xd1, 0xc6, 0xbe, 0x4c, 0x4a, 0x61, 0x9f, 0x35, 0xa5, 0x85, 0x55, 0x2f, 0x7d, 0xbe, 0x36, 0x8e,
9990
+	0x3b, 0xa8, 0xa9, 0x97, 0xe2, 0xd3, 0xeb, 0xd3, 0x28, 0xa4, 0x24, 0x9e, 0x40, 0x42, 0x52, 0x87,
9991
+	0x94, 0xc3, 0xc8, 0x89, 0x06, 0xa1, 0x34, 0xc7, 0xea, 0xa5, 0xa7, 0xf2, 0x80, 0x4b, 0x80, 0x44,
9992
+	0x43, 0xea, 0x19, 0x10, 0xd8, 0xfe, 0x6d, 0x81, 0x54, 0x25, 0x5f, 0xc3, 0xf7, 0xda, 0x6e, 0xe7,
9993
+	0x18, 0xf5, 0xf4, 0xf5, 0x94, 0x9e, 0x9e, 0xcb, 0xf1, 0x2a, 0x6a, 0x89, 0x07, 0x6a, 0xab, 0x93,
9994
+	0xd1, 0xd6, 0x17, 0xf2, 0x8b, 0x38, 0x5c, 0x67, 0xef, 0x5b, 0x64, 0xd6, 0xe0, 0x5e, 0x77, 0xc3,
9995
+	0x88, 0x7e, 0x75, 0x48, 0x6f, 0x4b, 0x87, 0xe8, 0xcd, 0xf0, 0xdd, 0x35, 0x31, 0x5d, 0xaa, 0x6f,
9996
+	0x0e, 0xc5, 0x55, 0x34, 0xc5, 0x50, 0xde, 0xd7, 0xc8, 0x84, 0x1b, 0xb1, 0x5e, 0xc8, 0xb5, 0x57,
9997
+	0xcc, 0x69, 0x08, 0x6a, 0xb1, 0xf5, 0x93, 0x28, 0x65, 0x62, 0x55, 0xe0, 0x81, 0x82, 0xb5, 0x7f,
9998
+	0x57, 0x48, 0xbd, 0x92, 0xd0, 0x2a, 0xf5, 0x48, 0x25, 0xe2, 0x80, 0x1d, 0xbe, 0x52, 0xfe, 0x4a,
9999
+	0x42, 0xec, 0x0b, 0x39, 0xc4, 0x6e, 0x29, 0x88, 0x0d, 0xbf, 0xeb, 0x36, 0xf7, 0x93, 0x77, 0x44,
10000
+	0x72, 0x08, 0xb1, 0x0c, 0xba, 0x4c, 0xa6, 0x78, 0xc8, 0x51, 0x8c, 0xe8, 0xaf, 0x1f, 0x40, 0xf6,
10001
+	0x29, 0xd0, 0x03, 0x9f, 0x70, 0xcf, 0xa1, 0x62, 0x88, 0xa6, 0x40, 0x32, 0x8b, 0x76, 0x09, 0xe1,
10002
+	0x4b, 0xeb, 0xf9, 0x9e, 0x78, 0x01, 0x34, 0x83, 0xcb, 0xe3, 0x2d, 0xba, 0x11, 0xcf, 0x4f, 0xec,
10003
+	0x39, 0xa1, 0x81, 0x81, 0x6f, 0xaf, 0x71, 0xd7, 0x94, 0x35, 0x1a, 0xfa, 0x04, 0xa9, 0x76, 0x9d,
10004
+	0x30, 0x7a, 0x45, 0xed, 0xaf, 0xb4, 0x85, 0x62, 0xe2, 0x8b, 0xd7, 0x93, 0x21, 0x30, 0xf9, 0xec,
10005
+	0xbf, 0x5a, 0x64, 0x4a, 0x82, 0x7d, 0x16, 0xd6, 0xf4, 0x5a, 0xda, 0x9a, 0x1e, 0xcb, 0xb1, 0xad,
10006
+	0x07, 0xd8, 0x11, 0x21, 0x15, 0xf5, 0x16, 0x7e, 0xc7, 0x7e, 0xab, 0x84, 0x36, 0xc5, 0x1f, 0x74,
10007
+	0xa8, 0x5f, 0x22, 0x53, 0x4d, 0xdf, 0x8b, 0x1c, 0x97, 0xe7, 0x07, 0xe8, 0xbb, 0x4f, 0xe9, 0x3d,
10008
+	0x6e, 0xe8, 0x01, 0x48, 0x78, 0x84, 0xa7, 0x6f, 0xfb, 0xdd, 0xae, 0xff, 0x86, 0xb4, 0x88, 0x4a,
10009
+	0x72, 0x26, 0xaf, 0x4a, 0x2a, 0xe0, 0x28, 0x7d, 0x84, 0x54, 0xfa, 0x22, 0x82, 0xf8, 0x78, 0xfc,
10010
+	0x2b, 0x89, 0x02, 0x36, 0x90, 0x0e, 0x31, 0x07, 0x7d, 0x9c, 0x4c, 0x87, 0xae, 0xd7, 0x64, 0x9b,
10011
+	0x8c, 0x4b, 0x6a, 0x85, 0x32, 0x68, 0x17, 0xeb, 0x73, 0x9c, 0x7b, 0x7a, 0xd3, 0xa0, 0x43, 0x8a,
10012
+	0x8b, 0xab, 0x6d, 0x4a, 0x3e, 0x6f, 0xb9, 0x18, 0xa9, 0xab, 0x97, 0x1e, 0xbe, 0xcd, 0x6d, 0x11,
10013
+	0x53, 0xea, 0x27, 0xc5, 0x5b, 0x6e, 0x6a, 0x04, 0x48, 0xc0, 0xe8, 0x25, 0x42, 0x44, 0xaa, 0xc5,
10014
+	0xfd, 0x4b, 0xaf, 0x1f, 0xca, 0xf8, 0x5d, 0x49, 0xac, 0x6f, 0x2b, 0x1e, 0x01, 0x83, 0x8b, 0x3e,
10015
+	0x4c, 0xa6, 0xb8, 0x8e, 0xba, 0xeb, 0x5c, 0x4d, 0xa1, 0x8c, 0xd4, 0x45, 0x25, 0x60, 0x4b, 0x13,
10016
+	0x21, 0x19, 0xa7, 0x35, 0x42, 0xba, 0x2e, 0x0f, 0xab, 0xf5, 0x7d, 0xbe, 0x42, 0x19, 0x89, 0x8b,
10017
+	0xf5, 0x19, 0x01, 0xbe, 0x1e, 0x53, 0xc1, 0xe0, 0x10, 0x6a, 0xf7, 0xfc, 0x37, 0x1c, 0x9e, 0x08,
10018
+	0x4d, 0xa5, 0xd5, 0x7e, 0xdd, 0x7f, 0x95, 0x53, 0x01, 0x47, 0xe9, 0xff, 0x91, 0x49, 0x7c, 0xc9,
10019
+	0x05, 0x22, 0x41, 0xab, 0x22, 0xe9, 0xd1, 0x16, 0xae, 0xc7, 0xec, 0x5f, 0xeb, 0x28, 0x73, 0x63,
10020
+	0x10, 0xf5, 0x07, 0x11, 0x4f, 0x5c, 0x0a, 0x91, 0x8f, 0x96, 0xfd, 0xe8, 0xed, 0xc4, 0x17, 0x60,
10021
+	0x6d, 0x16, 0x30, 0xae, 0xae, 0x7a, 0x99, 0x0b, 0x28, 0x6c, 0xf9, 0xc0, 0x01, 0xe8, 0x36, 0x21,
10022
+	0xfd, 0x41, 0xb8, 0xc3, 0xf7, 0x27, 0x60, 0x11, 0x06, 0x96, 0x4b, 0x87, 0xc3, 0xad, 0xfb, 0x4d,
10023
+	0xa7, 0x9b, 0xc5, 0x94, 0x9a, 0xd8, 0x88, 0x91, 0xc0, 0x40, 0xa5, 0x3e, 0xa9, 0xba, 0x3d, 0x9e,
10024
+	0xb0, 0xad, 0x3b, 0xdb, 0xac, 0x2b, 0x6c, 0xab, 0x38, 0xbe, 0x4f, 0x59, 0x8d, 0x01, 0x12, 0x4f,
10025
+	0x90, 0xd0, 0x42, 0x30, 0x25, 0xd8, 0xdf, 0xb6, 0xc8, 0xbc, 0xd4, 0xd5, 0x86, 0x1f, 0x46, 0x2a,
10026
+	0x19, 0x92, 0xee, 0x98, 0xab, 0x5a, 0xf8, 0x1e, 0xc7, 0x6b, 0x49, 0x6f, 0x3c, 0xa5, 0x54, 0xdd,
10027
+	0x50, 0x24, 0xd0, 0x63, 0xf4, 0x5e, 0x52, 0x72, 0x82, 0x8e, 0x3a, 0xda, 0x53, 0xf5, 0x8a, 0x88,
10028
+	0x91, 0xcb, 0xfc, 0x19, 0x24, 0x55, 0xec, 0x6b, 0xd8, 0x0c, 0xdc, 0xfe, 0x50, 0x82, 0xbb, 0x29,
10029
+	0xa9, 0x80, 0xa3, 0xf6, 0xc7, 0x13, 0x64, 0xda, 0x4c, 0xd5, 0x8f, 0x31, 0x2f, 0x68, 0x93, 0x8a,
10030
+	0x4e, 0xfd, 0x70, 0x0b, 0x9f, 0x1d, 0x4f, 0xbb, 0x2a, 0x27, 0x04, 0xc4, 0xa8, 0x4f, 0x8b, 0x33,
10031
+	0xaf, 0x9f, 0x20, 0xc6, 0xe6, 0x1b, 0x39, 0x87, 0xa1, 0x86, 0xb5, 0xea, 0xfb, 0x52, 0xfd, 0x18,
10032
+	0x21, 0xc6, 0xb4, 0xc0, 0xd3, 0x5c, 0xc0, 0xdc, 0x56, 0x06, 0x0a, 0x86, 0xc0, 0x79, 0x5a, 0x5d,
10033
+	0x6a, 0x07, 0x7e, 0x4f, 0x3a, 0x97, 0xb1, 0x85, 0xc8, 0x8d, 0xbb, 0xca, 0xa7, 0x83, 0x04, 0xa1,
10034
+	0x4d, 0x52, 0xde, 0x96, 0x69, 0x30, 0x3a, 0x9e, 0x71, 0x93, 0x9b, 0x6c, 0x0a, 0x5d, 0x27, 0x62,
10035
+	0xd7, 0x15, 0x19, 0x10, 0x9a, 0x5e, 0x4c, 0xc7, 0xae, 0xb2, 0x3c, 0xd1, 0xb3, 0x87, 0xc5, 0x2d,
10036
+	0xda, 0x20, 0x45, 0xe6, 0xed, 0x71, 0xff, 0x23, 0x8e, 0xc5, 0x83, 0x87, 0xbf, 0xe3, 0x15, 0x6f,
10037
+	0xef, 0x15, 0x27, 0xa8, 0x57, 0xd1, 0x1c, 0x8a, 0xfc, 0x19, 0xc4, 0x6c, 0xba, 0x47, 0xaa, 0x86,
10038
+	0xf6, 0xb8, 0x7b, 0x2a, 0xe6, 0x4c, 0xdf, 0x70, 0x57, 0x1a, 0xce, 0x20, 0x64, 0xc9, 0x51, 0x33,
10039
+	0xf6, 0x0a, 0x4c, 0x41, 0xf6, 0x4f, 0x26, 0xd0, 0x2d, 0x61, 0x59, 0xf1, 0x18, 0x29, 0x45, 0xfb,
10040
+	0x7d, 0x5d, 0x54, 0x9c, 0xd7, 0x39, 0xe6, 0x16, 0xa7, 0xf1, 0xbc, 0x63, 0xd6, 0x60, 0x15, 0x24,
10041
+	0x90, 0xcc, 0xc6, 0xce, 0x14, 0x8e, 0x6f, 0x67, 0xb8, 0xff, 0x6e, 0xf9, 0xcd, 0x5d, 0x16, 0xb4,
10042
+	0x45, 0xd1, 0x83, 0x67, 0x57, 0x1c, 0xa9, 0x95, 0x98, 0x0a, 0x06, 0x07, 0x7d, 0x95, 0x14, 0xf9,
10043
+	0x2a, 0xd0, 0xf4, 0xc6, 0x3c, 0x4f, 0x2f, 0xf2, 0x20, 0x60, 0x2c, 0x67, 0x52, 0x6c, 0x15, 0xa7,
10044
+	0x81, 0x40, 0x14, 0x25, 0x89, 0x74, 0x56, 0x21, 0xb7, 0xc3, 0x1c, 0x99, 0xa8, 0x3c, 0x19, 0x08,
10045
+	0x1c, 0xfb, 0x1e, 0x49, 0xe4, 0xe9, 0xb5, 0x02, 0x16, 0xc1, 0x50, 0xc4, 0x7f, 0x76, 0x2b, 0x5a,
10046
+	0x71, 0x03, 0x2c, 0x66, 0x8d, 0x54, 0x4c, 0x8f, 0x80, 0xc1, 0x45, 0x77, 0x78, 0x40, 0x97, 0xa8,
10047
+	0x18, 0x0b, 0x26, 0x73, 0xc7, 0x02, 0x95, 0x04, 0x18, 0x58, 0x90, 0x42, 0xa6, 0xaf, 0x93, 0xc9,
10048
+	0x50, 0xfe, 0x15, 0xe6, 0xb3, 0x53, 0x05, 0x63, 0x2a, 0x38, 0xee, 0x15, 0xa8, 0xa1, 0x10, 0xb4,
10049
+	0x00, 0xfb, 0xdf, 0x3a, 0x29, 0x94, 0x01, 0x20, 0x9d, 0xdc, 0x5a, 0xc7, 0x9b, 0xdc, 0x66, 0xcf,
10050
+	0x64, 0xe1, 0xb3, 0x3a, 0x93, 0xef, 0xc6, 0x67, 0x52, 0xe5, 0xd3, 0x17, 0xc9, 0x44, 0x7f, 0xc7,
10051
+	0x09, 0xf5, 0xa1, 0xbc, 0x47, 0xa7, 0x9d, 0x1b, 0x82, 0xc8, 0x4f, 0x25, 0x51, 0xb1, 0x52, 0x3c,
10052
+	0x81, 0xe2, 0x94, 0x49, 0xa6, 0xc3, 0xf7, 0xb2, 0xdb, 0x65, 0x2d, 0x4c, 0x1b, 0x93, 0x24, 0x53,
10053
+	0x0f, 0x40, 0xc2, 0x43, 0x9f, 0x24, 0xe5, 0x80, 0x39, 0x21, 0x77, 0x79, 0xea, 0x64, 0x9d, 0xd3,
10054
+	0x96, 0x09, 0x92, 0xfa, 0x89, 0xb0, 0x08, 0x55, 0x02, 0xca, 0x67, 0x40, 0x6e, 0xfa, 0x39, 0x32,
10055
+	0xd9, 0x3b, 0xbc, 0xed, 0xa3, 0xc7, 0x79, 0x71, 0x3a, 0xc3, 0xf3, 0xb6, 0x20, 0x8a, 0x93, 0xb9,
10056
+	0x3c, 0x09, 0x24, 0x15, 0x7d, 0x93, 0xcd, 0x14, 0x0c, 0x64, 0x60, 0xf9, 0xbe, 0xcd, 0xf3, 0xcd,
10057
+	0xe9, 0x77, 0x99, 0x48, 0xb8, 0x13, 0x69, 0xe5, 0xf1, 0xa5, 0x9d, 0xe1, 0xd2, 0xe6, 0x1b, 0xc3,
10058
+	0x58, 0x30, 0x4a, 0x00, 0x7d, 0x8e, 0x54, 0x5a, 0x83, 0xc0, 0x11, 0x44, 0xcc, 0x46, 0xef, 0xd7,
10059
+	0x09, 0xf8, 0x0a, 0xd2, 0xb9, 0x1e, 0x4f, 0x8a, 0x04, 0xb6, 0xa6, 0x09, 0x10, 0x4f, 0xe1, 0xa9,
10060
+	0xdc, 0xa2, 0x2f, 0x73, 0x43, 0xe5, 0xd0, 0x54, 0x4c, 0xd5, 0x87, 0x12, 0x5b, 0x47, 0x36, 0x02,
10061
+	0x2e, 0xde, 0x38, 0x90, 0x13, 0x0e, 0x41, 0xa1, 0x5f, 0x22, 0xe5, 0xa6, 0x2c, 0xd5, 0x64, 0x52,
10062
+	0x3b, 0x76, 0x48, 0x26, 0xaa, 0x11, 0x28, 0x00, 0x00, 0x81, 0xec, 0x7f, 0x95, 0xc8, 0x49, 0xb4,
10063
+	0x56, 0xd1, 0xef, 0xec, 0xec, 0xf3, 0xfa, 0xcf, 0x8c, 0x21, 0xf7, 0x67, 0x62, 0xc8, 0xa9, 0x14,
10064
+	0xb3, 0x11, 0x45, 0xbe, 0x45, 0x66, 0x94, 0xfb, 0xd6, 0x63, 0x18, 0x4d, 0x96, 0xc7, 0x3b, 0x71,
10065
+	0xea, 0xbd, 0x53, 0x42, 0x94, 0xd5, 0xac, 0xa4, 0xc0, 0x21, 0x23, 0x4c, 0x88, 0x47, 0x2f, 0xa7,
10066
+	0xc5, 0x17, 0xf3, 0x88, 0x47, 0x8f, 0x36, 0x2c, 0x7e, 0x33, 0x05, 0x0e, 0x19, 0x61, 0x42, 0x7c,
10067
+	0x73, 0x10, 0x46, 0x7e, 0x2f, 0x16, 0x5f, 0xca, 0x23, 0xbe, 0x21, 0x31, 0x46, 0x88, 0x6f, 0xa4,
10068
+	0xc0, 0x21, 0x23, 0x8c, 0xbe, 0x63, 0x91, 0x33, 0xaf, 0x33, 0x6f, 0xd7, 0xf5, 0xc2, 0x0d, 0xb7,
10069
+	0xcf, 0xba, 0xbc, 0x64, 0x8a, 0x17, 0xa2, 0x8e, 0xe9, 0xda, 0x78, 0x0b, 0x59, 0x4b, 0x83, 0xa5,
10070
+	0x57, 0x74, 0x0f, 0x5f, 0xd1, 0x99, 0xb5, 0xd1, 0xe2, 0xe0, 0xa0, 0x75, 0xd8, 0x7f, 0x2a, 0x62,
10071
+	0xb7, 0xc1, 0xf4, 0xa7, 0xa6, 0x07, 0xb2, 0x3e, 0xc5, 0x03, 0x71, 0x1d, 0xcb, 0xb6, 0xbc, 0xdb,
10072
+	0x7c, 0x95, 0x6d, 0xbf, 0xe4, 0xfb, 0xbb, 0xf9, 0x2c, 0xec, 0xc5, 0x14, 0x86, 0xf2, 0xea, 0x52,
10073
+	0xc7, 0xe9, 0x01, 0xc8, 0x08, 0xa3, 0xfb, 0xe4, 0xa4, 0x92, 0xa3, 0xa5, 0x2b, 0x03, 0x7b, 0x61,
10074
+	0xec, 0xdc, 0xe4, 0xa5, 0x18, 0x42, 0x09, 0x3f, 0x25, 0x5a, 0xd3, 0x29, 0x3a, 0xa4, 0x25, 0xd1,
10075
+	0x37, 0x2d, 0x32, 0x27, 0x73, 0x8b, 0xc6, 0x8e, 0xe3, 0x75, 0xd4, 0x6e, 0xa0, 0x81, 0x3d, 0x9f,
10076
+	0x23, 0x7d, 0x51, 0x28, 0x4a, 0xb8, 0xac, 0x05, 0x56, 0x33, 0xd8, 0x30, 0x24, 0xcd, 0xfe, 0x71,
10077
+	0x91, 0xd0, 0xe1, 0x76, 0x18, 0x7d, 0x3c, 0xe5, 0x2c, 0x2e, 0x64, 0x9c, 0xc5, 0x9c, 0x39, 0xc3,
10078
+	0xf0, 0x15, 0x1d, 0x52, 0x56, 0xab, 0xce, 0x57, 0x2f, 0xa1, 0x5a, 0x10, 0x77, 0x94, 0xfe, 0x10,
10079
+	0x5e, 0xe4, 0x3a, 0xb8, 0x8b, 0xb8, 0x5b, 0x47, 0x93, 0x34, 0xca, 0x4c, 0xb4, 0x00, 0x1a, 0x62,
10080
+	0x9d, 0xad, 0xb4, 0x86, 0xdb, 0xf3, 0x42, 0xee, 0xed, 0xd1, 0x32, 0x67, 0xe3, 0x5a, 0x5b, 0xd1,
10081
+	0xc1, 0x94, 0x62, 0xbf, 0x3d, 0x49, 0x8c, 0xfc, 0x87, 0x3e, 0xcf, 0xbd, 0x20, 0x0b, 0xf6, 0xdc,
10082
+	0x26, 0x5b, 0x6e, 0x36, 0xfd, 0x81, 0x17, 0xe1, 0xc6, 0xc4, 0xdf, 0x2c, 0x36, 0x53, 0xa3, 0x90,
10083
+	0xe1, 0x96, 0xfd, 0x7a, 0xe9, 0xd8, 0x70, 0x63, 0x72, 0xf5, 0xeb, 0x33, 0xc9, 0x31, 0x56, 0xb7,
10084
+	0x08, 0x9c, 0xaa, 0x96, 0x8b, 0xc7, 0x58, 0x2d, 0xbb, 0xa4, 0x12, 0xa6, 0x7d, 0xf1, 0x33, 0xb9,
10085
+	0x3e, 0x3e, 0xa0, 0xcf, 0x8b, 0x9b, 0x71, 0xb1, 0xa3, 0x8b, 0xe1, 0x85, 0xd6, 0x54, 0xd0, 0x46,
10086
+	0x5f, 0x9b, 0x47, 0x6b, 0x2a, 0x23, 0x48, 0xb4, 0xa6, 0x9e, 0x01, 0x81, 0x79, 0x8d, 0x36, 0x15,
10087
+	0x30, 0xa5, 0xc1, 0x10, 0x53, 0xa1, 0x4f, 0xa9, 0x0d, 0x00, 0xd9, 0x45, 0xff, 0xc3, 0x0d, 0x58,
10088
+	0x8f, 0x79, 0x51, 0x98, 0x64, 0x91, 0x7a, 0x34, 0x84, 0x04, 0x97, 0x0e, 0x08, 0xe9, 0xc7, 0x2d,
10089
+	0x1b, 0xac, 0x40, 0x96, 0x73, 0xbc, 0x4b, 0xba, 0xef, 0x93, 0x24, 0xea, 0x09, 0x1d, 0x0c, 0x41,
10090
+	0xf4, 0x2b, 0xe4, 0x6c, 0x92, 0x8f, 0xad, 0x30, 0xa7, 0x25, 0xc3, 0x06, 0x36, 0x36, 0x55, 0xa7,
10091
+	0xef, 0x3e, 0x3e, 0xfd, 0x6c, 0xe3, 0x20, 0x26, 0x38, 0x78, 0x3e, 0xbd, 0x45, 0xa6, 0x3d, 0xbf,
10092
+	0xc5, 0x1f, 0xbb, 0x3c, 0x15, 0xf2, 0x03, 0x4c, 0x9c, 0xea, 0xe3, 0xbd, 0x95, 0x6a, 0xfe, 0x3a,
10093
+	0xdd, 0xeb, 0x06, 0x92, 0xaa, 0xb3, 0x4c, 0x0a, 0xa4, 0x24, 0xd9, 0x7f, 0x28, 0x91, 0xf9, 0x11,
10094
+	0xf1, 0x9c, 0xde, 0xc0, 0xae, 0x4a, 0xae, 0xe6, 0x61, 0xfc, 0xd9, 0xc8, 0xe8, 0xac, 0xc8, 0x26,
10095
+	0x62, 0xb7, 0x7b, 0xa7, 0x9a, 0x88, 0x1a, 0x09, 0x0c, 0x54, 0xdd, 0x25, 0x29, 0x1e, 0xa9, 0x4b,
10096
+	0xb2, 0x46, 0x28, 0xbb, 0xc5, 0x37, 0x9e, 0x61, 0x2e, 0x27, 0xfe, 0x55, 0x25, 0x7e, 0xa5, 0xbe,
10097
+	0x88, 0xdc, 0xf4, 0xca, 0x10, 0x07, 0x8c, 0x98, 0x25, 0x4a, 0xa4, 0xb6, 0xcf, 0xad, 0x56, 0xac,
10098
+	0x57, 0x1e, 0x3b, 0xa3, 0x44, 0xba, 0xaa, 0x07, 0x20, 0xe1, 0xe1, 0x27, 0x28, 0x2e, 0x7b, 0xcb,
10099
+	0x79, 0x5a, 0xa0, 0x4a, 0x11, 0xd2, 0xa0, 0x0f, 0xac, 0x77, 0xe9, 0x32, 0x99, 0x95, 0x93, 0x96,
10100
+	0x37, 0x56, 0x75, 0x0f, 0x4a, 0x7d, 0x82, 0x3e, 0x83, 0x53, 0x54, 0x0b, 0x26, 0x19, 0x86, 0x2c,
10101
+	0xbf, 0xfd, 0xab, 0x22, 0x99, 0x1f, 0x91, 0x04, 0xc7, 0xcd, 0x38, 0xeb, 0x4e, 0x34, 0xe3, 0x3e,
10102
+	0x0b, 0x93, 0xe1, 0x99, 0x9d, 0xe7, 0x37, 0x9c, 0xe6, 0x0e, 0xc3, 0xef, 0x19, 0xb1, 0xda, 0xae,
10103
+	0x2b, 0x32, 0xe8, 0x71, 0x6d, 0x5d, 0xa5, 0x23, 0x59, 0xd7, 0xd8, 0x16, 0xf1, 0xbc, 0xae, 0x58,
10104
+	0x44, 0xc3, 0x69, 0xc3, 0x89, 0x76, 0xb0, 0x55, 0x13, 0x07, 0xcb, 0x95, 0xd4, 0x28, 0x64, 0xb8,
10105
+	0xed, 0x5f, 0x58, 0x64, 0x7e, 0x44, 0x32, 0x99, 0x8a, 0x70, 0xd6, 0x31, 0x46, 0x38, 0xd1, 0x0a,
10106
+	0x4f, 0x36, 0xd0, 0x6c, 0x85, 0xab, 0xcd, 0xc0, 0x51, 0xfb, 0xc3, 0xa1, 0x75, 0x5e, 0xd9, 0xe3,
10107
+	0xd1, 0x20, 0x5f, 0xb3, 0x70, 0x43, 0xf5, 0xe5, 0x94, 0xc9, 0x3c, 0x31, 0x76, 0xee, 0xbb, 0xea,
10108
+	0xb5, 0xfd, 0x4c, 0x43, 0xee, 0x4e, 0xb8, 0x16, 0xfb, 0xcf, 0x16, 0x99, 0x49, 0xb7, 0xfd, 0xe8,
10109
+	0x7d, 0xa4, 0x38, 0x08, 0x5c, 0x7c, 0xbb, 0x78, 0xc6, 0xcb, 0xb0, 0x0a, 0x82, 0x2e, 0x86, 0x03,
10110
+	0xd6, 0x46, 0xd5, 0xc5, 0xc3, 0xdc, 0xb4, 0x41, 0xd0, 0x69, 0x9f, 0x54, 0xfb, 0x81, 0x7f, 0x6b,
10111
+	0x5f, 0x95, 0xcb, 0xf9, 0xae, 0x2f, 0x6c, 0x24, 0x00, 0x49, 0xdf, 0xc8, 0x20, 0x82, 0x29, 0xc2,
10112
+	0xfe, 0xb9, 0x45, 0xe8, 0x70, 0x75, 0xf0, 0x5f, 0x67, 0x4d, 0x3f, 0x2c, 0x90, 0x49, 0xdc, 0x48,
10113
+	0xfa, 0x4d, 0x5e, 0x91, 0xa5, 0x94, 0x9e, 0x6f, 0x85, 0x99, 0x7e, 0x6d, 0x7c, 0xfe, 0xd2, 0x74,
10114
+	0xc8, 0xc8, 0xa2, 0x6f, 0x59, 0xe4, 0x14, 0x27, 0xa5, 0xdf, 0x2f, 0x5f, 0x0f, 0xfb, 0xc5, 0x2c,
10115
+	0x4c, 0xfd, 0x2c, 0x2e, 0xe2, 0xd4, 0xd0, 0x10, 0x0c, 0x0b, 0xb5, 0xff, 0x52, 0x20, 0xc3, 0x8c,
10116
+	0x42, 0xa5, 0x4d, 0x95, 0x4b, 0x59, 0x23, 0x2f, 0x63, 0xe1, 0xa8, 0x28, 0x87, 0x1c, 0x79, 0x9b,
10117
+	0x29, 0xdf, 0xe2, 0x95, 0x54, 0xd1, 0x57, 0x0e, 0xfc, 0xee, 0xcb, 0x3c, 0xa3, 0x37, 0x6e, 0x13,
10118
+	0x49, 0x58, 0x40, 0x78, 0x6e, 0xd4, 0x53, 0x4d, 0x7d, 0x39, 0x29, 0xdf, 0x1d, 0x93, 0x61, 0x59,
10119
+	0xc6, 0xd7, 0x6f, 0x44, 0x86, 0x44, 0xc8, 0x18, 0x0d, 0x46, 0xfb, 0x47, 0xbc, 0xc8, 0xcd, 0x96,
10120
+	0xa7, 0x62, 0xbe, 0x2c, 0x77, 0x56, 0x57, 0xb2, 0xed, 0x81, 0x55, 0x45, 0x06, 0x3d, 0x4e, 0xb7,
10121
+	0xc8, 0xa4, 0x88, 0x6d, 0x80, 0x87, 0x7a, 0xec, 0x18, 0x29, 0xbf, 0x46, 0x5e, 0x55, 0x08, 0xa0,
10122
+	0xa1, 0xec, 0xdf, 0xf3, 0x53, 0x39, 0x5c, 0x95, 0x71, 0x37, 0x78, 0x5a, 0x7c, 0x44, 0x8a, 0x9b,
10123
+	0xc0, 0xab, 0xa9, 0x45, 0xde, 0x8b, 0x8b, 0x3c, 0xbd, 0x3e, 0x82, 0x07, 0x46, 0xce, 0x8c, 0xe3,
10124
+	0x7b, 0xe1, 0x0e, 0xc4, 0x77, 0x7b, 0x93, 0x90, 0xe4, 0xf3, 0x2c, 0xbd, 0x40, 0x4a, 0x9e, 0xb8,
10125
+	0x0d, 0xa7, 0x16, 0x17, 0xa7, 0x90, 0xf2, 0x12, 0x9c, 0x1c, 0xa1, 0x0f, 0x90, 0x89, 0x3d, 0xa7,
10126
+	0x3b, 0xd0, 0xb7, 0x0c, 0xe3, 0xab, 0x11, 0xaf, 0x08, 0x22, 0xa8, 0x31, 0xfb, 0x97, 0x05, 0x52,
10127
+	0x35, 0x3e, 0x7f, 0x1c, 0x47, 0x22, 0x3b, 0xd1, 0xe7, 0x81, 0x55, 0xdf, 0xea, 0x78, 0x2e, 0xf7,
10128
+	0x97, 0x19, 0x11, 0x9e, 0x93, 0x97, 0x10, 0x4f, 0x21, 0x28, 0xe8, 0x4c, 0xe6, 0x53, 0x3c, 0x8e,
10129
+	0xcc, 0xc7, 0xfe, 0xae, 0x45, 0x66, 0x33, 0xab, 0x11, 0xdf, 0x84, 0xc2, 0xf8, 0x09, 0x77, 0x22,
10130
+	0x2e, 0x8c, 0x12, 0x3e, 0x30, 0xb8, 0x64, 0x82, 0xc2, 0xc2, 0xc8, 0xf5, 0x64, 0x87, 0x59, 0x7c,
10131
+	0x4b, 0x2a, 0x64, 0x12, 0x94, 0xd4, 0x28, 0x64, 0xb8, 0xed, 0xb7, 0x2d, 0x72, 0xef, 0x61, 0x8d,
10132
+	0x3c, 0x91, 0xae, 0x62, 0xb7, 0x2e, 0x4e, 0x81, 0xac, 0x74, 0xba, 0xba, 0x96, 0x1e, 0x86, 0x2c,
10133
+	0xbf, 0xb8, 0x2d, 0x64, 0x90, 0x70, 0x81, 0x71, 0xb0, 0x33, 0xa6, 0x83, 0xc9, 0x67, 0xff, 0xcd,
10134
+	0x22, 0xa7, 0x47, 0x55, 0x55, 0x34, 0xd0, 0x37, 0x7b, 0xd4, 0x85, 0xad, 0x6b, 0x47, 0x2f, 0xd4,
10135
+	0x6a, 0xf2, 0x7e, 0xcf, 0x15, 0xee, 0xb1, 0xf6, 0x47, 0xdf, 0xf9, 0x59, 0xbc, 0xcc, 0x4f, 0x4b,
10136
+	0xcc, 0x43, 0xe7, 0x48, 0x71, 0x97, 0xed, 0x2b, 0x45, 0x80, 0xf8, 0x93, 0x9e, 0x4e, 0x9d, 0x0e,
10137
+	0x3c, 0x0e, 0x4f, 0x17, 0x2e, 0x5b, 0x4f, 0x57, 0xde, 0xfe, 0xd9, 0xf9, 0x13, 0x6f, 0xfe, 0xe3,
10138
+	0xc2, 0x09, 0xfb, 0x07, 0x16, 0x31, 0x43, 0xbb, 0xb8, 0xdc, 0xb2, 0x13, 0x45, 0x7d, 0x49, 0xc2,
10139
+	0x8f, 0x32, 0xf2, 0x72, 0xcb, 0x4b, 0x5b, 0x5b, 0x1b, 0x92, 0x08, 0xc9, 0xb8, 0xf8, 0x38, 0x2a,
10140
+	0x1e, 0x42, 0xc5, 0x5d, 0x4a, 0x3e, 0x8e, 0x0a, 0xee, 0x4d, 0xc5, 0x6e, 0x70, 0x88, 0x9b, 0x14,
10141
+	0x9e, 0xaf, 0x98, 0xd5, 0x7d, 0xdb, 0xaa, 0x4a, 0xab, 0x15, 0xa7, 0x1e, 0xb3, 0x7f, 0xc3, 0x03,
10142
+	0xe4, 0xd0, 0xd7, 0x3a, 0x7a, 0x33, 0x0e, 0xf4, 0x56, 0x7e, 0xeb, 0x1f, 0x9d, 0x1c, 0x1c, 0xd9,
10143
+	0x62, 0xdf, 0xb5, 0x08, 0x49, 0x0a, 0x2d, 0xda, 0x25, 0xd3, 0x0a, 0x38, 0x95, 0x5d, 0xe4, 0x59,
10144
+	0xf0, 0x69, 0x5c, 0xc0, 0xf4, 0xa6, 0x81, 0x07, 0x29, 0x74, 0x51, 0x40, 0xf4, 0x44, 0x17, 0x4c,
10145
+	0x9e, 0x83, 0x42, 0xfa, 0x6a, 0xd7, 0x35, 0x3d, 0x00, 0x09, 0x8f, 0xfd, 0xbd, 0x09, 0x32, 0x3f,
10146
+	0xe2, 0x83, 0xc1, 0xff, 0x70, 0x85, 0xcf, 0xc3, 0xb5, 0xba, 0x7f, 0x13, 0x66, 0xc3, 0xbd, 0xba,
10147
+	0x9e, 0x23, 0x4a, 0x65, 0xf5, 0x87, 0xb8, 0xaa, 0xe1, 0x7a, 0x4d, 0xd5, 0x98, 0x72, 0x74, 0xc1,
10148
+	0xa6, 0x9a, 0x9d, 0x09, 0x19, 0x4c, 0x9e, 0x74, 0x85, 0x57, 0xbe, 0xad, 0x9a, 0x7f, 0x1a, 0xff,
10149
+	0x0f, 0x80, 0xba, 0x2d, 0x33, 0x99, 0x67, 0x43, 0x64, 0x9f, 0x07, 0x0c, 0x18, 0x48, 0x81, 0xd2,
10150
+	0xef, 0xf0, 0xbc, 0x05, 0x09, 0xcb, 0x41, 0xe4, 0xb6, 0x9d, 0x66, 0xfc, 0x65, 0xfd, 0x88, 0x11,
10151
+	0x6c, 0x01, 0x5f, 0x6e, 0x0e, 0x32, 0xf0, 0x30, 0x24, 0xd0, 0xbe, 0xc9, 0x8f, 0x7a, 0x36, 0x37,
10152
+	0xbb, 0xbd, 0xc0, 0xcf, 0xe4, 0xdd, 0xf6, 0x4c, 0xe0, 0x57, 0x57, 0xda, 0xd5, 0x98, 0xfd, 0x0e,
10153
+	0x2f, 0xae, 0x32, 0xa9, 0x6d, 0xae, 0xda, 0xf1, 0xa6, 0x59, 0x3b, 0x1e, 0x39, 0x43, 0x4f, 0x55,
10154
+	0x91, 0x76, 0x9b, 0xcc, 0xa4, 0x9b, 0xf5, 0x46, 0x41, 0x63, 0x1d, 0x56, 0xd0, 0x88, 0x8b, 0x97,
10155
+	0x8e, 0xb8, 0x81, 0xc9, 0x8d, 0x18, 0xbf, 0xb5, 0xc7, 0xbd, 0xde, 0x65, 0xa4, 0x43, 0xcc, 0x51,
10156
+	0x7f, 0xf0, 0xbd, 0x0f, 0xcf, 0x9d, 0x78, 0x9f, 0xff, 0xfe, 0xce, 0x7f, 0x6f, 0x7e, 0x74, 0xce,
10157
+	0x7a, 0x8f, 0xff, 0xde, 0xe7, 0xbf, 0x7f, 0xf2, 0xdf, 0xf7, 0x3f, 0x3e, 0x77, 0xe2, 0x66, 0x61,
10158
+	0xef, 0xe2, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xee, 0x17, 0x05, 0xb7, 0x91, 0x33, 0x00, 0x00,
10153 10159
 }
... ...
@@ -493,6 +493,12 @@ message CommonSpec {
493 493
   // be active on a node before the system actively tries to terminate the
494 494
   // build; value must be positive integer
495 495
   optional int64 completionDeadlineSeconds = 8;
496
+
497
+  // nodeSelector is a selector which must be true for the build pod to fit on a node
498
+  // If nil, it can be overridden by default build nodeselector values for the cluster.
499
+  // If set to an empty map or a map with any values, default build nodeselector values
500
+  // are ignored.
501
+  optional OptionalNodeSelector nodeSelector = 9;
496 502
 }
497 503
 
498 504
 // CustomBuildStrategy defines input parameters specific to Custom build.
... ...
@@ -695,6 +701,15 @@ message JenkinsPipelineBuildStrategy {
695 695
   optional string jenkinsfile = 2;
696 696
 }
697 697
 
698
+// OptionalNodeSelector is a map that may also be left nil to distinguish between set and unset.
699
+// +protobuf.nullable=true
700
+// +protobuf.options.(gogoproto.goproto_stringer)=false
701
+message OptionalNodeSelector {
702
+  // items, if empty, will result in an empty map
703
+
704
+  map<string, string> items = 1;
705
+}
706
+
698 707
 // ProxyConfig defines what proxies to use for an operation
699 708
 message ProxyConfig {
700 709
   // httpProxy is a proxy used to reach the git repository over http
... ...
@@ -244,6 +244,7 @@ var map_CommonSpec = map[string]string{
244 244
 	"resources":                 "resources computes resource requirements to execute the build.",
245 245
 	"postCommit":                "postCommit is a build hook executed after the build output image is committed, before it is pushed to a registry.",
246 246
 	"completionDeadlineSeconds": "completionDeadlineSeconds is an optional duration in seconds, counted from the time when a build pod gets scheduled in the system, that the build may be active on a node before the system actively tries to terminate the build; value must be positive integer",
247
+	"nodeSelector":              "nodeSelector is a selector which must be true for the build pod to fit on a node If nil, it can be overridden by default build nodeselector values for the cluster. If set to an empty map or a map with any values, default build nodeselector values are ignored.",
247 248
 }
248 249
 
249 250
 func (CommonSpec) SwaggerDoc() map[string]string {
... ...
@@ -1,6 +1,7 @@
1 1
 package v1
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"time"
5 6
 
6 7
 	"k8s.io/kubernetes/pkg/api/unversioned"
... ...
@@ -34,6 +35,15 @@ type BuildSpec struct {
34 34
 	TriggeredBy []BuildTriggerCause `json:"triggeredBy" protobuf:"bytes,2,rep,name=triggeredBy"`
35 35
 }
36 36
 
37
+// OptionalNodeSelector is a map that may also be left nil to distinguish between set and unset.
38
+// +protobuf.nullable=true
39
+// +protobuf.options.(gogoproto.goproto_stringer)=false
40
+type OptionalNodeSelector map[string]string
41
+
42
+func (t OptionalNodeSelector) String() string {
43
+	return fmt.Sprintf("%v", map[string]string(t))
44
+}
45
+
37 46
 // CommonSpec encapsulates all the inputs necessary to represent a build.
38 47
 type CommonSpec struct {
39 48
 	// serviceAccount is the name of the ServiceAccount to use to run the pod
... ...
@@ -66,6 +76,12 @@ type CommonSpec struct {
66 66
 	// be active on a node before the system actively tries to terminate the
67 67
 	// build; value must be positive integer
68 68
 	CompletionDeadlineSeconds *int64 `json:"completionDeadlineSeconds,omitempty" protobuf:"varint,8,opt,name=completionDeadlineSeconds"`
69
+
70
+	// nodeSelector is a selector which must be true for the build pod to fit on a node
71
+	// If nil, it can be overridden by default build nodeselector values for the cluster.
72
+	// If set to an empty map or a map with any values, default build nodeselector values
73
+	// are ignored.
74
+	NodeSelector OptionalNodeSelector `json:"nodeSelector" protobuf:"bytes,9,name=nodeSelector"`
69 75
 }
70 76
 
71 77
 // BuildTriggerCause holds information about a triggered build. It is used for
... ...
@@ -1171,6 +1171,15 @@ func autoConvert_v1_CommonSpec_To_api_CommonSpec(in *CommonSpec, out *api.Common
1171 1171
 		return err
1172 1172
 	}
1173 1173
 	out.CompletionDeadlineSeconds = in.CompletionDeadlineSeconds
1174
+	if in.NodeSelector != nil {
1175
+		in, out := &in.NodeSelector, &out.NodeSelector
1176
+		*out = make(map[string]string, len(*in))
1177
+		for key, val := range *in {
1178
+			(*out)[key] = val
1179
+		}
1180
+	} else {
1181
+		out.NodeSelector = nil
1182
+	}
1174 1183
 	return nil
1175 1184
 }
1176 1185
 
... ...
@@ -1205,6 +1214,15 @@ func autoConvert_api_CommonSpec_To_v1_CommonSpec(in *api.CommonSpec, out *Common
1205 1205
 		return err
1206 1206
 	}
1207 1207
 	out.CompletionDeadlineSeconds = in.CompletionDeadlineSeconds
1208
+	if in.NodeSelector != nil {
1209
+		in, out := &in.NodeSelector, &out.NodeSelector
1210
+		*out = make(OptionalNodeSelector, len(*in))
1211
+		for key, val := range *in {
1212
+			(*out)[key] = val
1213
+		}
1214
+	} else {
1215
+		out.NodeSelector = nil
1216
+	}
1208 1217
 	return nil
1209 1218
 }
1210 1219
 
... ...
@@ -647,6 +647,15 @@ func DeepCopy_v1_CommonSpec(in interface{}, out interface{}, c *conversion.Clone
647 647
 		} else {
648 648
 			out.CompletionDeadlineSeconds = nil
649 649
 		}
650
+		if in.NodeSelector != nil {
651
+			in, out := &in.NodeSelector, &out.NodeSelector
652
+			*out = make(OptionalNodeSelector)
653
+			for key, val := range *in {
654
+				(*out)[key] = val
655
+			}
656
+		} else {
657
+			out.NodeSelector = nil
658
+		}
650 659
 		return nil
651 660
 	}
652 661
 }
... ...
@@ -9,6 +9,7 @@ import (
9 9
 
10 10
 	"github.com/golang/glog"
11 11
 
12
+	"github.com/openshift/origin/pkg/util/labelselector"
12 13
 	kapi "k8s.io/kubernetes/pkg/api"
13 14
 	"k8s.io/kubernetes/pkg/api/validation"
14 15
 	"k8s.io/kubernetes/pkg/runtime"
... ...
@@ -190,6 +191,7 @@ func validateCommonSpec(spec *buildapi.CommonSpec, fldPath *field.Path) field.Er
190 190
 	allErrs = append(allErrs, validateOutput(&spec.Output, fldPath.Child("output"))...)
191 191
 	allErrs = append(allErrs, validateStrategy(&spec.Strategy, fldPath.Child("strategy"))...)
192 192
 	allErrs = append(allErrs, validatePostCommit(spec.PostCommit, fldPath.Child("postCommit"))...)
193
+	allErrs = append(allErrs, ValidateNodeSelector(spec.NodeSelector, fldPath.Child("nodeSelector"))...)
193 194
 
194 195
 	// TODO: validate resource requirements (prereq: https://github.com/kubernetes/kubernetes/pull/7059)
195 196
 	return allErrs
... ...
@@ -723,3 +725,15 @@ func ValidateImageLabels(labels []buildapi.ImageLabel, fldPath *field.Path) (all
723 723
 
724 724
 	return
725 725
 }
726
+
727
+func ValidateNodeSelector(nodeSelector map[string]string, fldPath *field.Path) field.ErrorList {
728
+	allErrs := field.ErrorList{}
729
+	for k, v := range nodeSelector {
730
+		_, err := labelselector.Parse(fmt.Sprintf("%s=%s", k, v))
731
+		if err != nil {
732
+			allErrs = append(allErrs, field.Invalid(fldPath.Key(k),
733
+				nodeSelector[k], "must be a valid node selector"))
734
+		}
735
+	}
736
+	return allErrs
737
+}
... ...
@@ -2005,6 +2005,22 @@ func TestValidateCommonSpec(t *testing.T) {
2005 2005
 				},
2006 2006
 			},
2007 2007
 		},
2008
+		// 36
2009
+		// invalid nodeselector
2010
+		{
2011
+			string(field.ErrorTypeInvalid) + "nodeSelector[A@B!]",
2012
+			buildapi.CommonSpec{
2013
+				Source: buildapi.BuildSource{
2014
+					Git: &buildapi.GitBuildSource{
2015
+						URI: "http://github.com/my/repository",
2016
+					},
2017
+				},
2018
+				Strategy: buildapi.BuildStrategy{
2019
+					DockerStrategy: &buildapi.DockerBuildStrategy{},
2020
+				},
2021
+				NodeSelector: map[string]string{"A@B!": "C"},
2022
+			},
2023
+		},
2008 2024
 	}
2009 2025
 
2010 2026
 	for count, config := range errorCases {
... ...
@@ -2288,6 +2304,20 @@ func TestValidateCommonSpecSuccess(t *testing.T) {
2288 2288
 				},
2289 2289
 			},
2290 2290
 		},
2291
+		// 10
2292
+		{
2293
+			CommonSpec: buildapi.CommonSpec{
2294
+				Source: buildapi.BuildSource{
2295
+					Git: &buildapi.GitBuildSource{
2296
+						URI: "http://github.com/my/repository",
2297
+					},
2298
+				},
2299
+				Strategy: buildapi.BuildStrategy{
2300
+					DockerStrategy: &buildapi.DockerBuildStrategy{},
2301
+				},
2302
+				NodeSelector: map[string]string{"A": "B", "C": "D"},
2303
+			},
2304
+		},
2291 2305
 	}
2292 2306
 	for count, config := range testCases {
2293 2307
 		errors := validateCommonSpec(&config.CommonSpec, nil)
... ...
@@ -646,6 +646,15 @@ func DeepCopy_api_CommonSpec(in interface{}, out interface{}, c *conversion.Clon
646 646
 		} else {
647 647
 			out.CompletionDeadlineSeconds = nil
648 648
 		}
649
+		if in.NodeSelector != nil {
650
+			in, out := &in.NodeSelector, &out.NodeSelector
651
+			*out = make(map[string]string)
652
+			for key, val := range *in {
653
+				(*out)[key] = val
654
+			}
655
+		} else {
656
+			out.NodeSelector = nil
657
+		}
649 658
 		return nil
650 659
 	}
651 660
 }
... ...
@@ -12,6 +12,8 @@ import (
12 12
 	"k8s.io/kubernetes/pkg/client/record"
13 13
 	kclient "k8s.io/kubernetes/pkg/client/unversioned"
14 14
 
15
+	builddefaults "github.com/openshift/origin/pkg/build/admission/defaults"
16
+	buildoverrides "github.com/openshift/origin/pkg/build/admission/overrides"
15 17
 	buildapi "github.com/openshift/origin/pkg/build/api"
16 18
 	buildclient "github.com/openshift/origin/pkg/build/client"
17 19
 	"github.com/openshift/origin/pkg/build/controller/policy"
... ...
@@ -29,6 +31,8 @@ type BuildController struct {
29 29
 	ImageStreamClient imageStreamClient
30 30
 	Recorder          record.EventRecorder
31 31
 	RunPolicies       []policy.RunPolicy
32
+	BuildDefaults     builddefaults.BuildDefaults
33
+	BuildOverrides    buildoverrides.BuildOverrides
32 34
 }
33 35
 
34 36
 // BuildStrategy knows how to create a pod spec for a pod which can execute a build.
... ...
@@ -186,6 +190,13 @@ func (bc *BuildController) nextBuildPhase(build *buildapi.Build) error {
186 186
 		}
187 187
 		return fmt.Errorf("failed to create a build pod spec for build %s/%s: %v", build.Namespace, build.Name, err)
188 188
 	}
189
+	if err := bc.BuildDefaults.ApplyDefaults(podSpec); err != nil {
190
+		return fmt.Errorf("failed to apply build defaults for build %s/%s: %v", build.Namespace, build.Name, err)
191
+	}
192
+	if err := bc.BuildOverrides.ApplyOverrides(podSpec); err != nil {
193
+		return fmt.Errorf("failed to apply build overrides for build %s/%s: %v", build.Namespace, build.Name, err)
194
+	}
195
+
189 196
 	glog.V(4).Infof("Pod %s for build %s/%s is about to be created", podSpec.Name, build.Namespace, build.Name)
190 197
 
191 198
 	if _, err := bc.PodManager.CreatePod(build.Namespace, podSpec); err != nil {
... ...
@@ -18,6 +18,8 @@ import (
18 18
 	utilruntime "k8s.io/kubernetes/pkg/util/runtime"
19 19
 	"k8s.io/kubernetes/pkg/watch"
20 20
 
21
+	builddefaults "github.com/openshift/origin/pkg/build/admission/defaults"
22
+	buildoverrides "github.com/openshift/origin/pkg/build/admission/overrides"
21 23
 	buildapi "github.com/openshift/origin/pkg/build/api"
22 24
 	buildclient "github.com/openshift/origin/pkg/build/client"
23 25
 	buildcontroller "github.com/openshift/origin/pkg/build/controller"
... ...
@@ -73,6 +75,9 @@ type BuildControllerFactory struct {
73 73
 	DockerBuildStrategy *strategy.DockerBuildStrategy
74 74
 	SourceBuildStrategy *strategy.SourceBuildStrategy
75 75
 	CustomBuildStrategy *strategy.CustomBuildStrategy
76
+	BuildDefaults       builddefaults.BuildDefaults
77
+	BuildOverrides      buildoverrides.BuildOverrides
78
+
76 79
 	// Stop may be set to allow controllers created by this factory to be terminated.
77 80
 	Stop <-chan struct{}
78 81
 }
... ...
@@ -97,7 +102,9 @@ func (factory *BuildControllerFactory) Create() controller.RunnableController {
97 97
 			SourceBuildStrategy: factory.SourceBuildStrategy,
98 98
 			CustomBuildStrategy: factory.CustomBuildStrategy,
99 99
 		},
100
-		Recorder: eventBroadcaster.NewRecorder(kapi.EventSource{Component: "build-controller"}),
100
+		Recorder:       eventBroadcaster.NewRecorder(kapi.EventSource{Component: "build-controller"}),
101
+		BuildDefaults:  factory.BuildDefaults,
102
+		BuildOverrides: factory.BuildOverrides,
101 103
 	}
102 104
 
103 105
 	return &controller.RetryController{
... ...
@@ -89,6 +89,7 @@ func (bs *CustomBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod,
89 89
 				},
90 90
 			},
91 91
 			RestartPolicy: kapi.RestartPolicyNever,
92
+			NodeSelector:  build.Spec.NodeSelector,
92 93
 		},
93 94
 	}
94 95
 	if build.Spec.CompletionDeadlineSeconds != nil {
... ...
@@ -30,18 +30,22 @@ func TestCustomCreateBuildPod(t *testing.T) {
30 30
 		t.Errorf("Expected error when Image is empty, got nothing")
31 31
 	}
32 32
 
33
-	expected := mockCustomBuild(false, false)
34
-	actual, err := strategy.CreateBuildPod(expected)
33
+	build := mockCustomBuild(false, false)
34
+	actual, err := strategy.CreateBuildPod(build)
35 35
 	if err != nil {
36 36
 		t.Fatalf("Unexpected error: %v", err)
37 37
 	}
38 38
 
39
-	if expected, actual := buildapi.GetBuildPodName(expected), actual.ObjectMeta.Name; expected != actual {
39
+	if expected, actual := buildapi.GetBuildPodName(build), actual.ObjectMeta.Name; expected != actual {
40 40
 		t.Errorf("Expected %s, but got %s!", expected, actual)
41 41
 	}
42
-	if !reflect.DeepEqual(map[string]string{buildapi.BuildLabel: buildapi.LabelValue(expected.Name)}, actual.Labels) {
42
+	if !reflect.DeepEqual(map[string]string{buildapi.BuildLabel: buildapi.LabelValue(build.Name)}, actual.Labels) {
43 43
 		t.Errorf("Pod Labels does not match Build Labels!")
44 44
 	}
45
+	if !reflect.DeepEqual(nodeSelector, actual.Spec.NodeSelector) {
46
+		t.Errorf("Pod NodeSelector does not match Build NodeSelector.  Expected: %v, got: %v", nodeSelector, actual.Spec.NodeSelector)
47
+	}
48
+
45 49
 	container := actual.Spec.Containers[0]
46 50
 	if container.Name != "custom-build" {
47 51
 		t.Errorf("Expected custom-build, but got %s!", container.Name)
... ...
@@ -63,13 +67,13 @@ func TestCustomCreateBuildPod(t *testing.T) {
63 63
 			t.Fatalf("Expected %s in VolumeMount[%d], got %s", expected, i, container.VolumeMounts[i].MountPath)
64 64
 		}
65 65
 	}
66
-	if !kapi.Semantic.DeepEqual(container.Resources, expected.Spec.Resources) {
67
-		t.Fatalf("Expected actual=expected, %v != %v", container.Resources, expected.Spec.Resources)
66
+	if !kapi.Semantic.DeepEqual(container.Resources, build.Spec.Resources) {
67
+		t.Fatalf("Expected actual=expected, %v != %v", container.Resources, build.Spec.Resources)
68 68
 	}
69 69
 	if len(actual.Spec.Volumes) != 3 {
70 70
 		t.Fatalf("Expected 3 volumes in Build pod, got %d", len(actual.Spec.Volumes))
71 71
 	}
72
-	buildJSON, _ := runtime.Encode(kapi.Codecs.LegacyCodec(buildapi.SchemeGroupVersion), expected)
72
+	buildJSON, _ := runtime.Encode(kapi.Codecs.LegacyCodec(buildapi.SchemeGroupVersion), build)
73 73
 	errorCases := map[int][]string{
74 74
 		0: {"BUILD", string(buildJSON)},
75 75
 	}
... ...
@@ -218,6 +222,7 @@ func mockCustomBuild(forcePull, emptySource bool) *buildapi.Build {
218 218
 					},
219 219
 				},
220 220
 				CompletionDeadlineSeconds: &timeout,
221
+				NodeSelector:              nodeSelector,
221 222
 			},
222 223
 		},
223 224
 		Status: buildapi.BuildStatus{
... ...
@@ -61,6 +61,7 @@ func (bs *DockerBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod,
61 61
 				},
62 62
 			},
63 63
 			RestartPolicy: kapi.RestartPolicyNever,
64
+			NodeSelector:  build.Spec.NodeSelector,
64 65
 		},
65 66
 	}
66 67
 	pod.Spec.Containers[0].ImagePullPolicy = kapi.PullIfNotPresent
... ...
@@ -20,18 +20,22 @@ func TestDockerCreateBuildPod(t *testing.T) {
20 20
 		Codec: kapi.Codecs.LegacyCodec(buildapi.SchemeGroupVersion),
21 21
 	}
22 22
 
23
-	expected := mockDockerBuild()
24
-	actual, err := strategy.CreateBuildPod(expected)
23
+	build := mockDockerBuild()
24
+	actual, err := strategy.CreateBuildPod(build)
25 25
 	if err != nil {
26 26
 		t.Errorf("Unexpected error: %v", err)
27 27
 	}
28 28
 
29
-	if expected, actual := buildapi.GetBuildPodName(expected), actual.ObjectMeta.Name; expected != actual {
29
+	if expected, actual := buildapi.GetBuildPodName(build), actual.ObjectMeta.Name; expected != actual {
30 30
 		t.Errorf("Expected %s, but got %s!", expected, actual)
31 31
 	}
32
-	if !reflect.DeepEqual(map[string]string{buildapi.BuildLabel: buildapi.LabelValue(expected.Name)}, actual.Labels) {
32
+	if !reflect.DeepEqual(map[string]string{buildapi.BuildLabel: buildapi.LabelValue(build.Name)}, actual.Labels) {
33 33
 		t.Errorf("Pod Labels does not match Build Labels!")
34 34
 	}
35
+	if !reflect.DeepEqual(nodeSelector, actual.Spec.NodeSelector) {
36
+		t.Errorf("Pod NodeSelector does not match Build NodeSelector.  Expected: %v, got: %v", nodeSelector, actual.Spec.NodeSelector)
37
+	}
38
+
35 39
 	container := actual.Spec.Containers[0]
36 40
 	if container.Name != "docker-build" {
37 41
 		t.Errorf("Expected docker-build, but got %s!", container.Name)
... ...
@@ -66,8 +70,8 @@ func TestDockerCreateBuildPod(t *testing.T) {
66 66
 	if len(actual.Spec.Volumes) != 4 {
67 67
 		t.Fatalf("Expected 4 volumes in Build pod, got %d", len(actual.Spec.Volumes))
68 68
 	}
69
-	if !kapi.Semantic.DeepEqual(container.Resources, expected.Spec.Resources) {
70
-		t.Fatalf("Expected actual=expected, %v != %v", container.Resources, expected.Spec.Resources)
69
+	if !kapi.Semantic.DeepEqual(container.Resources, build.Spec.Resources) {
70
+		t.Fatalf("Expected actual=expected, %v != %v", container.Resources, build.Spec.Resources)
71 71
 	}
72 72
 	found := false
73 73
 	foundIllegal := false
... ...
@@ -86,7 +90,7 @@ func TestDockerCreateBuildPod(t *testing.T) {
86 86
 		t.Fatalf("Found illegal environment variable 'ILLEGAL' defined on container")
87 87
 	}
88 88
 
89
-	buildJSON, _ := runtime.Encode(kapi.Codecs.LegacyCodec(buildapi.SchemeGroupVersion), expected)
89
+	buildJSON, _ := runtime.Encode(kapi.Codecs.LegacyCodec(buildapi.SchemeGroupVersion), build)
90 90
 	errorCases := map[int][]string{
91 91
 		0: {"BUILD", string(buildJSON)},
92 92
 	}
... ...
@@ -158,6 +162,7 @@ func mockDockerBuild() *buildapi.Build {
158 158
 					},
159 159
 				},
160 160
 				CompletionDeadlineSeconds: &timeout,
161
+				NodeSelector:              nodeSelector,
161 162
 			},
162 163
 		},
163 164
 		Status: buildapi.BuildStatus{
... ...
@@ -84,6 +84,7 @@ func (bs *SourceBuildStrategy) CreateBuildPod(build *buildapi.Build) (*kapi.Pod,
84 84
 				},
85 85
 			},
86 86
 			RestartPolicy: kapi.RestartPolicyNever,
87
+			NodeSelector:  build.Spec.NodeSelector,
87 88
 		},
88 89
 	}
89 90
 	pod.Spec.Containers[0].ImagePullPolicy = kapi.PullIfNotPresent
... ...
@@ -39,6 +39,8 @@ func TestSTICreateBuildPodRootAllowed(t *testing.T) {
39 39
 	testSTICreateBuildPod(t, true)
40 40
 }
41 41
 
42
+var nodeSelector = map[string]string{"node": "mynode"}
43
+
42 44
 func testSTICreateBuildPod(t *testing.T, rootAllowed bool) {
43 45
 	strategy := &SourceBuildStrategy{
44 46
 		Image:            "sti-test-image",
... ...
@@ -46,18 +48,22 @@ func testSTICreateBuildPod(t *testing.T, rootAllowed bool) {
46 46
 		AdmissionControl: &FakeAdmissionControl{admit: rootAllowed},
47 47
 	}
48 48
 
49
-	expected := mockSTIBuild()
50
-	actual, err := strategy.CreateBuildPod(expected)
49
+	build := mockSTIBuild()
50
+	actual, err := strategy.CreateBuildPod(build)
51 51
 	if err != nil {
52 52
 		t.Errorf("Unexpected error: %v", err)
53 53
 	}
54 54
 
55
-	if expected, actual := buildapi.GetBuildPodName(expected), actual.ObjectMeta.Name; expected != actual {
55
+	if expected, actual := buildapi.GetBuildPodName(build), actual.ObjectMeta.Name; expected != actual {
56 56
 		t.Errorf("Expected %s, but got %s!", expected, actual)
57 57
 	}
58
-	if !reflect.DeepEqual(map[string]string{buildapi.BuildLabel: buildapi.LabelValue(expected.Name)}, actual.Labels) {
58
+	if !reflect.DeepEqual(map[string]string{buildapi.BuildLabel: buildapi.LabelValue(build.Name)}, actual.Labels) {
59 59
 		t.Errorf("Pod Labels does not match Build Labels!")
60 60
 	}
61
+	if !reflect.DeepEqual(nodeSelector, actual.Spec.NodeSelector) {
62
+		t.Errorf("Pod NodeSelector does not match Build NodeSelector.  Expected: %v, got: %v", nodeSelector, actual.Spec.NodeSelector)
63
+	}
64
+
61 65
 	container := actual.Spec.Containers[0]
62 66
 	if container.Name != "sti-build" {
63 67
 		t.Errorf("Expected sti-build, but got %s!", container.Name)
... ...
@@ -71,6 +77,7 @@ func testSTICreateBuildPod(t *testing.T, rootAllowed bool) {
71 71
 	if actual.Spec.RestartPolicy != kapi.RestartPolicyNever {
72 72
 		t.Errorf("Expected never, got %#v", actual.Spec.RestartPolicy)
73 73
 	}
74
+
74 75
 	// strategy ENV is whitelisted into the container environment, and not all
75 76
 	// the values are allowed, so only expect 10 not 11 values.
76 77
 	expectedEnvCount := 10
... ...
@@ -98,8 +105,8 @@ func testSTICreateBuildPod(t *testing.T, rootAllowed bool) {
98 98
 	if *actual.Spec.ActiveDeadlineSeconds != 60 {
99 99
 		t.Errorf("Expected ActiveDeadlineSeconds 60, got %d", *actual.Spec.ActiveDeadlineSeconds)
100 100
 	}
101
-	if !kapi.Semantic.DeepEqual(container.Resources, expected.Spec.Resources) {
102
-		t.Fatalf("Expected actual=expected, %v != %v", container.Resources, expected.Spec.Resources)
101
+	if !kapi.Semantic.DeepEqual(container.Resources, build.Spec.Resources) {
102
+		t.Fatalf("Expected actual=expected, %v != %v", container.Resources, build.Spec.Resources)
103 103
 	}
104 104
 	found := false
105 105
 	foundIllegal := false
... ...
@@ -137,7 +144,7 @@ func testSTICreateBuildPod(t *testing.T, rootAllowed bool) {
137 137
 	if !foundDropCaps && !rootAllowed {
138 138
 		t.Fatalf("Expected %s when root is not allowed", buildapi.DropCapabilities)
139 139
 	}
140
-	buildJSON, _ := runtime.Encode(kapi.Codecs.LegacyCodec(buildapi.SchemeGroupVersion), expected)
140
+	buildJSON, _ := runtime.Encode(kapi.Codecs.LegacyCodec(buildapi.SchemeGroupVersion), build)
141 141
 	errorCases := map[int][]string{
142 142
 		0: {"BUILD", string(buildJSON)},
143 143
 	}
... ...
@@ -215,6 +222,7 @@ func mockSTIBuild() *buildapi.Build {
215 215
 					},
216 216
 				},
217 217
 				CompletionDeadlineSeconds: &timeout,
218
+				NodeSelector:              nodeSelector,
218 219
 			},
219 220
 		},
220 221
 		Status: buildapi.BuildStatus{
... ...
@@ -405,6 +405,7 @@ func (g *BuildGenerator) generateBuildFromConfig(ctx kapi.Context, bc *buildapi.
405 405
 				Resources:                 bcCopy.Spec.Resources,
406 406
 				PostCommit:                bcCopy.Spec.PostCommit,
407 407
 				CompletionDeadlineSeconds: bcCopy.Spec.CompletionDeadlineSeconds,
408
+				NodeSelector:              bcCopy.Spec.NodeSelector,
408 409
 			},
409 410
 		},
410 411
 		ObjectMeta: kapi.ObjectMeta{
... ...
@@ -69,8 +69,6 @@ var legacyOpenshiftAdmissionPlugins = sets.NewString(
69 69
 	overrideapi.PluginName,
70 70
 	serviceadmit.ExternalIPPluginName,
71 71
 	"SecurityContextConstraint",
72
-	"BuildDefaults",
73
-	"BuildOverrides",
74 72
 	"SCCExecRestrictions",
75 73
 )
76 74
 
... ...
@@ -346,8 +346,6 @@ var (
346 346
 		"LimitRanger",
347 347
 		"ServiceAccount",
348 348
 		"SecurityContextConstraint",
349
-		"BuildDefaults",
350
-		"BuildOverrides",
351 349
 		storageclassdefaultadmission.PluginName,
352 350
 		"AlwaysPullImages",
353 351
 		"LimitPodHardAntiAffinityTopology",
... ...
@@ -382,8 +380,6 @@ var (
382 382
 		"LimitRanger",
383 383
 		"ServiceAccount",
384 384
 		"SecurityContextConstraint",
385
-		"BuildDefaults",
386
-		"BuildOverrides",
387 385
 		storageclassdefaultadmission.PluginName,
388 386
 		"AlwaysPullImages",
389 387
 		"LimitPodHardAntiAffinityTopology",
... ...
@@ -28,6 +28,8 @@ import (
28 28
 	utilwait "k8s.io/kubernetes/pkg/util/wait"
29 29
 	serviceaccountadmission "k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
30 30
 
31
+	builddefaults "github.com/openshift/origin/pkg/build/admission/defaults"
32
+	buildoverrides "github.com/openshift/origin/pkg/build/admission/overrides"
31 33
 	buildclient "github.com/openshift/origin/pkg/build/client"
32 34
 	buildcontrollerfactory "github.com/openshift/origin/pkg/build/controller/factory"
33 35
 	buildstrategy "github.com/openshift/origin/pkg/build/controller/strategy"
... ...
@@ -229,7 +231,7 @@ func (c *MasterConfig) RunProjectCache() {
229 229
 }
230 230
 
231 231
 // RunBuildController starts the build sync loop for builds and buildConfig processing.
232
-func (c *MasterConfig) RunBuildController(informers shared.InformerFactory) {
232
+func (c *MasterConfig) RunBuildController(informers shared.InformerFactory) error {
233 233
 	// initialize build controller
234 234
 	dockerImage := c.ImageFor("docker-builder")
235 235
 	stiImage := c.ImageFor("sti-builder")
... ...
@@ -243,6 +245,15 @@ func (c *MasterConfig) RunBuildController(informers shared.InformerFactory) {
243 243
 		wantsInformers.SetInformers(informers)
244 244
 	}
245 245
 
246
+	buildDefaults, err := builddefaults.NewBuildDefaults(c.Options.AdmissionConfig.PluginConfig)
247
+	if err != nil {
248
+		return err
249
+	}
250
+	buildOverrides, err := buildoverrides.NewBuildOverrides(c.Options.AdmissionConfig.PluginConfig)
251
+	if err != nil {
252
+		return err
253
+	}
254
+
246 255
 	osclient, kclient := c.BuildControllerClients()
247 256
 	factory := buildcontrollerfactory.BuildControllerFactory{
248 257
 		KubeClient:   kclient,
... ...
@@ -264,12 +275,15 @@ func (c *MasterConfig) RunBuildController(informers shared.InformerFactory) {
264 264
 			// TODO: this will be set to --storage-version (the internal schema we use)
265 265
 			Codec: codec,
266 266
 		},
267
+		BuildDefaults:  buildDefaults,
268
+		BuildOverrides: buildOverrides,
267 269
 	}
268 270
 
269 271
 	controller := factory.Create()
270 272
 	controller.Run()
271 273
 	deleteController := factory.CreateDeleteController()
272 274
 	deleteController.Run()
275
+	return nil
273 276
 }
274 277
 
275 278
 // RunBuildPodController starts the build/pod status sync loop for build status
... ...
@@ -51,9 +51,6 @@ var (
51 51
 		"OriginNamespaceLifecycle",
52 52
 		"openshift.io/JenkinsBootstrapper",
53 53
 		"BuildByStrategy",
54
-		// TODO: remove the log setting logic from the build defaulter and make this
55
-		// default off again.
56
-		"BuildDefaults",
57 54
 		storageclassdefaultadmission.PluginName,
58 55
 		imageadmission.PluginName,
59 56
 		lifecycle.PluginName,
... ...
@@ -80,7 +77,6 @@ var (
80 80
 		"PodNodeConstraints",
81 81
 		overrideapi.PluginName,
82 82
 		imagepolicy.PluginName,
83
-		"BuildOverrides",
84 83
 		"AlwaysPullImages",
85 84
 		"ImagePolicyWebhook",
86 85
 	)
... ...
@@ -26,8 +26,6 @@ var admissionPluginsNotUsedByKube = sets.NewString(
26 26
 	"ResourceQuota",          // from kube, we replace this with quotaadmission.PluginName
27 27
 
28 28
 	"BuildByStrategy",                  // from origin, only needed for managing builds, not kubernetes resources
29
-	"BuildDefaults",                    // from origin, only needed for managing builds, not kubernetes resources
30
-	"BuildOverrides",                   // from origin, only needed for managing builds, not kubernetes resources
31 29
 	imageadmission.PluginName,          // from origin, used for limiting image sizes, not kubernetes resources
32 30
 	"openshift.io/JenkinsBootstrapper", // from origin, only needed for managing builds, not kubernetes resources
33 31
 	"OriginNamespaceLifecycle",         // from origin, only needed for rejecting openshift resources, so not needed by kube
... ...
@@ -707,7 +707,11 @@ func startControllers(oc *origin.MasterConfig, kc *kubernetes.MasterConfig) erro
707 707
 
708 708
 	// no special order
709 709
 	if configapi.IsBuildEnabled(&oc.Options) {
710
-		oc.RunBuildController(oc.Informers)
710
+		err := oc.RunBuildController(oc.Informers)
711
+		if err != nil {
712
+			glog.Fatalf("Could not start build controller: %v", err)
713
+			return err
714
+		}
711 715
 		oc.RunBuildPodController()
712 716
 		oc.RunBuildConfigChangeController()
713 717
 		oc.RunBuildImageChangeTriggerController()
... ...
@@ -2,6 +2,7 @@ package integration
2 2
 
3 3
 import (
4 4
 	"reflect"
5
+	"strings"
5 6
 	"testing"
6 7
 	"time"
7 8
 
... ...
@@ -68,6 +69,42 @@ func TestBuildDefaultEnvironment(t *testing.T) {
68 68
 	}
69 69
 }
70 70
 
71
+func TestBuildDefaultLabels(t *testing.T) {
72
+	defer testutil.DumpEtcdOnFailure(t)
73
+	labels := []buildapi.ImageLabel{{Name: "KEY", Value: "VALUE"}}
74
+	oclient, kclient := setupBuildDefaultsAdmissionTest(t, &defaultsapi.BuildDefaultsConfig{
75
+		ImageLabels: labels,
76
+	})
77
+	build, _ := runBuildPodAdmissionTest(t, oclient, kclient, buildPodAdmissionTestDockerBuild())
78
+	if actual := build.Spec.Output.ImageLabels; !reflect.DeepEqual(labels, actual) {
79
+		t.Errorf("Resulting build did not get expected labels: %v", actual)
80
+	}
81
+}
82
+
83
+func TestBuildDefaultNodeSelectors(t *testing.T) {
84
+	defer testutil.DumpEtcdOnFailure(t)
85
+	selectors := map[string]string{"KEY": "VALUE"}
86
+	oclient, kclient := setupBuildDefaultsAdmissionTest(t, &defaultsapi.BuildDefaultsConfig{
87
+		NodeSelector: selectors,
88
+	})
89
+	_, pod := runBuildPodAdmissionTest(t, oclient, kclient, buildPodAdmissionTestDockerBuild())
90
+	if actual := pod.Spec.NodeSelector; !reflect.DeepEqual(selectors, actual) {
91
+		t.Errorf("Resulting pod did not get expected nodeselectors: %v", actual)
92
+	}
93
+}
94
+
95
+func TestBuildDefaultAnnotations(t *testing.T) {
96
+	defer testutil.DumpEtcdOnFailure(t)
97
+	annotations := map[string]string{"KEY": "VALUE"}
98
+	oclient, kclient := setupBuildDefaultsAdmissionTest(t, &defaultsapi.BuildDefaultsConfig{
99
+		Annotations: annotations,
100
+	})
101
+	_, pod := runBuildPodAdmissionTest(t, oclient, kclient, buildPodAdmissionTestDockerBuild())
102
+	if actual := pod.Annotations; strings.Compare(actual["KEY"], annotations["KEY"]) != 0 {
103
+		t.Errorf("Resulting pod did not get expected annotations: actual: %v, expected: %v", actual["KEY"], annotations["KEY"])
104
+	}
105
+}
106
+
71 107
 func TestBuildOverrideForcePull(t *testing.T) {
72 108
 	defer testutil.DumpEtcdOnFailure(t)
73 109
 	oclient, kclient := setupBuildOverridesAdmissionTest(t, &overridesapi.BuildOverridesConfig{
... ...
@@ -93,6 +130,42 @@ func TestBuildOverrideForcePullCustomStrategy(t *testing.T) {
93 93
 	}
94 94
 }
95 95
 
96
+func TestBuildOverrideLabels(t *testing.T) {
97
+	defer testutil.DumpEtcdOnFailure(t)
98
+	labels := []buildapi.ImageLabel{{Name: "KEY", Value: "VALUE"}}
99
+	oclient, kclient := setupBuildOverridesAdmissionTest(t, &overridesapi.BuildOverridesConfig{
100
+		ImageLabels: labels,
101
+	})
102
+	build, _ := runBuildPodAdmissionTest(t, oclient, kclient, buildPodAdmissionTestDockerBuild())
103
+	if actual := build.Spec.Output.ImageLabels; !reflect.DeepEqual(labels, actual) {
104
+		t.Errorf("Resulting build did not get expected labels: %v", actual)
105
+	}
106
+}
107
+
108
+func TestBuildOverrideNodeSelectors(t *testing.T) {
109
+	defer testutil.DumpEtcdOnFailure(t)
110
+	selectors := map[string]string{"KEY": "VALUE"}
111
+	oclient, kclient := setupBuildOverridesAdmissionTest(t, &overridesapi.BuildOverridesConfig{
112
+		NodeSelector: selectors,
113
+	})
114
+	_, pod := runBuildPodAdmissionTest(t, oclient, kclient, buildPodAdmissionTestDockerBuild())
115
+	if actual := pod.Spec.NodeSelector; !reflect.DeepEqual(selectors, actual) {
116
+		t.Errorf("Resulting build did not get expected nodeselectors: %v", actual)
117
+	}
118
+}
119
+
120
+func TestBuildOverrideAnnotations(t *testing.T) {
121
+	defer testutil.DumpEtcdOnFailure(t)
122
+	annotations := map[string]string{"KEY": "VALUE"}
123
+	oclient, kclient := setupBuildOverridesAdmissionTest(t, &overridesapi.BuildOverridesConfig{
124
+		Annotations: annotations,
125
+	})
126
+	_, pod := runBuildPodAdmissionTest(t, oclient, kclient, buildPodAdmissionTestDockerBuild())
127
+	if actual := pod.Annotations; strings.Compare(actual["KEY"], annotations["KEY"]) != 0 {
128
+		t.Errorf("Resulting build did not get expected annotations: %v", actual)
129
+	}
130
+}
131
+
96 132
 func buildPodAdmissionTestCustomBuild() *buildapi.Build {
97 133
 	build := &buildapi.Build{ObjectMeta: kapi.ObjectMeta{
98 134
 		Labels: map[string]string{
... ...
@@ -189,7 +262,7 @@ func setupBuildPodAdmissionTest(t *testing.T, pluginConfig map[string]configapi.
189 189
 	if err != nil {
190 190
 		t.Fatalf("%v", err)
191 191
 	}
192
-	master.KubernetesMasterConfig.AdmissionConfig.PluginConfig = pluginConfig
192
+	master.AdmissionConfig.PluginConfig = pluginConfig
193 193
 	clusterAdminKubeConfig, err := testserver.StartConfiguredMaster(master)
194 194
 	if err != nil {
195 195
 		t.Fatalf("%v", err)
... ...
@@ -23,6 +23,7 @@ items:
23 23
     creationTimestamp: null
24 24
     name: nginx
25 25
   spec:
26
+    nodeSelector: null
26 27
     output:
27 28
       to:
28 29
         kind: ImageStreamTag
... ...
@@ -77,6 +78,7 @@ items:
77 77
     creationTimestamp: null
78 78
     name: web
79 79
   spec:
80
+    nodeSelector: null
80 81
     output:
81 82
       to:
82 83
         kind: ImageStreamTag