Browse code

add serializeable start config

deads2k authored on 2015/03/04 23:37:17
Showing 64 changed files
... ...
@@ -75,7 +75,7 @@ OPENSHIFT_ON_PANIC=crash openshift start --master="${API_SCHEME}://${API_HOST}:$
75 75
 OS_PID=$!
76 76
 
77 77
 if [[ "${API_SCHEME}" == "https" ]]; then
78
-    export CURL_CA_BUNDLE="${CERT_DIR}/admin/root.crt"
78
+    export CURL_CA_BUNDLE="${CERT_DIR}/ca/cert.crt"
79 79
     export CURL_CERT="${CERT_DIR}/admin/cert.crt"
80 80
     export CURL_KEY="${CERT_DIR}/admin/key.key"
81 81
 fi
... ...
@@ -174,14 +174,18 @@ echo "templates: ok"
174 174
 [ "$(osc get --help 2>&1 | grep 'Display one or many resources')" ]
175 175
 [ "$(openshift cli get --help 2>&1 | grep 'Display one or many resources')" ]
176 176
 [ "$(openshift kubectl get --help 2>&1 | grep 'Display one or many resources')" ]
177
-[ "$(openshift start --help 2>&1 | grep 'Start an OpenShift server')" ]
177
+[ "$(openshift start --help 2>&1 | grep 'Start an OpenShift all-in-one server')" ]
178
+[ "$(openshift start master --help 2>&1 | grep 'Start an OpenShift master')" ]
179
+[ "$(openshift start node --help 2>&1 | grep 'Start an OpenShift node')" ]
178 180
 [ "$(osc get --help 2>&1 | grep 'osc')" ]
179 181
 
180 182
 # help for given command through help command must be consistent
181 183
 [ "$(osc help get 2>&1 | grep 'Display one or many resources')" ]
182 184
 [ "$(openshift cli help get 2>&1 | grep 'Display one or many resources')" ]
183 185
 [ "$(openshift kubectl help get 2>&1 | grep 'Display one or many resources')" ]
184
-[ "$(openshift help start 2>&1 | grep 'Start an OpenShift server')" ]
186
+[ "$(openshift help start 2>&1 | grep 'Start an OpenShift all-in-one server')" ]
187
+[ "$(openshift help start master 2>&1 | grep 'Start an OpenShift master')" ]
188
+[ "$(openshift help start node 2>&1 | grep 'Start an OpenShift node')" ]
185 189
 [ "$(openshift cli help update 2>&1 | grep 'openshift')" ]
186 190
 
187 191
 # runnable commands with required flags must error consistently
... ...
@@ -50,9 +50,12 @@ LOG_DIR="${LOG_DIR:-${BASETMPDIR}/logs}"
50 50
 ARTIFACT_DIR="${ARTIFACT_DIR:-${BASETMPDIR}/artifacts}"
51 51
 mkdir -p $LOG_DIR
52 52
 mkdir -p $ARTIFACT_DIR
53
+
54
+DEFAULT_SERVER_IP=`ifconfig | grep -Ev "(127.0.0.1|172.17.42.1)" | grep "inet " | head -n 1 | awk '{print $2}'`
55
+API_HOST="${API_HOST:-${DEFAULT_SERVER_IP}}"
53 56
 API_PORT="${API_PORT:-8443}"
54 57
 API_SCHEME="${API_SCHEME:-https}"
55
-API_HOST="${API_HOST:-localhost}"
58
+MASTER_ADDR="${API_SCHEME}://${API_HOST}:${API_PORT}"
56 59
 PUBLIC_MASTER_HOST="${PUBLIC_MASTER_HOST:-${API_HOST}}"
57 60
 KUBELET_SCHEME="${KUBELET_SCHEME:-http}"
58 61
 KUBELET_PORT="${KUBELET_PORT:-10250}"
... ...
@@ -168,25 +171,32 @@ echo "[INFO] Using images:              ${USE_IMAGES}"
168 168
 
169 169
 # Start All-in-one server and wait for health
170 170
 # Specify the scheme and port for the listen address, but let the IP auto-discover.	Set --public-master to localhost, for a stable link to the console.
171
+echo "[INFO] Create certificates for the OpenShift server"
172
+# find the same IP that openshift start will bind to.  This allows access from pods that have to talk back to master
173
+ALL_IP_ADDRESSES=`ifconfig | grep "inet " | awk '{print $2}'`
174
+SERVER_HOSTNAME_LIST="${PUBLIC_MASTER_HOST},localhost"
175
+while read -r IP_ADDRESS
176
+do
177
+	SERVER_HOSTNAME_LIST="${SERVER_HOSTNAME_LIST},${IP_ADDRESS}"
178
+done <<< "${ALL_IP_ADDRESSES}"
179
+
180
+openshift admin create-all-certs --overwrite=false --cert-dir="${CERT_DIR}" --hostnames="${SERVER_HOSTNAME_LIST}" --nodes="127.0.0.1" --master="${MASTER_ADDR}" --public-master="${API_SCHEME}://${PUBLIC_MASTER_HOST}"
181
+
182
+
171 183
 echo "[INFO] Starting OpenShift server"
172 184
 sudo env "PATH=${PATH}" OPENSHIFT_ON_PANIC=crash openshift start \
173
-     --listen="${API_SCHEME}://0.0.0.0:${API_PORT}" --public-master="${API_SCHEME}://${PUBLIC_MASTER_HOST}" \
185
+     --listen="${API_SCHEME}://0.0.0.0:${API_PORT}"  --master="${MASTER_ADDR}" --public-master="${API_SCHEME}://${PUBLIC_MASTER_HOST}" \
174 186
      --hostname="127.0.0.1" --volume-dir="${VOLUME_DIR}" \
175 187
      --etcd-dir="${ETCD_DATA_DIR}" --cert-dir="${CERT_DIR}" --loglevel=4 \
176
-     --images="${USE_IMAGES}" \
188
+     --images="${USE_IMAGES}" --create-certs=false\
177 189
      &> "${LOG_DIR}/openshift.log" &
178 190
 OS_PID=$!
179 191
 
180 192
 if [[ "${API_SCHEME}" == "https" ]]; then
181
-	export CURL_CA_BUNDLE="${CERT_DIR}/master/root.crt"
193
+	export CURL_CA_BUNDLE="${CERT_DIR}/ca/cert.crt"
182 194
 	export CURL_CERT="${CERT_DIR}/admin/cert.crt"
183 195
 	export CURL_KEY="${CERT_DIR}/admin/key.key"
184 196
 
185
-	# Generate the certs first
186
-	wait_for_file "${CERT_DIR}/openshift-client/key.key" 0.5 80
187
-	wait_for_file "${CERT_DIR}/admin/key.key" 0.5 80
188
-	wait_for_file "${CURL_CA_BUNDLE}" 0.5 80
189
-
190 197
 	# Read client cert data in to send to containerized components
191 198
 	sudo chmod -R a+rX "${CERT_DIR}/openshift-client/"
192 199
 
... ...
@@ -204,6 +214,9 @@ wait_for_url "${API_SCHEME}://${API_HOST}:${API_PORT}/api/v1beta1/minions/127.0.
204 204
 # Set KUBERNETES_MASTER for osc
205 205
 export KUBERNETES_MASTER="${API_SCHEME}://${API_HOST}:${API_PORT}"
206 206
 
207
+# add e2e-user as a viewer for the default namespace so we can see infrastructure pieces appear
208
+openshift ex policy add-user view anypassword:e2e-user --namespace=default
209
+
207 210
 # create test project so that this shows up in the console
208 211
 openshift ex new-project test --description="This is an example project to demonstrate OpenShift v3" --admin="anypassword:e2e-user"
209 212
 
... ...
@@ -44,12 +44,23 @@ start_etcd
44 44
 trap cleanup EXIT SIGINT
45 45
 
46 46
 function exectest() {
47
+  echo "running $1..."
48
+
47 49
   out=$("${testexec}" -test.run="^$1$" "${@:2}" 2>&1)
50
+
51
+  tput cuu 1 # Move up one line
52
+  tput el    # Clear "running" line
53
+
48 54
   res=$?
49 55
   if [[ ${res} -eq 0 ]]; then
50
-    echo ok $1
56
+    tput setaf 2 # green
57
+    echo "ok      $1"
58
+    tput sgr0    # reset
51 59
     exit 0
52 60
   else
61
+    tput setaf 1 # red
62
+    echo "failed  $1"
63
+    tput sgr0    # reset
53 64
     echo "${out}"
54 65
     exit 1
55 66
   fi
... ...
@@ -25,6 +25,7 @@ find_files() {
25 25
       \( \
26 26
         -wholename './output' \
27 27
         -o -wholename './_output' \
28
+        -o -wholename './.git' \
28 29
         -o -wholename './release' \
29 30
         -o -wholename './pkg/assets/bindata.go' \
30 31
         -o -wholename './target' \
... ...
@@ -33,7 +33,7 @@ func TestSubjects(t *testing.T) {
33 33
 			Resource: "pods",
34 34
 		},
35 35
 		expectedUsers:  util.NewStringSet("Anna", "ClusterAdmin", "Ellen", "Valerie", "system:kube-client", "system:openshift-client", "system:openshift-deployer"),
36
-		expectedGroups: util.NewStringSet("RootUsers", "system:cluster-admins"),
36
+		expectedGroups: util.NewStringSet("RootUsers", "system:cluster-admins", "system:nodes"),
37 37
 	}
38 38
 	test.policies = newDefaultGlobalPolicies()
39 39
 	test.policies = append(test.policies, newAdzePolicies()...)
... ...
@@ -18,7 +18,8 @@ import (
18 18
 	"github.com/openshift/origin/pkg/cmd/infra/builder"
19 19
 	"github.com/openshift/origin/pkg/cmd/infra/deployer"
20 20
 	"github.com/openshift/origin/pkg/cmd/infra/router"
21
-	"github.com/openshift/origin/pkg/cmd/server"
21
+	"github.com/openshift/origin/pkg/cmd/server/certs"
22
+	"github.com/openshift/origin/pkg/cmd/server/start"
22 23
 	"github.com/openshift/origin/pkg/cmd/templates"
23 24
 	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
24 25
 	"github.com/openshift/origin/pkg/version"
... ...
@@ -78,8 +79,9 @@ func NewCommandOpenShift() *cobra.Command {
78 78
 	root.SetUsageTemplate(templates.MainUsageTemplate())
79 79
 	root.SetHelpTemplate(templates.MainHelpTemplate())
80 80
 
81
-	openshiftStartCommand, _ := server.NewCommandStartServer("start")
82
-	root.AddCommand(openshiftStartCommand)
81
+	startAllInOne, _ := start.NewCommandStartAllInOne()
82
+	root.AddCommand(startAllInOne)
83
+	root.AddCommand(certs.NewCommandAdmin())
83 84
 	root.AddCommand(cli.NewCommandCLI("cli", "openshift cli"))
84 85
 	root.AddCommand(cli.NewCmdKubectl("kube"))
85 86
 	root.AddCommand(newExperimentalCommand("openshift", "ex"))
86 87
new file mode 100644
... ...
@@ -0,0 +1,125 @@
0
+package api
1
+
2
+import (
3
+	"crypto/x509"
4
+	"fmt"
5
+	"io/ioutil"
6
+
7
+	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
9
+
10
+	"github.com/openshift/origin/pkg/client"
11
+	"github.com/openshift/origin/pkg/cmd/server/crypto"
12
+)
13
+
14
+func GetKubeClient(kubeConfigFile string) (*kclient.Client, *kclient.Config, error) {
15
+	loadingRules := &clientcmd.ClientConfigLoadingRules{CommandLinePath: kubeConfigFile}
16
+	loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
17
+
18
+	kubeConfig, err := loader.ClientConfig()
19
+	if err != nil {
20
+		return nil, nil, err
21
+	}
22
+	kubeClient, err := kclient.New(kubeConfig)
23
+	if err != nil {
24
+		return nil, nil, err
25
+	}
26
+
27
+	return kubeClient, kubeConfig, nil
28
+}
29
+
30
+func GetOpenShiftClient(kubeConfigFile string) (*client.Client, *kclient.Config, error) {
31
+	loadingRules := &clientcmd.ClientConfigLoadingRules{CommandLinePath: kubeConfigFile}
32
+	loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
33
+
34
+	kubeConfig, err := loader.ClientConfig()
35
+	if err != nil {
36
+		return nil, nil, err
37
+	}
38
+	openshiftClient, err := client.New(kubeConfig)
39
+	if err != nil {
40
+		return nil, nil, err
41
+	}
42
+
43
+	return openshiftClient, kubeConfig, nil
44
+}
45
+
46
+func UseTLS(servingInfo ServingInfo) bool {
47
+	return len(servingInfo.ServerCert.CertFile) > 0
48
+}
49
+
50
+// GetAPIClientCertCAPool returns the cert pool used to validate client certificates to the API server
51
+func GetAPIClientCertCAPool(options MasterConfig) (*x509.CertPool, error) {
52
+	certs, err := getAPIClientCertCAs(options)
53
+	if err != nil {
54
+		return nil, err
55
+	}
56
+	roots := x509.NewCertPool()
57
+	for _, root := range certs {
58
+		roots.AddCert(root)
59
+	}
60
+	return roots, nil
61
+}
62
+
63
+// GetClientCertCAPool returns a cert pool containing all client CAs that could be presented (union of API and OAuth)
64
+func GetClientCertCAPool(options MasterConfig) (*x509.CertPool, error) {
65
+	roots := x509.NewCertPool()
66
+
67
+	// Add CAs for OAuth
68
+	certs, err := getOAuthClientCertCAs(options)
69
+	if err != nil {
70
+		return nil, err
71
+	}
72
+	for _, root := range certs {
73
+		roots.AddCert(root)
74
+	}
75
+
76
+	// Add CAs for API
77
+	certs, err = getAPIClientCertCAs(options)
78
+	if err != nil {
79
+		return nil, err
80
+	}
81
+	for _, root := range certs {
82
+		roots.AddCert(root)
83
+	}
84
+
85
+	return roots, nil
86
+}
87
+
88
+// GetAPIServerCertCAPool returns the cert pool containing the roots for the API server cert
89
+func GetAPIServerCertCAPool(options MasterConfig) (*x509.CertPool, error) {
90
+	caRoots, err := crypto.GetTLSCARoots(options.ServingInfo.ClientCA)
91
+	if err != nil {
92
+		return nil, err
93
+	}
94
+	roots := x509.NewCertPool()
95
+	for _, root := range caRoots.Roots {
96
+		roots.AddCert(root)
97
+	}
98
+	return roots, nil
99
+}
100
+
101
+func getOAuthClientCertCAs(options MasterConfig) ([]*x509.Certificate, error) {
102
+	caFile := options.OAuthConfig.ProxyCA
103
+	if len(caFile) == 0 {
104
+		return nil, nil
105
+	}
106
+	caPEMBlock, err := ioutil.ReadFile(caFile)
107
+	if err != nil {
108
+		return nil, err
109
+	}
110
+	certs, err := crypto.CertsFromPEM(caPEMBlock)
111
+	if err != nil {
112
+		return nil, fmt.Errorf("Error reading %s: %s", caFile, err)
113
+	}
114
+	return certs, nil
115
+}
116
+
117
+func getAPIClientCertCAs(options MasterConfig) ([]*x509.Certificate, error) {
118
+	apiClientCertCAs, err := crypto.GetTLSCARoots(options.ServingInfo.ClientCA)
119
+	if err != nil {
120
+		return nil, err
121
+	}
122
+
123
+	return apiClientCertCAs.Roots, nil
124
+}
0 125
new file mode 100644
... ...
@@ -0,0 +1,24 @@
0
+package latest
1
+
2
+import (
3
+	"github.com/openshift/origin/pkg/cmd/server/api/v1"
4
+)
5
+
6
+// Version is the string that represents the current external default version.
7
+const Version = "v1"
8
+
9
+// OldestVersion is the string that represents the oldest server version supported,
10
+// for client code that wants to hardcode the lowest common denominator.
11
+const OldestVersion = "v1"
12
+
13
+// Versions is the list of versions that are recognized in code. The order provided
14
+// may be assumed to be least feature rich to most feature rich, and clients may
15
+// choose to prefer the latter items in the list over the former items when presented
16
+// with a set of versions to choose.
17
+var Versions = []string{"v1"}
18
+
19
+// Codec is the default codec for serializing output that should use
20
+// the latest supported version.  Use this Codec when writing to
21
+// disk, a data store that is not dynamically versioned, or in tests.
22
+// This codec can decode any object that Kubernetes is aware of.
23
+var Codec = v1.Codec
0 24
new file mode 100644
... ...
@@ -0,0 +1,17 @@
0
+package api
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
4
+)
5
+
6
+var Scheme = runtime.NewScheme()
7
+
8
+func init() {
9
+	Scheme.AddKnownTypes("",
10
+		&MasterConfig{},
11
+		&NodeConfig{},
12
+	)
13
+}
14
+
15
+func (*MasterConfig) IsAnAPIObject() {}
16
+func (*NodeConfig) IsAnAPIObject()   {}
0 17
new file mode 100644
... ...
@@ -0,0 +1,161 @@
0
+package api
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+)
5
+
6
+// NodeConfig is the fully specified config starting an OpenShift node
7
+type NodeConfig struct {
8
+	api.TypeMeta
9
+
10
+	// NodeName is the value used to identify this particular node in the cluster.  If possible, this should be your fully qualified hostname.
11
+	// If you're describing a set of static nodes to the master, this value must match one of the values in the list
12
+	NodeName string
13
+
14
+	// ServingInfo describes how to start serving
15
+	ServingInfo ServingInfo
16
+
17
+	// MasterKubeConfig is a filename for the .kubeconfig file that describes how to connect this node to the master
18
+	MasterKubeConfig string
19
+
20
+	// domain suffix
21
+	DNSDomain string
22
+	// ip
23
+	DNSIP string
24
+
25
+	// VolumeDir is the directory that volumes will be stored under
26
+	VolumeDirectory string
27
+
28
+	// NetworkContainerImage is the image used as the Kubelet network namespace and volume container.
29
+	NetworkContainerImage string
30
+
31
+	// AllowDisabledDocker if true, the Kubelet will ignore errors from Docker.  This means that a node can start on a machine that doesn't have docker started.
32
+	AllowDisabledDocker bool
33
+
34
+	// RecordEvents indicates whether or not to record events from the master
35
+	RecordEvents bool
36
+}
37
+
38
+type MasterConfig struct {
39
+	api.TypeMeta
40
+
41
+	// ServingInfo describes how to start serving
42
+	ServingInfo ServingInfo
43
+
44
+	// CORSAllowedOrigins
45
+	CORSAllowedOrigins []string
46
+
47
+	// EtcdClientInfo contains information about how to connect to etcd
48
+	EtcdClientInfo RemoteConnectionInfo
49
+
50
+	// KubernetesMasterConfig, if present start the kubernetes master in this process
51
+	KubernetesMasterConfig *KubernetesMasterConfig
52
+	// EtcdConfig, if present start etcd in this process
53
+	EtcdConfig *EtcdConfig
54
+	// OAuthConfig, if present start the /oauth endpoint in this process
55
+	OAuthConfig *OAuthConfig
56
+	// AssetConfig, if present start the asset serverin this process
57
+	AssetConfig *AssetConfig
58
+	// DNSConfig, if present start the DNS server in this process
59
+	DNSConfig *DNSConfig
60
+
61
+	// MasterClients holds all the client connection information for controllers and other system components
62
+	MasterClients MasterClients
63
+
64
+	ImageConfig ImageConfig
65
+
66
+	// MasterAuthorizationNamespace is the global namespace for Policy
67
+	MasterAuthorizationNamespace string
68
+	// OpenShiftSharedResourcesNamespace is the namespace where shared OpenShift resources live (like shared templates)
69
+	OpenShiftSharedResourcesNamespace string
70
+}
71
+
72
+type ImageConfig struct {
73
+	Format string
74
+	Latest bool
75
+}
76
+
77
+type RemoteConnectionInfo struct {
78
+	// URL is the URL for etcd
79
+	URL string
80
+	// CA is the CA for confirming that the server at the etcdURL is the actual server
81
+	CA string
82
+	// EtcdClientCertInfo is the TLS client cert information for securing communication to  etcd
83
+	// this is anonymous so that we can inline it for serialization
84
+	ClientCert CertInfo
85
+}
86
+
87
+type ServingInfo struct {
88
+	// BindAddress is the ip:port to serve on
89
+	BindAddress string
90
+	// ServerCert is the TLS cert info for serving secure traffic
91
+	ServerCert CertInfo
92
+	// ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
93
+	ClientCA string
94
+}
95
+
96
+type MasterClients struct {
97
+	// DeployerKubeConfig is a .kubeconfig filename for depoyment pods to use
98
+	DeployerKubeConfig string
99
+	// OpenShiftLoopbackKubeConfig is a .kubeconfig filename for system components to loopback to this master
100
+	OpenShiftLoopbackKubeConfig string
101
+	// KubernetesKubeConfig is a .kubeconfig filename for system components to communicate to kubernetes for building the proxy
102
+	KubernetesKubeConfig string
103
+}
104
+
105
+type DNSConfig struct {
106
+	// BindAddress is the ip:port to serve DNS on
107
+	BindAddress string
108
+}
109
+
110
+type AssetConfig struct {
111
+	ServingInfo ServingInfo
112
+
113
+	// PublicURL is where you can find the asset server (TODO do we really need this?)
114
+	PublicURL string
115
+
116
+	// LogoutURI is an optional, absolute URI to redirect web browsers to after logging out of the web console.
117
+	// If not specified, the built-in logout page is shown.
118
+	LogoutURI string
119
+
120
+	// MasterPublicURL is how the web console can access the OpenShift api server
121
+	MasterPublicURL string
122
+
123
+	// TODO: we probably don't need this since we have a proxy
124
+	// KubernetesPublicURL is how the web console can access the Kubernetes api server
125
+	KubernetesPublicURL string
126
+}
127
+
128
+type OAuthConfig struct {
129
+	// ProxyCA is the certificate bundle for confirming the identity of front proxy forwards to the oauth server
130
+	ProxyCA string
131
+
132
+	// MasterURL is used for building valid client redirect URLs for external access
133
+	MasterURL string
134
+
135
+	// MasterPublicURL is used for building valid client redirect URLs for external access
136
+	MasterPublicURL string
137
+
138
+	// AssetPublicURL is used for building valid client redirect URLs for external access
139
+	AssetPublicURL string
140
+
141
+	// all the handlers here
142
+}
143
+
144
+type EtcdConfig struct {
145
+	ServingInfo ServingInfo
146
+
147
+	PeerAddress   string
148
+	MasterAddress string
149
+	StorageDir    string
150
+}
151
+
152
+type KubernetesMasterConfig struct {
153
+	ServicesSubnet  string
154
+	StaticNodeNames []string
155
+}
156
+
157
+type CertInfo struct {
158
+	CertFile string
159
+	KeyFile  string
160
+}
0 161
new file mode 100644
... ...
@@ -0,0 +1,43 @@
0
+package v1
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
4
+	newer "github.com/openshift/origin/pkg/cmd/server/api"
5
+)
6
+
7
+func init() {
8
+	err := newer.Scheme.AddConversionFuncs(
9
+		func(in *ServingInfo, out *newer.ServingInfo, s conversion.Scope) error {
10
+			out.BindAddress = in.BindAddress
11
+			out.ClientCA = in.ClientCA
12
+			out.ServerCert.CertFile = in.CertFile
13
+			out.ServerCert.KeyFile = in.KeyFile
14
+			return nil
15
+		},
16
+		func(in *newer.ServingInfo, out *ServingInfo, s conversion.Scope) error {
17
+			out.BindAddress = in.BindAddress
18
+			out.ClientCA = in.ClientCA
19
+			out.CertFile = in.ServerCert.CertFile
20
+			out.KeyFile = in.ServerCert.KeyFile
21
+			return nil
22
+		},
23
+		func(in *RemoteConnectionInfo, out *newer.RemoteConnectionInfo, s conversion.Scope) error {
24
+			out.URL = in.URL
25
+			out.CA = in.CA
26
+			out.ClientCert.CertFile = in.CertFile
27
+			out.ClientCert.KeyFile = in.KeyFile
28
+			return nil
29
+		},
30
+		func(in *newer.RemoteConnectionInfo, out *RemoteConnectionInfo, s conversion.Scope) error {
31
+			out.URL = in.URL
32
+			out.CA = in.CA
33
+			out.CertFile = in.ClientCert.CertFile
34
+			out.KeyFile = in.ClientCert.KeyFile
35
+			return nil
36
+		},
37
+	)
38
+	if err != nil {
39
+		// If one of the conversion functions is malformed, detect it immediately.
40
+		panic(err)
41
+	}
42
+}
0 43
new file mode 100644
... ...
@@ -0,0 +1,18 @@
0
+package v1
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
4
+	"github.com/openshift/origin/pkg/cmd/server/api"
5
+)
6
+
7
+var Codec = runtime.CodecFor(api.Scheme, "v1")
8
+
9
+func init() {
10
+	api.Scheme.AddKnownTypes("v1",
11
+		&MasterConfig{},
12
+		&NodeConfig{},
13
+	)
14
+}
15
+
16
+func (*MasterConfig) IsAnAPIObject() {}
17
+func (*NodeConfig) IsAnAPIObject()   {}
0 18
new file mode 100644
... ...
@@ -0,0 +1,162 @@
0
+package v1
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta3"
4
+)
5
+
6
+// NodeConfig is the fully specified config starting an OpenShift node
7
+type NodeConfig struct {
8
+	v1beta3.TypeMeta `json:",inline"`
9
+
10
+	// NodeName is the value used to identify this particular node in the cluster.  If possible, this should be your fully qualified hostname.
11
+	// If you're describing a set of static nodes to the master, this value must match one of the values in the list
12
+	NodeName string `json:"nodeName"`
13
+
14
+	// ServingInfo describes how to start serving
15
+	ServingInfo ServingInfo `json:"servingInfo"`
16
+
17
+	// MasterKubeConfig is a filename for the .kubeconfig file that describes how to connect this node to the master
18
+	MasterKubeConfig string `json:"masterKubeConfig"`
19
+
20
+	// domain suffix
21
+	DNSDomain string `json:"dnsDomain"`
22
+	// ip
23
+	DNSIP string `json:"dnsIP"`
24
+
25
+	// VolumeDir is the directory that volumes will be stored under
26
+	VolumeDirectory string `json:"volumeDirectory"`
27
+
28
+	// NetworkContainerImage is the image used as the Kubelet network namespace and volume container.
29
+	NetworkContainerImage string `json:"networkContainerImage"`
30
+
31
+	// AllowDisabledDocker if true, the Kubelet will ignore errors from Docker.  This means that a node can start on a machine that doesn't have docker started.
32
+	AllowDisabledDocker bool `json:"allowDisabledDocker"`
33
+
34
+	// RecordEvents indicates whether or not to record events from the master
35
+	RecordEvents bool `json:"recordEvents"`
36
+}
37
+
38
+type MasterConfig struct {
39
+	v1beta3.TypeMeta `json:",inline"`
40
+
41
+	// ServingInfo describes how to start serving
42
+	ServingInfo ServingInfo `json:"servingInfo"`
43
+
44
+	// CORSAllowedOrigins
45
+	CORSAllowedOrigins []string `json:"corsAllowedOrigins"`
46
+
47
+	// EtcdClientInfo contains information about how to connect to etcd
48
+	EtcdClientInfo RemoteConnectionInfo `json:"etcdClientInfo"`
49
+
50
+	// KubernetesMasterConfig, if present start the kubernetes master in this process
51
+	KubernetesMasterConfig *KubernetesMasterConfig `json:"kubernetesMasterConfig"`
52
+	// EtcdConfig, if present start etcd in this process
53
+	EtcdConfig *EtcdConfig `json:"etcdConfig"`
54
+	// OAuthConfig, if present start the /oauth endpoint in this process
55
+	OAuthConfig *OAuthConfig `json:"oauthConfig"`
56
+	// AssetConfig, if present start the asset serverin this process
57
+	AssetConfig *AssetConfig `json:"assetConfig"`
58
+	// DNSConfig, if present start the DNS server in this process
59
+	DNSConfig *DNSConfig `json:"dnsConfig"`
60
+
61
+	// MasterClients holds all the client connection information for controllers and other system components
62
+	MasterClients MasterClients `json:"masterClients"`
63
+
64
+	ImageConfig ImageConfig `json:"imageConfig"`
65
+
66
+	// MasterAuthorizationNamespace is the global namespace for Policy
67
+	MasterAuthorizationNamespace string `json:"masterAuthorizationNamespace"`
68
+	// OpenShiftSharedResourcesNamespace is the namespace where shared OpenShift resources live (like shared templates)
69
+	OpenShiftSharedResourcesNamespace string `json:"openshiftSharedResourcesNamespace"`
70
+}
71
+
72
+type ImageConfig struct {
73
+	Format string `json:"format"`
74
+	Latest bool   `json:"latest"`
75
+}
76
+
77
+type RemoteConnectionInfo struct {
78
+	// URL is the URL for etcd
79
+	URL string `json:"url"`
80
+	// CA is the CA for confirming that the server at the etcdURL is the actual server
81
+	CA string `json:"ca"`
82
+	// EtcdClientCertInfo is the TLS client cert information for securing communication to  etcd
83
+	// this is anonymous so that we can inline it for serialization
84
+	CertInfo `json:",inline"`
85
+}
86
+
87
+type ServingInfo struct {
88
+	// BindAddress is the ip:port to serve on
89
+	BindAddress string `json:"bindAddress"`
90
+	// ServerCert is the TLS cert info for serving secure traffic.
91
+	// this is anonymous so that we can inline it for serialization
92
+	CertInfo `json:",inline"`
93
+	// ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
94
+	ClientCA string `json:"clientCA"`
95
+}
96
+
97
+type MasterClients struct {
98
+	// DeployerKubeConfig is a .kubeconfig filename for depoyment pods to use
99
+	DeployerKubeConfig string `json:"deployerKubeConfig"`
100
+	// OpenShiftLoopbackKubeConfig is a .kubeconfig filename for system components to loopback to this master
101
+	OpenShiftLoopbackKubeConfig string `json:"openshiftLoopbackKubeConfig"`
102
+	// KubernetesKubeConfig is a .kubeconfig filename for system components to communicate to kubernetes for building the proxy
103
+	KubernetesKubeConfig string `json:"kubernetesKubeConfig"`
104
+}
105
+
106
+type DNSConfig struct {
107
+	// BindAddress is the ip:port to serve DNS on
108
+	BindAddress string `json:"bindAddress"`
109
+}
110
+
111
+type AssetConfig struct {
112
+	ServingInfo ServingInfo `json:"servingInfo"`
113
+
114
+	// PublicURL is where you can find the asset server (TODO do we really need this?)
115
+	PublicURL string `json:"publicURL"`
116
+
117
+	// LogoutURI is an optional, absolute URI to redirect web browsers to after logging out of the web console.
118
+	// If not specified, the built-in logout page is shown.
119
+	LogoutURI string `json:"logoutURI"`
120
+
121
+	// MasterPublicURL is how the web console can access the OpenShift api server
122
+	MasterPublicURL string `json:"masterPublicURL"`
123
+
124
+	// TODO: we probably don't need this since we have a proxy
125
+	// KubernetesPublicURL is how the web console can access the Kubernetes api server
126
+	KubernetesPublicURL string `json:"kubernetesPublicURL"`
127
+}
128
+
129
+type OAuthConfig struct {
130
+	// ProxyCA is the certificate bundle for confirming the identity of front proxy forwards to the oauth server
131
+	ProxyCA string `json:"proxyCA"`
132
+
133
+	// MasterURL is used for building valid client redirect URLs for external access
134
+	MasterURL string `json:"masterURL"`
135
+
136
+	// MasterPublicURL is used for building valid client redirect URLs for external access
137
+	MasterPublicURL string `json:"masterPublicURL"`
138
+
139
+	// AssetPublicURL is used for building valid client redirect URLs for external access
140
+	AssetPublicURL string `json:"assetPublicURL"`
141
+
142
+	// all the handlers here
143
+}
144
+
145
+type EtcdConfig struct {
146
+	ServingInfo ServingInfo `json:"servingInfo"`
147
+
148
+	PeerAddress   string `json:"peerAddress"`
149
+	MasterAddress string `json:"masterAddress"`
150
+	StorageDir    string `json:"storageDirectory"`
151
+}
152
+
153
+type KubernetesMasterConfig struct {
154
+	ServicesSubnet  string   `json:"servicesSubnet"`
155
+	StaticNodeNames []string `json:"staticNodeNames"`
156
+}
157
+
158
+type CertInfo struct {
159
+	CertFile string `json:"certFile"`
160
+	KeyFile  string `json:"keyFile"`
161
+}
... ...
@@ -16,6 +16,7 @@ const (
16 16
 	AuthenticatedGroup   = "system:authenticated"
17 17
 	UnauthenticatedGroup = "system:unauthenticated"
18 18
 	ClusterAdminGroup    = "system:cluster-admins"
19
+	NodesGroup           = "system:nodes"
19 20
 )
20 21
 
21 22
 const (
... ...
@@ -226,7 +227,8 @@ func GetBootstrapMasterRoleBindings(masterNamespace string) []authorizationapi.R
226 226
 				Name:      InternalComponentRoleName,
227 227
 				Namespace: masterNamespace,
228 228
 			},
229
-			Users: util.NewStringSet(InternalComponentUsername, InternalComponentKubeUsername),
229
+			Users:  util.NewStringSet(InternalComponentUsername, InternalComponentKubeUsername),
230
+			Groups: util.NewStringSet(NodesGroup),
230 231
 		},
231 232
 		{
232 233
 			ObjectMeta: kapi.ObjectMeta{
233 234
new file mode 100644
... ...
@@ -0,0 +1,182 @@
0
+package certs
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"path"
6
+	"path/filepath"
7
+
8
+	"github.com/golang/glog"
9
+	"github.com/spf13/cobra"
10
+
11
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
12
+)
13
+
14
+type CreateAllCertsOptions struct {
15
+	CertDir    string
16
+	SignerName string
17
+
18
+	Hostnames util.StringList
19
+	NodeList  util.StringList
20
+
21
+	APIServerURL       string
22
+	PublicAPIServerURL string
23
+
24
+	Overwrite bool
25
+}
26
+
27
+func NewCommandCreateAllCerts() *cobra.Command {
28
+	options := &CreateAllCertsOptions{}
29
+
30
+	cmd := &cobra.Command{
31
+		Use:   "create-all-certs",
32
+		Short: "Create all certificates for OpenShift All-In-One",
33
+		Run: func(c *cobra.Command, args []string) {
34
+			if err := options.Validate(args); err != nil {
35
+				fmt.Println(err.Error())
36
+				c.Help()
37
+				return
38
+			}
39
+
40
+			if err := options.CreateAllCerts(); err != nil {
41
+				glog.Fatal(err)
42
+			}
43
+		},
44
+	}
45
+
46
+	flags := cmd.Flags()
47
+
48
+	flags.StringVar(&options.CertDir, "cert-dir", "openshift.local.certificates", "The certificate data directory.")
49
+	flags.StringVar(&options.SignerName, "signer-name", DefaultSignerName(), "The name to use for the generated signer.")
50
+
51
+	flags.StringVar(&options.APIServerURL, "master", "https://localhost:8443", "The API server's URL.")
52
+	flags.StringVar(&options.PublicAPIServerURL, "public-master", "", "The API public facing server's URL (if applicable).")
53
+	flags.Var(&options.Hostnames, "hostnames", "Every hostname or IP you want server certs to be valid for. Comma delimited list")
54
+	flags.Var(&options.NodeList, "nodes", "The names of all static nodes you'd like to generate certificates for. Comma delimited list")
55
+	flags.BoolVar(&options.Overwrite, "overwrite", true, "Overwrite existing cert files if found.  If false, any existing file will be left as-is.")
56
+
57
+	return cmd
58
+}
59
+
60
+func (o CreateAllCertsOptions) Validate(args []string) error {
61
+	if len(args) != 0 {
62
+		return errors.New("no arguments are supported")
63
+	}
64
+	if len(o.Hostnames) == 0 {
65
+		return errors.New("at least one hostname must be provided")
66
+	}
67
+	if len(o.CertDir) == 0 {
68
+		return errors.New("cert-dir must be provided")
69
+	}
70
+	if len(o.SignerName) == 0 {
71
+		return errors.New("signer-name must be provided")
72
+	}
73
+	if len(o.APIServerURL) == 0 {
74
+		return errors.New("master must be provided")
75
+	}
76
+
77
+	return nil
78
+}
79
+
80
+func (o CreateAllCertsOptions) CreateAllCerts() error {
81
+	glog.V(2).Infof("Creating all certs with: %#v", o)
82
+
83
+	signerCertOptions := CreateSignerCertOptions{
84
+		CertFile:   DefaultCertFilename(o.CertDir, DefaultCADir),
85
+		KeyFile:    DefaultKeyFilename(o.CertDir, DefaultCADir),
86
+		SerialFile: DefaultSerialFilename(o.CertDir, DefaultCADir),
87
+		Name:       o.SignerName,
88
+		Overwrite:  o.Overwrite,
89
+	}
90
+	if _, err := signerCertOptions.CreateSignerCert(); err != nil {
91
+		return err
92
+	}
93
+	// once we've minted the signer, don't overwrite it
94
+	getSignerCertOptions := GetSignerCertOptions{
95
+		CertFile:   DefaultCertFilename(o.CertDir, DefaultCADir),
96
+		KeyFile:    DefaultKeyFilename(o.CertDir, DefaultCADir),
97
+		SerialFile: DefaultSerialFilename(o.CertDir, DefaultCADir),
98
+	}
99
+
100
+	for _, clientCertInfo := range DefaultClientCerts(o.CertDir) {
101
+		clientCertOptions := CreateClientCertOptions{
102
+			GetSignerCertOptions: &getSignerCertOptions,
103
+
104
+			CertFile: clientCertInfo.CertLocation.CertFile,
105
+			KeyFile:  clientCertInfo.CertLocation.KeyFile,
106
+
107
+			User:      clientCertInfo.User,
108
+			Groups:    util.StringList(clientCertInfo.Groups.List()),
109
+			Overwrite: o.Overwrite,
110
+		}
111
+		if _, err := clientCertOptions.CreateClientCert(); err != nil {
112
+			return err
113
+		}
114
+
115
+		createKubeConfigOptions := CreateKubeConfigOptions{
116
+			APIServerURL:       o.APIServerURL,
117
+			PublicAPIServerURL: o.PublicAPIServerURL,
118
+			APIServerCAFile:    getSignerCertOptions.CertFile,
119
+			ServerNick:         "master",
120
+
121
+			CertFile: clientCertInfo.CertLocation.CertFile,
122
+			KeyFile:  clientCertInfo.CertLocation.KeyFile,
123
+			UserNick: clientCertInfo.SubDir,
124
+
125
+			KubeConfigFile: path.Join(filepath.Dir(clientCertOptions.CertFile), ".kubeconfig"),
126
+		}
127
+		if _, err := createKubeConfigOptions.CreateKubeConfig(); err != nil {
128
+			return err
129
+		}
130
+	}
131
+
132
+	for _, nodeName := range o.NodeList {
133
+		username := "node-" + nodeName
134
+		nodeCertOptions := CreateNodeClientCertOptions{
135
+			GetSignerCertOptions: &getSignerCertOptions,
136
+
137
+			CertFile: DefaultCertFilename(o.CertDir, username),
138
+			KeyFile:  DefaultKeyFilename(o.CertDir, username),
139
+
140
+			NodeName:  nodeName,
141
+			Overwrite: o.Overwrite,
142
+		}
143
+		if _, err := nodeCertOptions.CreateNodeClientCert(); err != nil {
144
+			return err
145
+		}
146
+
147
+		createKubeConfigOptions := CreateKubeConfigOptions{
148
+			APIServerURL:       o.APIServerURL,
149
+			PublicAPIServerURL: o.PublicAPIServerURL,
150
+			APIServerCAFile:    getSignerCertOptions.CertFile,
151
+			ServerNick:         "master",
152
+
153
+			CertFile: nodeCertOptions.CertFile,
154
+			KeyFile:  nodeCertOptions.KeyFile,
155
+			UserNick: username,
156
+
157
+			KubeConfigFile: path.Join(filepath.Dir(nodeCertOptions.CertFile), ".kubeconfig"),
158
+		}
159
+		if _, err := createKubeConfigOptions.CreateKubeConfig(); err != nil {
160
+			return err
161
+		}
162
+	}
163
+
164
+	for _, serverCertInfo := range DefaultServerCerts(o.CertDir) {
165
+		serverCertOptions := CreateServerCertOptions{
166
+			GetSignerCertOptions: &getSignerCertOptions,
167
+
168
+			CertFile: serverCertInfo.CertFile,
169
+			KeyFile:  serverCertInfo.KeyFile,
170
+
171
+			Hostnames: o.Hostnames,
172
+			Overwrite: o.Overwrite,
173
+		}
174
+
175
+		if _, err := serverCertOptions.CreateServerCert(); err != nil {
176
+			return err
177
+		}
178
+	}
179
+
180
+	return nil
181
+}
0 182
new file mode 100644
... ...
@@ -0,0 +1,93 @@
0
+package certs
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+
6
+	"github.com/golang/glog"
7
+	"github.com/spf13/cobra"
8
+
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
11
+
12
+	"github.com/openshift/origin/pkg/cmd/server/crypto"
13
+)
14
+
15
+type CreateClientCertOptions struct {
16
+	GetSignerCertOptions *GetSignerCertOptions
17
+
18
+	CertFile string
19
+	KeyFile  string
20
+
21
+	User   string
22
+	Groups util.StringList
23
+
24
+	Overwrite bool
25
+}
26
+
27
+func NewCommandCreateClientCert() *cobra.Command {
28
+	options := &CreateClientCertOptions{GetSignerCertOptions: &GetSignerCertOptions{}}
29
+
30
+	cmd := &cobra.Command{
31
+		Use:   "create-client-cert",
32
+		Short: "Create client certificate",
33
+		Run: func(c *cobra.Command, args []string) {
34
+			if err := options.Validate(args); err != nil {
35
+				fmt.Println(err.Error())
36
+				c.Help()
37
+				return
38
+			}
39
+
40
+			if _, err := options.CreateClientCert(); err != nil {
41
+				fmt.Println(err.Error())
42
+				c.Help()
43
+				return
44
+			}
45
+		},
46
+	}
47
+
48
+	flags := cmd.Flags()
49
+	BindGetSignerCertOptions(options.GetSignerCertOptions, flags, "")
50
+
51
+	flags.StringVar(&options.CertFile, "cert", "openshift.local.certificates/user/cert.crt", "The certificate file.")
52
+	flags.StringVar(&options.KeyFile, "key", "openshift.local.certificates/user/key.key", "The key file.")
53
+
54
+	flags.StringVar(&options.User, "user", "", "The scope qualified username.")
55
+	flags.Var(&options.Groups, "groups", "The list of groups this user belongs to. Comma delimited list")
56
+	flags.BoolVar(&options.Overwrite, "overwrite", true, "Overwrite existing cert files if found.  If false, any existing file will be left as-is.")
57
+
58
+	return cmd
59
+}
60
+
61
+func (o CreateClientCertOptions) Validate(args []string) error {
62
+	if len(args) != 0 {
63
+		return errors.New("no arguments are supported")
64
+	}
65
+	if len(o.CertFile) == 0 {
66
+		return errors.New("cert must be provided")
67
+	}
68
+	if len(o.KeyFile) == 0 {
69
+		return errors.New("key must be provided")
70
+	}
71
+	if len(o.User) == 0 {
72
+		return errors.New("user must be provided")
73
+	}
74
+
75
+	return o.GetSignerCertOptions.Validate()
76
+}
77
+
78
+func (o CreateClientCertOptions) CreateClientCert() (*crypto.TLSCertificateConfig, error) {
79
+	glog.V(2).Infof("Createing a client cert with: %#v and %#v", o, o.GetSignerCertOptions)
80
+
81
+	signerCert, err := o.GetSignerCertOptions.GetSignerCert()
82
+	if err != nil {
83
+		return nil, err
84
+	}
85
+
86
+	userInfo := &user.DefaultInfo{Name: o.User, Groups: o.Groups}
87
+	if o.Overwrite {
88
+		return signerCert.MakeClientCertificate(o.CertFile, o.KeyFile, userInfo)
89
+	} else {
90
+		return signerCert.EnsureClientCertificate(o.CertFile, o.KeyFile, userInfo)
91
+	}
92
+}
0 93
new file mode 100644
... ...
@@ -0,0 +1,24 @@
0
+package certs
1
+
2
+import (
3
+	"github.com/spf13/cobra"
4
+)
5
+
6
+func NewCommandAdmin() *cobra.Command {
7
+	cmd := &cobra.Command{
8
+		Use:   "admin",
9
+		Short: "Admin commands",
10
+		Run: func(c *cobra.Command, args []string) {
11
+			c.Help()
12
+		},
13
+	}
14
+
15
+	cmd.AddCommand(NewCommandCreateKubeConfig())
16
+	cmd.AddCommand(NewCommandCreateAllCerts())
17
+	cmd.AddCommand(NewCommandCreateClientCert())
18
+	cmd.AddCommand(NewCommandCreateNodeClientCert())
19
+	cmd.AddCommand(NewCommandCreateServerCert())
20
+	cmd.AddCommand(NewCommandCreateSignerCert())
21
+
22
+	return cmd
23
+}
0 24
new file mode 100644
... ...
@@ -0,0 +1,162 @@
0
+package certs
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"io/ioutil"
6
+	"os"
7
+	"path/filepath"
8
+
9
+	"github.com/golang/glog"
10
+	"github.com/spf13/cobra"
11
+
12
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
13
+	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
14
+)
15
+
16
+type CreateKubeConfigOptions struct {
17
+	APIServerURL       string
18
+	PublicAPIServerURL string
19
+	APIServerCAFile    string
20
+	ServerNick         string
21
+
22
+	CertFile string
23
+	KeyFile  string
24
+	UserNick string
25
+
26
+	KubeConfigFile string
27
+}
28
+
29
+func NewCommandCreateKubeConfig() *cobra.Command {
30
+	options := &CreateKubeConfigOptions{}
31
+
32
+	cmd := &cobra.Command{
33
+		Use:   "create-kubeconfig",
34
+		Short: "Create a basic .kubeconfig file from client certs",
35
+		Long: `
36
+Create's a .kubeconfig file at <--kubeconfig> that looks like this:
37
+
38
+clusters:
39
+- cluster:
40
+    certificate-authority-data: <contents of --certificate-authority>
41
+    server: <--master>
42
+  name: <--cluster>
43
+- cluster:
44
+    certificate-authority-data: <contents of --certificate-authority>
45
+    server: <--public-master>
46
+  name: public-<--cluster>
47
+contexts:
48
+- context:
49
+    cluster: <--cluster>
50
+    user: <--user>
51
+  name: <--cluster>
52
+current-context: <--cluster>
53
+kind: Config
54
+users:
55
+- name: <--user>
56
+  user:
57
+    client-certificate-data: <contents of --client-certificate>
58
+    client-key-data: <contents of --client-key>
59
+`,
60
+		Run: func(c *cobra.Command, args []string) {
61
+			if err := options.Validate(args); err != nil {
62
+				fmt.Println(err.Error())
63
+				c.Help()
64
+				return
65
+			}
66
+
67
+			if _, err := options.CreateKubeConfig(); err != nil {
68
+				glog.Fatal(err)
69
+			}
70
+		},
71
+	}
72
+
73
+	flags := cmd.Flags()
74
+
75
+	flags.StringVar(&options.APIServerURL, "master", "https://localhost:8443", "The API server's URL.")
76
+	flags.StringVar(&options.PublicAPIServerURL, "public-master", "", "The API public facing server's URL (if applicable).")
77
+	flags.StringVar(&options.APIServerCAFile, "certificate-authority", "openshift.local.certificates/ca/cert.crt", "Path to the API server's CA file.")
78
+	flags.StringVar(&options.ServerNick, "cluster", "master", "Nick name for this server in .kubeconfig.")
79
+	flags.StringVar(&options.CertFile, "client-certificate", "openshift.local.certificates/admin/cert.crt", "The client cert file.")
80
+	flags.StringVar(&options.KeyFile, "client-key", "openshift.local.certificates/admin/key.key", "The client key file.")
81
+	flags.StringVar(&options.UserNick, "user", "user", "Nick name for this user in .kubeconfig.")
82
+	flags.StringVar(&options.KubeConfigFile, "kubeconfig", ".kubeconfig", "Path for the resulting .kubeconfig file.")
83
+
84
+	return cmd
85
+}
86
+
87
+func (o CreateKubeConfigOptions) Validate(args []string) error {
88
+	if len(args) != 0 {
89
+		return errors.New("no arguments are supported")
90
+	}
91
+	if len(o.KubeConfigFile) == 0 {
92
+		return errors.New("kubeconfig must be provided")
93
+	}
94
+	if len(o.ServerNick) == 0 {
95
+		return errors.New("cluster must be provided")
96
+	}
97
+	if len(o.UserNick) == 0 {
98
+		return errors.New("user-nick must be provided")
99
+	}
100
+
101
+	return nil
102
+}
103
+
104
+func (o CreateKubeConfigOptions) CreateKubeConfig() (*clientcmdapi.Config, error) {
105
+	glog.V(2).Infof("creating a .kubeconfig with: %#v", o)
106
+
107
+	caData, err := ioutil.ReadFile(o.APIServerCAFile)
108
+	if err != nil {
109
+		return nil, err
110
+	}
111
+	certData, err := ioutil.ReadFile(o.CertFile)
112
+	if err != nil {
113
+		return nil, err
114
+	}
115
+	keyData, err := ioutil.ReadFile(o.KeyFile)
116
+	if err != nil {
117
+		return nil, err
118
+	}
119
+
120
+	credentials := make(map[string]clientcmdapi.AuthInfo)
121
+	credentials[o.UserNick] = clientcmdapi.AuthInfo{
122
+		ClientCertificateData: certData,
123
+		ClientKeyData:         keyData,
124
+	}
125
+
126
+	clusters := make(map[string]clientcmdapi.Cluster)
127
+	clusters[o.ServerNick] = clientcmdapi.Cluster{
128
+		Server: o.APIServerURL,
129
+		CertificateAuthorityData: caData,
130
+	}
131
+
132
+	contexts := make(map[string]clientcmdapi.Context)
133
+	contexts[o.ServerNick] = clientcmdapi.Context{Cluster: o.ServerNick, AuthInfo: o.UserNick}
134
+
135
+	createPublic := len(o.PublicAPIServerURL) > 0
136
+	if createPublic {
137
+		publicNick := "public-" + o.ServerNick
138
+		clusters[publicNick] = clientcmdapi.Cluster{
139
+			Server: o.PublicAPIServerURL,
140
+			CertificateAuthorityData: caData,
141
+		}
142
+		contexts[publicNick] = clientcmdapi.Context{Cluster: o.ServerNick, AuthInfo: o.UserNick}
143
+	}
144
+
145
+	kubeConfig := &clientcmdapi.Config{
146
+		Clusters:       clusters,
147
+		AuthInfos:      credentials,
148
+		Contexts:       contexts,
149
+		CurrentContext: o.ServerNick,
150
+	}
151
+
152
+	// Ensure the parent dir exists
153
+	if err := os.MkdirAll(filepath.Dir(o.KubeConfigFile), os.FileMode(0755)); err != nil {
154
+		return nil, err
155
+	}
156
+	if err := clientcmd.WriteToFile(*kubeConfig, o.KubeConfigFile); err != nil {
157
+		return nil, err
158
+	}
159
+
160
+	return kubeConfig, nil
161
+}
0 162
new file mode 100644
... ...
@@ -0,0 +1,91 @@
0
+package certs
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+
6
+	"github.com/golang/glog"
7
+	"github.com/spf13/cobra"
8
+
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
10
+
11
+	"github.com/openshift/origin/pkg/cmd/server/crypto"
12
+)
13
+
14
+type CreateNodeClientCertOptions struct {
15
+	GetSignerCertOptions *GetSignerCertOptions
16
+
17
+	CertFile string
18
+	KeyFile  string
19
+
20
+	NodeName string
21
+
22
+	Overwrite bool
23
+}
24
+
25
+func NewCommandCreateNodeClientCert() *cobra.Command {
26
+	options := &CreateNodeClientCertOptions{GetSignerCertOptions: &GetSignerCertOptions{}}
27
+
28
+	cmd := &cobra.Command{
29
+		Use:   "create-node-cert",
30
+		Short: "Create node certificate",
31
+		Run: func(c *cobra.Command, args []string) {
32
+			if err := options.Validate(args); err != nil {
33
+				fmt.Println(err.Error())
34
+				c.Help()
35
+				return
36
+			}
37
+
38
+			if _, err := options.CreateNodeClientCert(); err != nil {
39
+				glog.Fatal(err)
40
+			}
41
+		},
42
+	}
43
+
44
+	flags := cmd.Flags()
45
+	BindGetSignerCertOptions(options.GetSignerCertOptions, flags, "")
46
+
47
+	flags.StringVar(&options.CertFile, "cert", "openshift.local.certificates/user/cert.crt", "The certificate file.")
48
+	flags.StringVar(&options.KeyFile, "key", "openshift.local.certificates/user/key.key", "The key file.")
49
+
50
+	flags.StringVar(&options.NodeName, "node-name", "", "The name of the node.")
51
+	flags.BoolVar(&options.Overwrite, "overwrite", true, "Overwrite existing cert files if found.  If false, any existing file will be left as-is.")
52
+
53
+	return cmd
54
+}
55
+
56
+func (o CreateNodeClientCertOptions) Validate(args []string) error {
57
+	if len(args) != 0 {
58
+		return errors.New("no arguments are supported")
59
+	}
60
+	if len(o.CertFile) == 0 {
61
+		return errors.New("cert must be provided")
62
+	}
63
+	if len(o.KeyFile) == 0 {
64
+		return errors.New("key must be provided")
65
+	}
66
+	if len(o.NodeName) == 0 {
67
+		return errors.New("node-name must be provided")
68
+	}
69
+
70
+	return o.GetSignerCertOptions.Validate()
71
+}
72
+
73
+func (o CreateNodeClientCertOptions) CreateNodeClientCert() (*crypto.TLSCertificateConfig, error) {
74
+	glog.V(2).Infof("Createing a node client cert with: %#v and %#v", o, o.GetSignerCertOptions)
75
+
76
+	username := "node-" + o.NodeName
77
+
78
+	nodeCertOptions := CreateClientCertOptions{
79
+		GetSignerCertOptions: o.GetSignerCertOptions,
80
+
81
+		CertFile: o.CertFile,
82
+		KeyFile:  o.KeyFile,
83
+
84
+		User:      "system:" + username,
85
+		Groups:    util.StringList([]string{"system:nodes"}),
86
+		Overwrite: o.Overwrite,
87
+	}
88
+
89
+	return nodeCertOptions.CreateClientCert()
90
+}
0 91
new file mode 100644
... ...
@@ -0,0 +1,86 @@
0
+package certs
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+
6
+	"github.com/golang/glog"
7
+	"github.com/spf13/cobra"
8
+
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
10
+
11
+	"github.com/openshift/origin/pkg/cmd/server/crypto"
12
+)
13
+
14
+type CreateServerCertOptions struct {
15
+	GetSignerCertOptions *GetSignerCertOptions
16
+
17
+	CertFile string
18
+	KeyFile  string
19
+
20
+	Hostnames util.StringList
21
+	Overwrite bool
22
+}
23
+
24
+func NewCommandCreateServerCert() *cobra.Command {
25
+	options := &CreateServerCertOptions{GetSignerCertOptions: &GetSignerCertOptions{}}
26
+
27
+	cmd := &cobra.Command{
28
+		Use:   "create-server-cert",
29
+		Short: "Create server certificate",
30
+		Run: func(c *cobra.Command, args []string) {
31
+			if err := options.Validate(args); err != nil {
32
+				fmt.Println(err.Error())
33
+				c.Help()
34
+				return
35
+			}
36
+
37
+			if _, err := options.CreateServerCert(); err != nil {
38
+				glog.Fatal(err)
39
+			}
40
+		},
41
+	}
42
+
43
+	flags := cmd.Flags()
44
+	BindGetSignerCertOptions(options.GetSignerCertOptions, flags, "signer-")
45
+
46
+	flags.StringVar(&options.CertFile, "cert", "openshift.local.certificates/user/cert.crt", "The certificate file.")
47
+	flags.StringVar(&options.KeyFile, "key", "openshift.local.certificates/user/key.key", "The key file.")
48
+
49
+	flags.Var(&options.Hostnames, "hostnames", "Every hostname or IP you want server certs to be valid for. Comma delimited list")
50
+	flags.BoolVar(&options.Overwrite, "overwrite", true, "Overwrite existing cert files if found.  If false, any existing file will be left as-is.")
51
+
52
+	return cmd
53
+}
54
+
55
+func (o CreateServerCertOptions) Validate(args []string) error {
56
+	if len(args) != 0 {
57
+		return errors.New("no arguments are supported")
58
+	}
59
+	if len(o.Hostnames) == 0 {
60
+		return errors.New("at least one hostname must be provided")
61
+	}
62
+	if len(o.CertFile) == 0 {
63
+		return errors.New("cert must be provided")
64
+	}
65
+	if len(o.KeyFile) == 0 {
66
+		return errors.New("key must be provided")
67
+	}
68
+
69
+	return o.GetSignerCertOptions.Validate()
70
+}
71
+
72
+func (o CreateServerCertOptions) CreateServerCert() (*crypto.TLSCertificateConfig, error) {
73
+	glog.V(2).Infof("Createing a server cert with: %#v", o)
74
+
75
+	signerCert, err := o.GetSignerCertOptions.GetSignerCert()
76
+	if err != nil {
77
+		return nil, err
78
+	}
79
+
80
+	if o.Overwrite {
81
+		return signerCert.MakeServerCert(o.CertFile, o.KeyFile, util.NewStringSet([]string(o.Hostnames)...))
82
+	} else {
83
+		return signerCert.EnsureServerCert(o.CertFile, o.KeyFile, util.NewStringSet([]string(o.Hostnames)...))
84
+	}
85
+}
0 86
new file mode 100644
... ...
@@ -0,0 +1,83 @@
0
+package certs
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+
6
+	"github.com/golang/glog"
7
+	"github.com/spf13/cobra"
8
+	"github.com/spf13/pflag"
9
+
10
+	"github.com/openshift/origin/pkg/cmd/server/crypto"
11
+)
12
+
13
+type CreateSignerCertOptions struct {
14
+	CertFile   string
15
+	KeyFile    string
16
+	SerialFile string
17
+	Name       string
18
+
19
+	Overwrite bool
20
+}
21
+
22
+func BindSignerCertOptions(options *CreateSignerCertOptions, flags *pflag.FlagSet, prefix string) {
23
+	flags.StringVar(&options.CertFile, prefix+"cert", "openshift.local.certificates/ca/cert.crt", "The certificate file.")
24
+	flags.StringVar(&options.KeyFile, prefix+"key", "openshift.local.certificates/ca/key.key", "The key file.")
25
+	flags.StringVar(&options.SerialFile, prefix+"serial", "openshift.local.certificates/ca/serial.txt", "The serial file that keeps track of how many certs have been signed.")
26
+	flags.StringVar(&options.Name, prefix+"name", DefaultSignerName(), "The name of the signer.")
27
+	flags.BoolVar(&options.Overwrite, prefix+"overwrite", options.Overwrite, "Overwrite existing cert files if found.  If false, any existing file will be left as-is.")
28
+}
29
+
30
+func NewCommandCreateSignerCert() *cobra.Command {
31
+	options := &CreateSignerCertOptions{Overwrite: true}
32
+
33
+	cmd := &cobra.Command{
34
+		Use:   "create-signer-cert",
35
+		Short: "Create signer certificate",
36
+		Run: func(c *cobra.Command, args []string) {
37
+			if err := options.Validate(args); err != nil {
38
+				fmt.Println(err.Error())
39
+				c.Help()
40
+				return
41
+			}
42
+
43
+			if _, err := options.CreateSignerCert(); err != nil {
44
+				glog.Fatal(err)
45
+			}
46
+		},
47
+	}
48
+
49
+	BindSignerCertOptions(options, cmd.Flags(), "")
50
+
51
+	return cmd
52
+}
53
+
54
+func (o CreateSignerCertOptions) Validate(args []string) error {
55
+	if len(args) != 0 {
56
+		return errors.New("no arguments are supported")
57
+	}
58
+	if len(o.CertFile) == 0 {
59
+		return errors.New("cert must be provided")
60
+	}
61
+	if len(o.KeyFile) == 0 {
62
+		return errors.New("key must be provided")
63
+	}
64
+	if len(o.SerialFile) == 0 {
65
+		return errors.New("serial must be provided")
66
+	}
67
+	if len(o.Name) == 0 {
68
+		return errors.New("name must be provided")
69
+	}
70
+
71
+	return nil
72
+}
73
+
74
+func (o CreateSignerCertOptions) CreateSignerCert() (*crypto.CA, error) {
75
+	glog.V(2).Infof("Createing a signer cert with: %#v", o)
76
+
77
+	if o.Overwrite {
78
+		return crypto.MakeCA(o.CertFile, o.KeyFile, o.SerialFile, o.Name)
79
+	} else {
80
+		return crypto.EnsureCA(o.CertFile, o.KeyFile, o.SerialFile, o.Name)
81
+	}
82
+}
0 83
new file mode 100644
... ...
@@ -0,0 +1,131 @@
0
+package certs
1
+
2
+import (
3
+	"fmt"
4
+	"path"
5
+	"time"
6
+
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
8
+
9
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
10
+)
11
+
12
+const (
13
+	DefaultCADir = "ca"
14
+)
15
+
16
+type ClientCertInfo struct {
17
+	CertLocation configapi.CertInfo
18
+	SubDir       string
19
+	User         string
20
+	Groups       util.StringSet
21
+}
22
+
23
+func DefaultSignerName() string {
24
+	return fmt.Sprintf("%s@%d", "openshift-signer", time.Now().Unix())
25
+}
26
+
27
+func DefaultRootCAFile(certDir string) string {
28
+	return DefaultCertFilename(certDir, DefaultCADir)
29
+}
30
+
31
+func DefaultClientCerts(certDir string) []ClientCertInfo {
32
+	return []ClientCertInfo{
33
+		DefaultDeployerClientCertInfo(certDir),
34
+		DefaultOpenshiftLoopbackClientCertInfo(certDir),
35
+		DefaultKubeClientClientCertInfo(certDir),
36
+		DefaultClusterAdminClientCertInfo(certDir),
37
+	}
38
+}
39
+
40
+func DefaultDeployerClientCertInfo(certDir string) ClientCertInfo {
41
+	return ClientCertInfo{
42
+		CertLocation: configapi.CertInfo{
43
+			CertFile: DefaultCertFilename(certDir, "openshift-deployer"),
44
+			KeyFile:  DefaultKeyFilename(certDir, "openshift-deployer"),
45
+		},
46
+		SubDir: "openshift-deployer",
47
+		User:   "system:openshift-deployer",
48
+		Groups: util.NewStringSet("system:deployers"),
49
+	}
50
+}
51
+
52
+func DefaultOpenshiftLoopbackClientCertInfo(certDir string) ClientCertInfo {
53
+	return ClientCertInfo{
54
+		CertLocation: configapi.CertInfo{
55
+			CertFile: DefaultCertFilename(certDir, "openshift-client"),
56
+			KeyFile:  DefaultKeyFilename(certDir, "openshift-client"),
57
+		},
58
+		SubDir: "openshift-client",
59
+		User:   "system:openshift-client",
60
+	}
61
+}
62
+
63
+func DefaultKubeClientClientCertInfo(certDir string) ClientCertInfo {
64
+	return ClientCertInfo{
65
+		CertLocation: configapi.CertInfo{
66
+			CertFile: DefaultCertFilename(certDir, "kube-client"),
67
+			KeyFile:  DefaultKeyFilename(certDir, "kube-client"),
68
+		},
69
+		SubDir: "kube-client",
70
+		User:   "system:kube-client",
71
+	}
72
+}
73
+
74
+func DefaultClusterAdminClientCertInfo(certDir string) ClientCertInfo {
75
+	return ClientCertInfo{
76
+		CertLocation: configapi.CertInfo{
77
+			CertFile: DefaultCertFilename(certDir, "admin"),
78
+			KeyFile:  DefaultKeyFilename(certDir, "admin"),
79
+		},
80
+		SubDir: "admin",
81
+		User:   "system:admin",
82
+		Groups: util.NewStringSet("system:cluster-admins"),
83
+	}
84
+}
85
+
86
+func DefaultServerCerts(certDir string) []configapi.CertInfo {
87
+	return []configapi.CertInfo{
88
+		DefaultMasterServingCertInfo(certDir),
89
+		DefaultAssetServingCertInfo(certDir),
90
+	}
91
+}
92
+
93
+func DefaultMasterServingCertInfo(certDir string) configapi.CertInfo {
94
+	return configapi.CertInfo{
95
+		CertFile: DefaultCertFilename(certDir, "master"),
96
+		KeyFile:  DefaultKeyFilename(certDir, "master"),
97
+	}
98
+}
99
+
100
+func DefaultNodeServingCertInfo(certDir, nodeName string) configapi.CertInfo {
101
+	return configapi.CertInfo{
102
+		CertFile: DefaultCertFilename(certDir, "node_serving-"+nodeName),
103
+		KeyFile:  DefaultKeyFilename(certDir, "node_serving-"+nodeName),
104
+	}
105
+}
106
+
107
+func DefaultAssetServingCertInfo(certDir string) configapi.CertInfo {
108
+	return configapi.CertInfo{
109
+		CertFile: DefaultCertFilename(certDir, "master"),
110
+		KeyFile:  DefaultKeyFilename(certDir, "master"),
111
+	}
112
+}
113
+
114
+func DefaultCertDir(certDir, username string) string {
115
+	return path.Join(certDir, username)
116
+}
117
+
118
+func DefaultCertFilename(certDir, username string) string {
119
+	return path.Join(DefaultCertDir(certDir, username), "cert.crt")
120
+}
121
+
122
+func DefaultKeyFilename(certDir, username string) string {
123
+	return path.Join(DefaultCertDir(certDir, username), "key.key")
124
+}
125
+func DefaultSerialFilename(certDir, username string) string {
126
+	return path.Join(DefaultCertDir(certDir, username), "serial.txt")
127
+}
128
+func DefaultKubeConfigFilename(certDir, username string) string {
129
+	return path.Join(DefaultCertDir(certDir, username), ".kubeconfig")
130
+}
0 131
new file mode 100644
... ...
@@ -0,0 +1,39 @@
0
+package certs
1
+
2
+import (
3
+	"errors"
4
+
5
+	"github.com/spf13/pflag"
6
+
7
+	"github.com/openshift/origin/pkg/cmd/server/crypto"
8
+)
9
+
10
+type GetSignerCertOptions struct {
11
+	CertFile   string
12
+	KeyFile    string
13
+	SerialFile string
14
+}
15
+
16
+func BindGetSignerCertOptions(options *GetSignerCertOptions, flags *pflag.FlagSet, prefix string) {
17
+	flags.StringVar(&options.CertFile, prefix+"signer-cert", "openshift.local.certificates/ca/cert.crt", "The certificate file.")
18
+	flags.StringVar(&options.KeyFile, prefix+"signer-key", "openshift.local.certificates/ca/key.key", "The key file.")
19
+	flags.StringVar(&options.SerialFile, prefix+"signer-serial", "openshift.local.certificates/ca/serial.txt", "The serial file that keeps track of how many certs have been signed.")
20
+}
21
+
22
+func (o GetSignerCertOptions) Validate() error {
23
+	if len(o.CertFile) == 0 {
24
+		return errors.New("signer-cert must be provided")
25
+	}
26
+	if len(o.KeyFile) == 0 {
27
+		return errors.New("signer-key must be provided")
28
+	}
29
+	if len(o.SerialFile) == 0 {
30
+		return errors.New("signer-serial must be provided")
31
+	}
32
+
33
+	return nil
34
+}
35
+
36
+func (o GetSignerCertOptions) GetSignerCert() (*crypto.CA, error) {
37
+	return crypto.GetCA(o.CertFile, o.KeyFile, o.SerialFile)
38
+}
0 39
deleted file mode 100644
... ...
@@ -1,152 +0,0 @@
1
-package server
2
-
3
-import (
4
-	"errors"
5
-	"fmt"
6
-	"net"
7
-	_ "net/http/pprof"
8
-	"strings"
9
-
10
-	"github.com/golang/glog"
11
-	"github.com/spf13/cobra"
12
-
13
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
14
-)
15
-
16
-const longCommandDesc = `
17
-Start an OpenShift server
18
-
19
-This command helps you launch an OpenShift server. The default mode is all-in-one, which allows
20
-you to run all of the components of an OpenShift system on a server with Docker. Running
21
-
22
-    $ openshift start
23
-
24
-will start OpenShift listening on all interfaces, launch an etcd server to store persistent
25
-data, and launch the Kubernetes system components. The server will run in the foreground until
26
-you terminate the process.
27
-
28
-Note: starting OpenShift without passing the --master address will attempt to find the IP
29
-address that will be visible inside running Docker containers. This is not always successful,
30
-so if you have problems tell OpenShift what public address it will be via --master=<ip>.
31
-
32
-You may also pass an optional argument to the start command to start OpenShift in one of the
33
-following roles:
34
-
35
-    $ openshift start master --nodes=<host1,host2,host3,...>
36
-
37
-      Launches the server and control plane for OpenShift. You may pass a list of the node
38
-      hostnames you want to use, or create nodes via the REST API or 'openshift kube'.
39
-
40
-    $ openshift start node --master=<masterIP>
41
-
42
-      Launches a new node and attempts to connect to the master on the provided IP.
43
-
44
-You may also pass --etcd=<address> to connect to an external etcd server instead of running an
45
-integrated instance, or --kubernetes=<addr> and --kubeconfig=<path> to connect to an existing
46
-Kubernetes cluster.
47
-`
48
-
49
-// NewCommandStartServer provides a CLI handler for 'start' command
50
-func NewCommandStartServer(name string) (*cobra.Command, *Config) {
51
-	cfg := NewDefaultConfig()
52
-
53
-	cmd := &cobra.Command{
54
-		Use:   fmt.Sprintf("%s [master|node]", name),
55
-		Short: "Launch OpenShift",
56
-		Long:  longCommandDesc,
57
-		Run: func(c *cobra.Command, args []string) {
58
-			if err := cfg.Validate(args); err != nil {
59
-				glog.Fatal(err)
60
-			}
61
-
62
-			cfg.Complete(args)
63
-
64
-			if err := cfg.Start(args); err != nil {
65
-				glog.Fatal(err)
66
-			}
67
-		},
68
-	}
69
-
70
-	flag := cmd.Flags()
71
-
72
-	flag.BoolVar(&cfg.WriteConfigOnly, "config-only", false, "Indicates that the command should build the config that would be used to start OpenShift and do nothing else. This is not yet implemented.")
73
-
74
-	flag.Var(&cfg.BindAddr, "listen", "The address to listen for connections on (host, host:port, or URL).")
75
-	flag.Var(&cfg.MasterAddr, "master", "The master address for use by OpenShift components (host, host:port, or URL). Scheme and port default to the --listen scheme and port.")
76
-	flag.Var(&cfg.MasterPublicAddr, "public-master", "The master address for use by public clients, if different (host, host:port, or URL). Defaults to same as --master.")
77
-	flag.Var(&cfg.EtcdAddr, "etcd", "The address of the etcd server (host, host:port, or URL). If specified, no built-in etcd will be started.")
78
-	flag.Var(&cfg.KubernetesAddr, "kubernetes", "The address of the Kubernetes server (host, host:port, or URL). If specified, no Kubernetes components will be started.")
79
-	flag.Var(&cfg.KubernetesPublicAddr, "public-kubernetes", "The Kubernetes server address for use by public clients, if different. (host, host:port, or URL). Defaults to same as --kubernetes.")
80
-	flag.Var(&cfg.PortalNet, "portal-net", "A CIDR notation IP range from which to assign portal IPs. This must not overlap with any IP ranges assigned to nodes for pods.")
81
-
82
-	flag.StringVar(&cfg.ImageTemplate.Format, "images", cfg.ImageTemplate.Format, "When fetching images used by the cluster for important components, use this format on both master and nodes. The latest release will be used by default.")
83
-	flag.BoolVar(&cfg.ImageTemplate.Latest, "latest-images", cfg.ImageTemplate.Latest, "If true, attempt to use the latest images for the cluster instead of the latest release.")
84
-
85
-	flag.StringVar(&cfg.VolumeDir, "volume-dir", "openshift.local.volumes", "The volume storage directory.")
86
-	flag.StringVar(&cfg.EtcdDir, "etcd-dir", "openshift.local.etcd", "The etcd data directory.")
87
-	flag.StringVar(&cfg.CertDir, "cert-dir", "openshift.local.certificates", "The certificate data directory.")
88
-
89
-	flag.StringVar(&cfg.Hostname, "hostname", cfg.Hostname, "The hostname to identify this node with the master.")
90
-	flag.Var(&cfg.NodeList, "nodes", "The hostnames of each node. This currently must be specified up front. Comma delimited list")
91
-	flag.Var(&cfg.CORSAllowedOrigins, "cors-allowed-origins", "List of allowed origins for CORS, comma separated.  An allowed origin can be a regular expression to support subdomain matching.  CORS is enabled for localhost, 127.0.0.1, and the asset server by default.")
92
-
93
-	flag.StringVar(&cfg.ClientConfigLoadingRules.CommandLinePath, "kubeconfig", "", "Path to the kubeconfig file to use for requests to the Kubernetes API.")
94
-
95
-	cfg.Docker.InstallFlags(flag)
96
-
97
-	return cmd, cfg
98
-}
99
-
100
-const startMaster = "master"
101
-const startNode = "node"
102
-
103
-func (cfg Config) Validate(args []string) error {
104
-	switch len(args) {
105
-	case 1:
106
-		switch args[0] {
107
-		case startMaster: // allowed case
108
-		case startNode: // allowed case
109
-		default:
110
-			return errors.New("You may start an OpenShift all-in-one server with no arguments, or pass 'master' or 'node' to run in that role.")
111
-		}
112
-	case 0:
113
-		// do nothing, this starts an all in one
114
-
115
-	default:
116
-		return errors.New("You may start an OpenShift all-in-one server with no arguments, or pass 'master' or 'node' to run in that role.")
117
-	}
118
-
119
-	return nil
120
-}
121
-
122
-// Complete takes the args and fills in information for the start config
123
-func (cfg *Config) Complete(args []string) {
124
-	cfg.StartMaster = (len(args) == 0) || (args[0] == startMaster)
125
-	cfg.StartNode = (len(args) == 0) || (args[0] == startNode)
126
-
127
-	if cfg.StartMaster {
128
-		// if we've explicitly called out a kube server or a client config, don't start kube in-process
129
-		cfg.StartKube = !cfg.KubernetesAddr.Provided && len(cfg.ClientConfigLoadingRules.CommandLinePath) == 0
130
-		// if we've explicitly called out an etcd server, don't start etcd in-process
131
-		cfg.StartEtcd = !cfg.EtcdAddr.Provided
132
-	}
133
-
134
-	// if this is an all-in-one start, be sure to add our hostname to the NodeList if it is not already present
135
-	isAllInOne := (len(args) == 0)
136
-	if isAllInOne {
137
-		nodeList := util.NewStringSet(strings.ToLower(cfg.Hostname))
138
-		// take everything toLower
139
-		for _, s := range cfg.NodeList {
140
-			nodeList.Insert(strings.ToLower(s))
141
-		}
142
-
143
-		cfg.NodeList = nodeList.List()
144
-
145
-		// in the all-in-one, default ClusterDNS to the master's address
146
-		if url, err := cfg.GetMasterAddress(); err == nil {
147
-			if host, _, err := net.SplitHostPort(url.Host); err == nil {
148
-				cfg.ClusterDNS = net.ParseIP(host)
149
-			}
150
-		}
151
-	}
152
-}
153 1
deleted file mode 100644
... ...
@@ -1,364 +0,0 @@
1
-package server
2
-
3
-import (
4
-	"strconv"
5
-	"strings"
6
-	"testing"
7
-
8
-	"github.com/spf13/cobra"
9
-
10
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
11
-)
12
-
13
-func TestCommandBindingListen(t *testing.T) {
14
-	valueToSet := "http://example.org:9123"
15
-	actualCfg := executeCommand([]string{"--listen=" + valueToSet})
16
-
17
-	expectedConfig := NewDefaultConfig()
18
-	expectedConfig.BindAddr.Set(valueToSet)
19
-
20
-	if expectedConfig.BindAddr.String() != actualCfg.BindAddr.String() {
21
-		t.Errorf("expected %v, got %v", expectedConfig.BindAddr.String(), actualCfg.BindAddr.String())
22
-	}
23
-}
24
-
25
-func TestCommandBindingMaster(t *testing.T) {
26
-	valueToSet := "http://example.org:9123"
27
-	actualCfg := executeCommand([]string{"--master=" + valueToSet})
28
-
29
-	expectedConfig := NewDefaultConfig()
30
-	expectedConfig.MasterAddr.Set(valueToSet)
31
-
32
-	if expectedConfig.MasterAddr.String() != actualCfg.MasterAddr.String() {
33
-		t.Errorf("expected %v, got %v", expectedConfig.MasterAddr.String(), actualCfg.MasterAddr.String())
34
-	}
35
-}
36
-
37
-func TestCommandBindingMasterPublic(t *testing.T) {
38
-	valueToSet := "http://example.org:9123"
39
-	actualCfg := executeCommand([]string{"--public-master=" + valueToSet})
40
-
41
-	expectedConfig := NewDefaultConfig()
42
-	expectedConfig.MasterPublicAddr.Set(valueToSet)
43
-
44
-	if expectedConfig.MasterPublicAddr.String() != actualCfg.MasterPublicAddr.String() {
45
-		t.Errorf("expected %v, got %v", expectedConfig.MasterPublicAddr.String(), actualCfg.MasterPublicAddr.String())
46
-	}
47
-}
48
-
49
-func TestCommandBindingEtcd(t *testing.T) {
50
-	valueToSet := "http://example.org:9123"
51
-	actualCfg := executeCommand([]string{"--etcd=" + valueToSet})
52
-
53
-	expectedConfig := NewDefaultConfig()
54
-	expectedConfig.EtcdAddr.Set(valueToSet)
55
-
56
-	if expectedConfig.EtcdAddr.String() != actualCfg.EtcdAddr.String() {
57
-		t.Errorf("expected %v, got %v", expectedConfig.EtcdAddr.String(), actualCfg.EtcdAddr.String())
58
-	}
59
-}
60
-
61
-func TestCommandBindingKubernetes(t *testing.T) {
62
-	valueToSet := "http://example.org:9123"
63
-	actualCfg := executeCommand([]string{"--kubernetes=" + valueToSet})
64
-
65
-	expectedConfig := NewDefaultConfig()
66
-	expectedConfig.KubernetesAddr.Set(valueToSet)
67
-
68
-	if expectedConfig.KubernetesAddr.String() != actualCfg.KubernetesAddr.String() {
69
-		t.Errorf("expected %v, got %v", expectedConfig.KubernetesAddr.String(), actualCfg.KubernetesAddr.String())
70
-	}
71
-}
72
-
73
-func TestCommandBindingKubernetesPublic(t *testing.T) {
74
-	valueToSet := "http://example.org:9123"
75
-	actualCfg := executeCommand([]string{"--public-kubernetes=" + valueToSet})
76
-
77
-	expectedConfig := NewDefaultConfig()
78
-	expectedConfig.KubernetesPublicAddr.Set(valueToSet)
79
-
80
-	if expectedConfig.KubernetesPublicAddr.String() != actualCfg.KubernetesPublicAddr.String() {
81
-		t.Errorf("expected %v, got %v", expectedConfig.KubernetesPublicAddr.String(), actualCfg.KubernetesPublicAddr.String())
82
-	}
83
-}
84
-
85
-func TestCommandBindingPortalNet(t *testing.T) {
86
-	valueToSet := "192.168.0.0/16"
87
-	actualCfg := executeCommand([]string{"--portal-net=" + valueToSet})
88
-
89
-	expectedConfig := NewDefaultConfig()
90
-	expectedConfig.PortalNet.Set(valueToSet)
91
-
92
-	if expectedConfig.PortalNet.String() != actualCfg.PortalNet.String() {
93
-		t.Errorf("expected %v, got %v", expectedConfig.PortalNet.String(), actualCfg.PortalNet.String())
94
-	}
95
-}
96
-
97
-func TestCommandBindingImageTemplateFormat(t *testing.T) {
98
-	valueToSet := "some-format-string"
99
-	actualCfg := executeCommand([]string{"--images=" + valueToSet})
100
-
101
-	expectedConfig := NewDefaultConfig()
102
-	expectedConfig.ImageTemplate.Format = valueToSet
103
-
104
-	if expectedConfig.ImageTemplate.Format != actualCfg.ImageTemplate.Format {
105
-		t.Errorf("expected %v, got %v", expectedConfig.ImageTemplate.Format, actualCfg.ImageTemplate.Format)
106
-	}
107
-}
108
-
109
-func TestCommandBindingImageLatest(t *testing.T) {
110
-	expectedConfig := NewDefaultConfig()
111
-
112
-	valueToSet := strconv.FormatBool(!expectedConfig.ImageTemplate.Latest)
113
-	actualCfg := executeCommand([]string{"--latest-images=" + valueToSet})
114
-
115
-	expectedConfig.ImageTemplate.Latest = !expectedConfig.ImageTemplate.Latest
116
-
117
-	if expectedConfig.ImageTemplate.Latest != actualCfg.ImageTemplate.Latest {
118
-		t.Errorf("expected %v, got %v", expectedConfig.ImageTemplate.Latest, actualCfg.ImageTemplate.Latest)
119
-	}
120
-}
121
-
122
-func TestCommandBindingVolumeDir(t *testing.T) {
123
-	valueToSet := "some-string"
124
-	actualCfg := executeCommand([]string{"--volume-dir=" + valueToSet})
125
-
126
-	expectedConfig := NewDefaultConfig()
127
-	expectedConfig.VolumeDir = valueToSet
128
-
129
-	if expectedConfig.VolumeDir != actualCfg.VolumeDir {
130
-		t.Errorf("expected %v, got %v", expectedConfig.VolumeDir, actualCfg.VolumeDir)
131
-	}
132
-}
133
-
134
-func TestCommandBindingEtcdDir(t *testing.T) {
135
-	valueToSet := "some-string"
136
-	actualCfg := executeCommand([]string{"--etcd-dir=" + valueToSet})
137
-
138
-	expectedConfig := NewDefaultConfig()
139
-	expectedConfig.EtcdDir = valueToSet
140
-
141
-	if expectedConfig.EtcdDir != actualCfg.EtcdDir {
142
-		t.Errorf("expected %v, got %v", expectedConfig.EtcdDir, actualCfg.EtcdDir)
143
-	}
144
-}
145
-
146
-func TestCommandBindingCertDir(t *testing.T) {
147
-	valueToSet := "some-string"
148
-	actualCfg := executeCommand([]string{"--cert-dir=" + valueToSet})
149
-
150
-	expectedConfig := NewDefaultConfig()
151
-	expectedConfig.CertDir = valueToSet
152
-
153
-	if expectedConfig.CertDir != actualCfg.CertDir {
154
-		t.Errorf("expected %v, got %v", expectedConfig.CertDir, actualCfg.CertDir)
155
-	}
156
-}
157
-
158
-func TestCommandBindingHostname(t *testing.T) {
159
-	valueToSet := "some-string"
160
-	actualCfg := executeCommand([]string{"--hostname=" + valueToSet})
161
-
162
-	expectedConfig := NewDefaultConfig()
163
-	expectedConfig.Hostname = valueToSet
164
-
165
-	if expectedConfig.Hostname != actualCfg.Hostname {
166
-		t.Errorf("expected %v, got %v", expectedConfig.Hostname, actualCfg.Hostname)
167
-	}
168
-}
169
-
170
-// AllInOne always adds the default hostname
171
-func TestCommandBindingNodesForAllInOneAppend(t *testing.T) {
172
-	valueToSet := "first,second,third"
173
-	actualCfg := executeCommand([]string{"--nodes=" + valueToSet})
174
-
175
-	expectedConfig := NewDefaultConfig()
176
-
177
-	stringList := util.StringList{}
178
-	stringList.Set(valueToSet + "," + strings.ToLower(expectedConfig.Hostname))
179
-	expectedConfig.NodeList.Set(strings.Join(util.NewStringSet(stringList...).List(), ","))
180
-
181
-	if expectedConfig.NodeList.String() != actualCfg.NodeList.String() {
182
-		t.Errorf("expected %v, got %v", expectedConfig.NodeList, actualCfg.NodeList)
183
-	}
184
-}
185
-
186
-// AllInOne always adds the default hostname
187
-func TestCommandBindingNodesForAllInOneAppendNoDupes(t *testing.T) {
188
-
189
-	valueToSet := "first,localhost,second,third"
190
-	actualCfg := executeCommand([]string{"--nodes=" + valueToSet, "--hostname=LOCALHOST"})
191
-
192
-	expectedConfig := NewDefaultConfig()
193
-	expectedConfig.NodeList.Set(valueToSet)
194
-
195
-	util.NewStringSet()
196
-
197
-	if expectedConfig.NodeList.String() != actualCfg.NodeList.String() {
198
-		t.Errorf("expected %v, got %v", expectedConfig.NodeList, actualCfg.NodeList)
199
-	}
200
-}
201
-
202
-// AllInOne always adds the default hostname
203
-func TestCommandBindingNodesDefaultingAllInOne(t *testing.T) {
204
-	actualCfg := executeCommand([]string{})
205
-
206
-	expectedConfig := NewDefaultConfig()
207
-	expectedConfig.NodeList.Set(strings.ToLower(expectedConfig.Hostname))
208
-
209
-	if expectedConfig.NodeList.String() != actualCfg.NodeList.String() {
210
-		t.Errorf("expected %v, got %v", expectedConfig.NodeList, actualCfg.NodeList)
211
-	}
212
-}
213
-
214
-// explicit start master never modifies the NodeList
215
-func TestCommandBindingNodesForMaster(t *testing.T) {
216
-	valueToSet := "first,second,third"
217
-	actualCfg := executeCommand([]string{"master", "--nodes=" + valueToSet})
218
-
219
-	expectedConfig := NewDefaultConfig()
220
-	expectedConfig.NodeList.Set(valueToSet)
221
-
222
-	if expectedConfig.NodeList.String() != actualCfg.NodeList.String() {
223
-		t.Errorf("expected %v, got %v", expectedConfig.NodeList, actualCfg.NodeList)
224
-	}
225
-}
226
-
227
-// explicit start master never modifies the NodeList
228
-func TestCommandBindingNodesDefaultingMaster(t *testing.T) {
229
-	actualCfg := executeCommand([]string{"master"})
230
-
231
-	expectedConfig := NewDefaultConfig()
232
-	expectedConfig.NodeList.Set("")
233
-
234
-	if expectedConfig.NodeList.String() != actualCfg.NodeList.String() {
235
-		t.Errorf("expected %v, got %v", expectedConfig.NodeList, actualCfg.NodeList)
236
-	}
237
-}
238
-
239
-func TestCommandBindingCors(t *testing.T) {
240
-	valueToSet := "first,second,third"
241
-	actualCfg := executeCommand([]string{"--cors-allowed-origins=" + valueToSet})
242
-
243
-	expectedConfig := NewDefaultConfig()
244
-	expectedConfig.CORSAllowedOrigins.Set(valueToSet)
245
-
246
-	if expectedConfig.CORSAllowedOrigins.String() != actualCfg.CORSAllowedOrigins.String() {
247
-		t.Errorf("expected %v, got %v", expectedConfig.CORSAllowedOrigins, actualCfg.CORSAllowedOrigins)
248
-	}
249
-}
250
-
251
-func TestCommandCompletionNode(t *testing.T) {
252
-	commandCompletionTest{
253
-		args: []string{"node"},
254
-
255
-		StartNode: true,
256
-	}.run(t)
257
-}
258
-
259
-func TestCommandCompletionMaster(t *testing.T) {
260
-	commandCompletionTest{
261
-		args: []string{"master"},
262
-
263
-		StartMaster: true,
264
-		StartKube:   true,
265
-		StartEtcd:   true,
266
-	}.run(t)
267
-}
268
-func TestCommandCompletionMasterExternalKubernetes(t *testing.T) {
269
-	commandCompletionTest{
270
-		args: []string{"master", "--kubernetes=foo"},
271
-
272
-		StartMaster: true,
273
-		StartKube:   false,
274
-		StartEtcd:   true,
275
-	}.run(t)
276
-}
277
-func TestCommandCompletionMasterExternalKubernetesConfig(t *testing.T) {
278
-	commandCompletionTest{
279
-		args: []string{"master", "--kubeconfig=foo"},
280
-
281
-		StartMaster: true,
282
-		StartKube:   false,
283
-		StartEtcd:   true,
284
-	}.run(t)
285
-}
286
-
287
-func TestCommandCompletionAllInOne(t *testing.T) {
288
-	commandCompletionTest{
289
-		StartNode:   true,
290
-		StartMaster: true,
291
-		StartKube:   true,
292
-		StartEtcd:   true,
293
-	}.run(t)
294
-}
295
-func TestCommandCompletionAllInOneExternalKubernetes(t *testing.T) {
296
-	commandCompletionTest{
297
-		args: []string{"--kubernetes=foo"},
298
-
299
-		StartNode:   true,
300
-		StartMaster: true,
301
-		StartKube:   false,
302
-		StartEtcd:   true,
303
-	}.run(t)
304
-}
305
-func TestCommandCompletionAllInOneExternalKubernetesConfig(t *testing.T) {
306
-	commandCompletionTest{
307
-		args: []string{"--kubeconfig=foo"},
308
-
309
-		StartNode:   true,
310
-		StartMaster: true,
311
-		StartKube:   false,
312
-		StartEtcd:   true,
313
-	}.run(t)
314
-}
315
-
316
-type commandCompletionTest struct {
317
-	args []string
318
-
319
-	StartNode   bool
320
-	StartMaster bool
321
-	StartKube   bool
322
-	StartEtcd   bool
323
-}
324
-
325
-func executeCommand(args []string) *Config {
326
-	argsToUse := make([]string, 0, 1+len(args))
327
-	argsToUse = append(argsToUse, "start")
328
-	argsToUse = append(argsToUse, args...)
329
-	argsToUse = append(argsToUse, "--config-only")
330
-
331
-	root := &cobra.Command{
332
-		Use:   "openshift",
333
-		Short: "test",
334
-		Long:  "",
335
-		Run: func(c *cobra.Command, args []string) {
336
-			c.Help()
337
-		},
338
-	}
339
-
340
-	openshiftStartCommand, cfg := NewCommandStartServer("start")
341
-	root.AddCommand(openshiftStartCommand)
342
-	root.SetArgs(argsToUse)
343
-	root.Execute()
344
-
345
-	return cfg
346
-}
347
-
348
-func (test commandCompletionTest) run(t *testing.T) {
349
-	actualCfg := executeCommand(test.args)
350
-
351
-	if test.StartNode != actualCfg.StartNode {
352
-		t.Errorf("expected %v, got %v", test.StartNode, actualCfg.StartNode)
353
-	}
354
-	if test.StartMaster != actualCfg.StartMaster {
355
-		t.Errorf("expected %v, got %v", test.StartMaster, actualCfg.StartMaster)
356
-	}
357
-	if test.StartKube != actualCfg.StartKube {
358
-		t.Errorf("expected %v, got %v", test.StartKube, actualCfg.StartKube)
359
-	}
360
-	if test.StartEtcd != actualCfg.StartEtcd {
361
-		t.Errorf("expected %v, got %v", test.StartEtcd, actualCfg.StartEtcd)
362
-	}
363
-
364
-}
365 1
deleted file mode 100644
... ...
@@ -1,303 +0,0 @@
1
-package server
2
-
3
-import (
4
-	"fmt"
5
-	"net"
6
-	"net/url"
7
-	"os/exec"
8
-	"strconv"
9
-	"strings"
10
-	"time"
11
-
12
-	etcdclient "github.com/coreos/go-etcd/etcd"
13
-	"github.com/golang/glog"
14
-
15
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
16
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
17
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
18
-	kutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
19
-
20
-	"github.com/openshift/origin/pkg/api/latest"
21
-	"github.com/openshift/origin/pkg/cmd/flagtypes"
22
-	"github.com/openshift/origin/pkg/cmd/util"
23
-	"github.com/openshift/origin/pkg/cmd/util/docker"
24
-	"github.com/openshift/origin/pkg/cmd/util/variable"
25
-)
26
-
27
-// Config is a struct that the command stores flag values into.
28
-type Config struct {
29
-	Docker *docker.Helper
30
-
31
-	WriteConfigOnly bool
32
-
33
-	StartNode   bool
34
-	StartMaster bool
35
-	StartKube   bool
36
-	StartEtcd   bool
37
-
38
-	MasterAddr     flagtypes.Addr
39
-	BindAddr       flagtypes.Addr
40
-	EtcdAddr       flagtypes.Addr
41
-	KubernetesAddr flagtypes.Addr
42
-	PortalNet      flagtypes.IPNet
43
-	DNSBindAddr    flagtypes.Addr
44
-	// addresses for external clients
45
-	MasterPublicAddr     flagtypes.Addr
46
-	KubernetesPublicAddr flagtypes.Addr
47
-	// addresses for asset server
48
-	AssetBindAddr   flagtypes.Addr
49
-	AssetPublicAddr flagtypes.Addr
50
-
51
-	ImageTemplate variable.ImageTemplate
52
-
53
-	Hostname  string
54
-	VolumeDir string
55
-
56
-	EtcdDir string
57
-
58
-	CertDir string
59
-
60
-	ClusterDNS net.IP
61
-
62
-	StorageVersion string
63
-
64
-	NodeList kutil.StringList
65
-
66
-	// ClientConfig is used when connecting to Kubernetes from the master, or
67
-	// when connecting to the master from a detached node. If StartKube is true,
68
-	// this value is not used.
69
-	ClientConfig clientcmd.ClientConfig
70
-	// ClientConfigLoadingRules is the ruleset used to load the client config.
71
-	// Only the CommandLinePath is expected to be used.
72
-	ClientConfigLoadingRules clientcmd.ClientConfigLoadingRules
73
-
74
-	CORSAllowedOrigins kutil.StringList
75
-}
76
-
77
-func NewDefaultConfig() *Config {
78
-	hostname, err := defaultHostname()
79
-	if err != nil {
80
-		hostname = "localhost"
81
-		glog.Warningf("Unable to lookup hostname, using %q: %v", hostname, err)
82
-	}
83
-
84
-	// TODO: secure etcd by default
85
-
86
-	config := &Config{
87
-		Docker: docker.NewHelper(),
88
-
89
-		MasterAddr:           flagtypes.Addr{Value: "localhost:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default(),
90
-		BindAddr:             flagtypes.Addr{Value: "0.0.0.0:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default(),
91
-		EtcdAddr:             flagtypes.Addr{Value: "0.0.0.0:4001", DefaultScheme: "http", DefaultPort: 4001}.Default(),
92
-		KubernetesAddr:       flagtypes.Addr{DefaultScheme: "https", DefaultPort: 8443}.Default(),
93
-		PortalNet:            flagtypes.DefaultIPNet("172.30.17.0/24"),
94
-		MasterPublicAddr:     flagtypes.Addr{Value: "localhost:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default(),
95
-		KubernetesPublicAddr: flagtypes.Addr{Value: "localhost:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default(),
96
-		AssetPublicAddr:      flagtypes.Addr{Value: "localhost:8444", DefaultScheme: "https", DefaultPort: 8444, AllowPrefix: true}.Default(),
97
-		AssetBindAddr:        flagtypes.Addr{Value: "0.0.0.0:8444", DefaultScheme: "https", DefaultPort: 8444, AllowPrefix: true}.Default(),
98
-
99
-		ImageTemplate: variable.NewDefaultImageTemplate(),
100
-
101
-		Hostname: hostname,
102
-	}
103
-
104
-	// TODO: allow DNS binding to be disabled.
105
-	config.DNSBindAddr = flagtypes.Addr{Value: config.BindAddr.Host, DefaultPort: 53}.Default()
106
-
107
-	config.ClientConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&config.ClientConfigLoadingRules, &clientcmd.ConfigOverrides{})
108
-
109
-	return config
110
-}
111
-
112
-// GetMasterAddress checks for an unset master address and then attempts to use the first
113
-// public IPv4 non-loopback address registered on this host.
114
-// TODO: make me IPv6 safe
115
-func (cfg Config) GetMasterAddress() (*url.URL, error) {
116
-	if cfg.MasterAddr.Provided {
117
-		return cfg.MasterAddr.URL, nil
118
-	}
119
-
120
-	// If the user specifies a bind address, and the master is not provided, use the bind port by default
121
-	port := cfg.MasterAddr.Port
122
-	if cfg.BindAddr.Provided {
123
-		port = cfg.BindAddr.Port
124
-	}
125
-
126
-	// If the user specifies a bind address, and the master is not provided, use the bind scheme by default
127
-	scheme := cfg.MasterAddr.URL.Scheme
128
-	if cfg.BindAddr.Provided {
129
-		scheme = cfg.BindAddr.URL.Scheme
130
-	}
131
-
132
-	// use the default ip address for the system
133
-	addr := ""
134
-	if ip, err := util.DefaultLocalIP4(); err == nil {
135
-		addr = ip.String()
136
-	} else if err == util.ErrorNoDefaultIP {
137
-		addr = "127.0.0.1"
138
-	} else if err != nil {
139
-		return nil, fmt.Errorf("Unable to find a public IP address: %v", err)
140
-	}
141
-
142
-	masterAddr := scheme + "://" + net.JoinHostPort(addr, strconv.Itoa(port))
143
-	return url.Parse(masterAddr)
144
-}
145
-
146
-func (cfg Config) GetMasterPublicAddress() (*url.URL, error) {
147
-	if cfg.MasterPublicAddr.Provided {
148
-		return cfg.MasterPublicAddr.URL, nil
149
-	}
150
-
151
-	return cfg.GetMasterAddress()
152
-}
153
-
154
-func (cfg Config) GetEtcdBindAddress() string {
155
-	// Derive the etcd bind address by using the bind address and the default etcd port
156
-	return net.JoinHostPort(cfg.BindAddr.Host, strconv.Itoa(cfg.EtcdAddr.DefaultPort))
157
-}
158
-
159
-func (cfg Config) GetEtcdPeerBindAddress() string {
160
-	// Derive the etcd peer address by using the bind address and the default etcd peering port
161
-	return net.JoinHostPort(cfg.BindAddr.Host, "7001")
162
-}
163
-
164
-func (cfg Config) GetEtcdAddress() (*url.URL, error) {
165
-	if cfg.EtcdAddr.Provided {
166
-		return cfg.EtcdAddr.URL, nil
167
-	}
168
-
169
-	// Etcd should be reachable on the same address that the master is (for simplicity)
170
-	masterAddr, err := cfg.GetMasterAddress()
171
-	if err != nil {
172
-		return nil, err
173
-	}
174
-
175
-	etcdAddr := net.JoinHostPort(getHost(*masterAddr), strconv.Itoa(cfg.EtcdAddr.DefaultPort))
176
-	return url.Parse(cfg.EtcdAddr.DefaultScheme + "://" + etcdAddr)
177
-}
178
-
179
-func (cfg Config) GetExternalKubernetesClientConfig() (*client.Config, bool, error) {
180
-	if len(cfg.ClientConfigLoadingRules.CommandLinePath) == 0 || cfg.ClientConfig == nil {
181
-		return nil, false, nil
182
-	}
183
-	clientConfig, err := cfg.ClientConfig.ClientConfig()
184
-	if err != nil {
185
-		return nil, false, err
186
-	}
187
-	return clientConfig, true, nil
188
-}
189
-
190
-func (cfg Config) GetKubernetesAddress() (*url.URL, error) {
191
-	if cfg.KubernetesAddr.Provided {
192
-		return cfg.KubernetesAddr.URL, nil
193
-	}
194
-
195
-	config, ok, err := cfg.GetExternalKubernetesClientConfig()
196
-	if err != nil {
197
-		return nil, err
198
-	}
199
-	if ok && len(config.Host) > 0 {
200
-		return url.Parse(config.Host)
201
-	}
202
-
203
-	return cfg.GetMasterAddress()
204
-}
205
-
206
-func (cfg Config) GetKubernetesPublicAddress() (*url.URL, error) {
207
-	if cfg.KubernetesPublicAddr.Provided {
208
-		return cfg.KubernetesPublicAddr.URL, nil
209
-	}
210
-	if cfg.KubernetesAddr.Provided {
211
-		return cfg.KubernetesAddr.URL, nil
212
-	}
213
-	config, ok, err := cfg.GetExternalKubernetesClientConfig()
214
-	if err != nil {
215
-		return nil, err
216
-	}
217
-	if ok && len(config.Host) > 0 {
218
-		return url.Parse(config.Host)
219
-	}
220
-
221
-	return cfg.GetMasterPublicAddress()
222
-}
223
-
224
-func (cfg Config) GetAssetPublicAddress() (*url.URL, error) {
225
-	if cfg.AssetPublicAddr.Provided {
226
-		return cfg.AssetPublicAddr.URL, nil
227
-	}
228
-	// Derive the asset public address by incrementing the master public address port by 1
229
-	// TODO: derive the scheme/port from the asset bind scheme/port once that is settable via the command line
230
-	t, err := cfg.GetMasterPublicAddress()
231
-	if err != nil {
232
-		return nil, err
233
-	}
234
-	assetPublicAddr := *t
235
-	assetPublicAddr.Host = net.JoinHostPort(getHost(assetPublicAddr), strconv.Itoa(getPort(assetPublicAddr)+1))
236
-
237
-	return &assetPublicAddr, nil
238
-}
239
-
240
-func (cfg Config) GetAssetBindAddress() string {
241
-	if cfg.AssetBindAddr.Provided {
242
-		return cfg.AssetBindAddr.URL.Host
243
-	}
244
-	// Derive the asset bind address by incrementing the master bind address port by 1
245
-	return net.JoinHostPort(cfg.BindAddr.Host, strconv.Itoa(cfg.BindAddr.Port+1))
246
-}
247
-
248
-// getAndTestEtcdClient creates an etcd client based on the provided config and waits
249
-// until etcd server is reachable. It errors out and exits if the server cannot
250
-// be reached for a certain amount of time.
251
-func (cfg Config) getAndTestEtcdClient() (*etcdclient.Client, error) {
252
-	address, err := cfg.GetEtcdAddress()
253
-	if err != nil {
254
-		return nil, err
255
-	}
256
-	etcdServers := []string{address.String()}
257
-	etcdClient := etcdclient.NewClient(etcdServers)
258
-
259
-	for i := 0; ; i++ {
260
-		// TODO: make sure this works with etcd2 (root key may not exist)
261
-		_, err := etcdClient.Get("/", false, false)
262
-		if err == nil || tools.IsEtcdNotFound(err) {
263
-			break
264
-		}
265
-		if i > 100 {
266
-			return nil, fmt.Errorf("Could not reach etcd: %v", err)
267
-		}
268
-		time.Sleep(50 * time.Millisecond)
269
-	}
270
-
271
-	return etcdClient, nil
272
-}
273
-
274
-// newOpenShiftEtcdHelper returns an EtcdHelper for the provided arguments or an error if the version
275
-// is incorrect.
276
-func (cfg Config) newOpenShiftEtcdHelper() (helper tools.EtcdHelper, err error) {
277
-	// Connect and setup etcd interfaces
278
-	client, err := cfg.getAndTestEtcdClient()
279
-	if err != nil {
280
-		return tools.EtcdHelper{}, err
281
-	}
282
-
283
-	version := cfg.StorageVersion
284
-	if len(version) == 0 {
285
-		version = latest.Version
286
-	}
287
-	interfaces, err := latest.InterfacesFor(version)
288
-	if err != nil {
289
-		return helper, err
290
-	}
291
-	return tools.EtcdHelper{client, interfaces.Codec, tools.RuntimeVersionAdapter{interfaces.MetadataAccessor}}, nil
292
-}
293
-
294
-// defaultHostname returns the default hostname for this system.
295
-func defaultHostname() (string, error) {
296
-	// Note: We use exec here instead of os.Hostname() because we
297
-	// want the FQDN, and this is the easiest way to get it.
298
-	fqdn, err := exec.Command("hostname", "-f").Output()
299
-	if err != nil {
300
-		return "", fmt.Errorf("Couldn't determine hostname: %v", err)
301
-	}
302
-	return strings.TrimSpace(string(fqdn)), nil
303
-}
304 1
deleted file mode 100644
... ...
@@ -1,528 +0,0 @@
1
-package server
2
-
3
-import (
4
-	"testing"
5
-
6
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
7
-	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
8
-	"github.com/openshift/origin/pkg/cmd/util"
9
-)
10
-
11
-func TestMasterPublicAddressDefaulting(t *testing.T) {
12
-	expected := "http://example.com:9012"
13
-
14
-	cfg := NewDefaultConfig()
15
-	cfg.MasterAddr.Set(expected)
16
-
17
-	actual, err := cfg.GetMasterPublicAddress()
18
-	if err != nil {
19
-		t.Errorf("unexpected error: %v", err)
20
-	}
21
-	if expected != actual.String() {
22
-		t.Errorf("expected %v, got %v", expected, actual)
23
-	}
24
-}
25
-
26
-func TestMasterPublicAddressExplicit(t *testing.T) {
27
-	expected := "http://external.com:12445"
28
-
29
-	cfg := NewDefaultConfig()
30
-	cfg.MasterAddr.Set("http://internal.com:9012")
31
-	cfg.MasterPublicAddr.Set(expected)
32
-
33
-	actual, err := cfg.GetMasterPublicAddress()
34
-	if err != nil {
35
-		t.Errorf("unexpected error: %v", err)
36
-	}
37
-	if expected != actual.String() {
38
-		t.Errorf("expected %v, got %v", expected, actual)
39
-	}
40
-}
41
-
42
-func TestAssetPublicAddressDefaulting(t *testing.T) {
43
-	master := "http://example.com:9011"
44
-	expected := "http://example.com:9012"
45
-
46
-	cfg := NewDefaultConfig()
47
-	cfg.MasterAddr.Set(master)
48
-
49
-	actual, err := cfg.GetAssetPublicAddress()
50
-	if err != nil {
51
-		t.Errorf("unexpected error: %v", err)
52
-	}
53
-	if expected != actual.String() {
54
-		t.Errorf("expected %v, got %v", expected, actual)
55
-	}
56
-}
57
-
58
-func TestAssetPublicAddressExplicit(t *testing.T) {
59
-	master := "http://example.com:9011"
60
-	expected := "https://example.com:9014"
61
-
62
-	cfg := NewDefaultConfig()
63
-	cfg.MasterAddr.Set(master)
64
-	cfg.AssetPublicAddr.Set(expected)
65
-
66
-	actual, err := cfg.GetAssetPublicAddress()
67
-	if err != nil {
68
-		t.Errorf("unexpected error: %v", err)
69
-	}
70
-	if expected != actual.String() {
71
-		t.Errorf("expected %v, got %v", expected, actual)
72
-	}
73
-}
74
-
75
-func TestAssetBindAddressDefaulting(t *testing.T) {
76
-	bind := "1.2.3.4:9011"
77
-	expected := "1.2.3.4:9012"
78
-
79
-	cfg := NewDefaultConfig()
80
-	cfg.BindAddr.Set(bind)
81
-
82
-	actual := cfg.GetAssetBindAddress()
83
-	if expected != actual {
84
-		t.Errorf("expected %v, got %v", expected, actual)
85
-	}
86
-}
87
-
88
-func TestAssetBindAddressExplicit(t *testing.T) {
89
-	bind := "1.2.3.4:9011"
90
-	expected := "2.3.4.5:1234"
91
-
92
-	cfg := NewDefaultConfig()
93
-	cfg.BindAddr.Set(bind)
94
-	cfg.AssetBindAddr.Set(expected)
95
-
96
-	actual := cfg.GetAssetBindAddress()
97
-	if expected != actual {
98
-		t.Errorf("expected %v, got %v", expected, actual)
99
-	}
100
-}
101
-
102
-func TestKubernetesPublicAddressDefaultToKubernetesAddress(t *testing.T) {
103
-	expected := "http://example.com:9012"
104
-
105
-	cfg := NewDefaultConfig()
106
-	cfg.KubernetesAddr.Set(expected)
107
-	cfg.MasterPublicAddr.Set("unexpectedpublicmaster")
108
-	cfg.MasterAddr.Set("unexpectedmaster")
109
-
110
-	actual, err := cfg.GetKubernetesPublicAddress()
111
-	if err != nil {
112
-		t.Fatalf("unexpected error: %v", err)
113
-	}
114
-	if expected != actual.String() {
115
-		t.Fatalf("expected %v, got %v", expected, actual)
116
-	}
117
-}
118
-
119
-func TestKubernetesPublicAddressDefaultToPublicMasterAddress(t *testing.T) {
120
-	expected := "http://example.com:9012"
121
-
122
-	cfg := NewDefaultConfig()
123
-	cfg.MasterPublicAddr.Set(expected)
124
-	cfg.MasterAddr.Set("unexpectedmaster")
125
-
126
-	actual, err := cfg.GetKubernetesPublicAddress()
127
-	if err != nil {
128
-		t.Fatalf("unexpected error: %v", err)
129
-	}
130
-	if expected != actual.String() {
131
-		t.Fatalf("expected %v, got %v", expected, actual)
132
-	}
133
-}
134
-
135
-func TestKubernetesPublicAddressDefaultToMasterAddress(t *testing.T) {
136
-	expected := "http://example.com:9012"
137
-
138
-	cfg := NewDefaultConfig()
139
-	cfg.MasterAddr.Set(expected)
140
-
141
-	actual, err := cfg.GetKubernetesPublicAddress()
142
-	if err != nil {
143
-		t.Fatalf("unexpected error: %v", err)
144
-	}
145
-	if expected != actual.String() {
146
-		t.Fatalf("expected %v, got %v", expected, actual)
147
-	}
148
-}
149
-
150
-func TestKubernetesPublicAddressExplicit(t *testing.T) {
151
-	expected := "http://external.com:12445"
152
-
153
-	cfg := NewDefaultConfig()
154
-	cfg.MasterAddr.Set("http://internal.com:9012")
155
-	cfg.KubernetesAddr.Set("http://internal.com:9013")
156
-	cfg.MasterPublicAddr.Set("http://internal.com:9014")
157
-	cfg.KubernetesPublicAddr.Set(expected)
158
-
159
-	actual, err := cfg.GetKubernetesPublicAddress()
160
-	if err != nil {
161
-		t.Fatalf("unexpected error: %v", err)
162
-	}
163
-	if expected != actual.String() {
164
-		t.Fatalf("expected %v, got %v", expected, actual)
165
-	}
166
-}
167
-
168
-func TestKubernetesAddressDefaulting(t *testing.T) {
169
-	expected := "http://example.com:9012"
170
-
171
-	cfg := NewDefaultConfig()
172
-	cfg.MasterAddr.Set(expected)
173
-
174
-	actual, err := cfg.GetKubernetesAddress()
175
-	if err != nil {
176
-		t.Fatalf("unexpected error: %v", err)
177
-	}
178
-	if expected != actual.String() {
179
-		t.Fatalf("expected %v, got %v", expected, actual)
180
-	}
181
-}
182
-
183
-func TestKubernetesAddressExplicit(t *testing.T) {
184
-	expected := "http://external.com:12445"
185
-
186
-	cfg := NewDefaultConfig()
187
-	cfg.MasterAddr.Set("http://internal.com:9012")
188
-	cfg.KubernetesAddr.Set(expected)
189
-
190
-	actual, err := cfg.GetKubernetesAddress()
191
-	if err != nil {
192
-		t.Fatalf("unexpected error: %v", err)
193
-	}
194
-	if expected != actual.String() {
195
-		t.Fatalf("expected %v, got %v", expected, actual)
196
-	}
197
-}
198
-
199
-func TestEtcdAddressDefaulting(t *testing.T) {
200
-	expected := "http://example.com:4001"
201
-	master := "https://example.com:9012"
202
-
203
-	cfg := NewDefaultConfig()
204
-	cfg.MasterAddr.Set(master)
205
-
206
-	actual, err := cfg.GetEtcdAddress()
207
-	if err != nil {
208
-		t.Fatalf("unexpected error: %v", err)
209
-	}
210
-	if expected != actual.String() {
211
-		t.Fatalf("expected %v, got %v", expected, actual)
212
-	}
213
-}
214
-
215
-func TestEtcdAddressExplicit(t *testing.T) {
216
-	expected := "http://external.com:12445"
217
-
218
-	cfg := NewDefaultConfig()
219
-	cfg.MasterAddr.Set("http://internal.com:9012")
220
-	cfg.EtcdAddr.Set(expected)
221
-
222
-	actual, err := cfg.GetEtcdAddress()
223
-	if err != nil {
224
-		t.Fatalf("unexpected error: %v", err)
225
-	}
226
-	if expected != actual.String() {
227
-		t.Fatalf("expected %v, got %v", expected, actual)
228
-	}
229
-}
230
-
231
-func TestEtcdBindAddressDefault(t *testing.T) {
232
-	expected := "0.0.0.0:4001"
233
-
234
-	cfg := NewDefaultConfig()
235
-	actual := cfg.GetEtcdBindAddress()
236
-	if expected != actual {
237
-		t.Fatalf("expected %v, got %v", expected, actual)
238
-	}
239
-}
240
-
241
-func TestEtcdPeerAddressDefault(t *testing.T) {
242
-	expected := "0.0.0.0:7001"
243
-
244
-	cfg := NewDefaultConfig()
245
-	actual := cfg.GetEtcdPeerBindAddress()
246
-	if expected != actual {
247
-		t.Fatalf("expected %v, got %v", expected, actual)
248
-	}
249
-}
250
-
251
-func TestEtcdBindAddressDefaultToBind(t *testing.T) {
252
-	expected := "1.2.3.4:4001"
253
-
254
-	cfg := NewDefaultConfig()
255
-	cfg.BindAddr.Set("https://1.2.3.4:8080")
256
-
257
-	actual := cfg.GetEtcdBindAddress()
258
-	if expected != actual {
259
-		t.Fatalf("expected %v, got %v", expected, actual)
260
-	}
261
-}
262
-
263
-func TestMasterAddressDefaultingToBindValues(t *testing.T) {
264
-	defaultIP, err := util.DefaultLocalIP4()
265
-	if err != nil {
266
-		t.Fatalf("unexpected error: %v", err)
267
-	}
268
-	expected := "http://" + defaultIP.String() + ":9012"
269
-
270
-	cfg := NewDefaultConfig()
271
-	cfg.StartMaster = true
272
-	cfg.BindAddr.Set("http://0.0.0.0:9012")
273
-
274
-	actual, err := cfg.GetMasterAddress()
275
-	if err != nil {
276
-		t.Fatalf("unexpected error: %v", err)
277
-	}
278
-	if expected != actual.String() {
279
-		t.Fatalf("expected %v, got %v", expected, actual)
280
-	}
281
-}
282
-
283
-func TestMasterAddressExplicit(t *testing.T) {
284
-	expected := "http://external.com:12445"
285
-
286
-	cfg := NewDefaultConfig()
287
-	cfg.MasterAddr.Set(expected)
288
-
289
-	actual, err := cfg.GetMasterAddress()
290
-	if err != nil {
291
-		t.Fatalf("unexpected error: %v", err)
292
-	}
293
-	if expected != actual.String() {
294
-		t.Fatalf("expected %v, got %v", expected, actual)
295
-	}
296
-}
297
-
298
-func TestKubeClientForExternalKubernetesMasterWithNoConfig(t *testing.T) {
299
-	expected := "https://localhost:8443"
300
-
301
-	cfg := NewDefaultConfig()
302
-	cfg.StartMaster = true
303
-	cfg.MasterAddr.Set(expected)
304
-
305
-	actual, err := cfg.GetKubernetesAddress()
306
-	if err != nil {
307
-		t.Fatalf("unexpected error: %v", err)
308
-	}
309
-	if expected != actual.String() {
310
-		t.Fatalf("expected %v, got %v", expected, actual)
311
-	}
312
-
313
-	_, config, err := cfg.GetKubeClient()
314
-	if err != nil {
315
-		t.Fatalf("unexpected error: %v", err)
316
-	}
317
-	if expected != config.Host {
318
-		t.Fatalf("expected %v, got %v", expected, config.Host)
319
-	}
320
-}
321
-
322
-func TestKubeClientForNodeWithNoConfig(t *testing.T) {
323
-	expected := "https://localhost:8443"
324
-
325
-	cfg := NewDefaultConfig()
326
-	cfg.StartNode = true
327
-	cfg.MasterAddr.Set(expected)
328
-
329
-	actual, err := cfg.GetKubernetesAddress()
330
-	if err != nil {
331
-		t.Fatalf("unexpected error: %v", err)
332
-	}
333
-	if expected != actual.String() {
334
-		t.Fatalf("expected %v, got %v", expected, actual)
335
-	}
336
-
337
-	_, config, err := cfg.GetKubeClient()
338
-	if err != nil {
339
-		t.Fatalf("unexpected error: %v", err)
340
-	}
341
-	if expected != config.Host {
342
-		t.Fatalf("expected %v, got %v", expected, config.Host)
343
-	}
344
-}
345
-
346
-func TestKubeClientForExternalKubernetesMasterWithConfig(t *testing.T) {
347
-	expectedServer := "https://some-other-server:1234"
348
-	expectedUser := "myuser"
349
-
350
-	cfg := NewDefaultConfig()
351
-	cfg.StartMaster = true
352
-	cfg.ClientConfigLoadingRules, cfg.ClientConfig = makeKubeconfig(expectedServer, expectedUser)
353
-
354
-	actualPublic, err := cfg.GetKubernetesPublicAddress()
355
-	if err != nil {
356
-		t.Fatalf("unexpected error: %v", err)
357
-	}
358
-	if expectedServer != actualPublic.String() {
359
-		t.Fatalf("expected %v, got %v", expectedServer, actualPublic)
360
-	}
361
-
362
-	actual, err := cfg.GetKubernetesAddress()
363
-	if err != nil {
364
-		t.Fatalf("unexpected error: %v", err)
365
-	}
366
-	if expectedServer != actual.String() {
367
-		t.Fatalf("expected %v, got %v", expectedServer, actual)
368
-	}
369
-
370
-	_, config, err := cfg.GetKubeClient()
371
-	if err != nil {
372
-		t.Fatalf("unexpected error: %v", err)
373
-	}
374
-	if config.Host != expectedServer {
375
-		t.Fatalf("expected %v, got %v", expectedServer, config.Host)
376
-	}
377
-	if config.Username != expectedUser {
378
-		t.Fatalf("expected %v, got %v", expectedUser, config.Username)
379
-	}
380
-}
381
-
382
-func TestKubeClientForNodeWithConfig(t *testing.T) {
383
-	expectedServer := "https://some-other-server:1234"
384
-	expectedUser := "myuser"
385
-
386
-	cfg := NewDefaultConfig()
387
-	cfg.StartNode = true
388
-	cfg.ClientConfigLoadingRules, cfg.ClientConfig = makeKubeconfig(expectedServer, expectedUser)
389
-
390
-	actualPublic, err := cfg.GetKubernetesPublicAddress()
391
-	if err != nil {
392
-		t.Fatalf("unexpected error: %v", err)
393
-	}
394
-	if expectedServer != actualPublic.String() {
395
-		t.Fatalf("expected %v, got %v", expectedServer, actualPublic)
396
-	}
397
-
398
-	actual, err := cfg.GetKubernetesAddress()
399
-	if err != nil {
400
-		t.Fatalf("unexpected error: %v", err)
401
-	}
402
-	if expectedServer != actual.String() {
403
-		t.Fatalf("expected %v, got %v", expectedServer, actual)
404
-	}
405
-
406
-	_, config, err := cfg.GetKubeClient()
407
-	if err != nil {
408
-		t.Fatalf("unexpected error: %v", err)
409
-	}
410
-	if config.Host != expectedServer {
411
-		t.Fatalf("expected %v, got %v", expectedServer, config.Host)
412
-	}
413
-	if config.Username != expectedUser {
414
-		t.Fatalf("expected %v, got %v", expectedUser, config.Username)
415
-	}
416
-}
417
-
418
-func TestKubeClientForExternalKubernetesMasterWithErrorKubeconfig(t *testing.T) {
419
-	cfg := NewDefaultConfig()
420
-	cfg.StartMaster = true
421
-	cfg.ClientConfigLoadingRules, cfg.ClientConfig = makeErrorKubeconfig()
422
-
423
-	// GetKubernetesPublicAddress hits the invalid kubeconfig in the fallback chain
424
-	_, err := cfg.GetKubernetesPublicAddress()
425
-	if err == nil {
426
-		t.Fatalf("expected error, got none")
427
-	}
428
-
429
-	// GetKubernetesAddress hits the invalid kubeconfig in the fallback chain
430
-	_, err = cfg.GetKubernetesAddress()
431
-	if err == nil {
432
-		t.Fatalf("expected error, got none")
433
-	}
434
-
435
-	// Should not get a client
436
-	if _, _, err = cfg.GetKubeClient(); err == nil {
437
-		t.Fatalf("expected error, got none")
438
-	}
439
-}
440
-
441
-func TestKubeClientForExternalKubernetesMasterWithConflictingKubernetesAddress(t *testing.T) {
442
-	expectedServer := "https://some-other-server:1234"
443
-	expectedUser := "myuser"
444
-
445
-	cfg := NewDefaultConfig()
446
-	cfg.StartMaster = true
447
-	// Explicitly set --kubernetes must match --kubeconfig or return an error
448
-	cfg.KubernetesAddr.Set(expectedServer)
449
-	cfg.ClientConfigLoadingRules, cfg.ClientConfig = makeKubeconfig("https://another-server:2345", expectedUser)
450
-
451
-	// GetKubernetesAddress returns the explicitly set address
452
-	actual, err := cfg.GetKubernetesAddress()
453
-	if err != nil {
454
-		t.Fatalf("unexpected error: %v", err)
455
-	}
456
-	if expectedServer != actual.String() {
457
-		t.Fatalf("expected %v, got %v", expectedServer, actual)
458
-	}
459
-
460
-	// Should not get a client that might let us send credentials to the wrong server
461
-	if _, _, err := cfg.GetKubeClient(); err == nil {
462
-		t.Fatalf("expected error, got none")
463
-	}
464
-}
465
-
466
-func TestKubeClientForNodeWithConflictingKubernetesAddress(t *testing.T) {
467
-	expectedServer := "https://some-other-server:1234"
468
-	expectedUser := "myuser"
469
-
470
-	cfg := NewDefaultConfig()
471
-	cfg.StartNode = true
472
-	cfg.KubernetesAddr.Set(expectedServer)
473
-	cfg.ClientConfigLoadingRules, cfg.ClientConfig = makeKubeconfig("https://another-server:2345", expectedUser)
474
-
475
-	// GetKubernetesAddress returns the explicitly set address
476
-	actualServer, err := cfg.GetKubernetesAddress()
477
-	if err != nil {
478
-		t.Fatalf("unexpected error: %v", err)
479
-	}
480
-	if expectedServer != actualServer.String() {
481
-		t.Fatalf("expected %v, got %v", expectedServer, actualServer)
482
-	}
483
-
484
-	// Should not get a client that might let us send credentials to the wrong server
485
-	if _, _, err := cfg.GetKubeClient(); err == nil {
486
-		t.Fatalf("expected error, got none")
487
-	}
488
-}
489
-
490
-func makeEmptyKubeconfig() (clientcmd.ClientConfigLoadingRules, clientcmd.ClientConfig) {
491
-	// Set a non-empty CommandLinePath to trigger loading
492
-	loadingRules := clientcmd.ClientConfigLoadingRules{CommandLinePath: "specified"}
493
-
494
-	clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
495
-		// Set empty loading rules to avoid missing file errors
496
-		&clientcmd.ClientConfigLoadingRules{},
497
-		&clientcmd.ConfigOverrides{},
498
-	)
499
-	return loadingRules, clientConfig
500
-}
501
-
502
-func makeErrorKubeconfig() (clientcmd.ClientConfigLoadingRules, clientcmd.ClientConfig) {
503
-	// Set a non-empty CommandLinePath to trigger loading
504
-	loadingRules := clientcmd.ClientConfigLoadingRules{CommandLinePath: "missing-file"}
505
-
506
-	clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
507
-		&loadingRules,
508
-		&clientcmd.ConfigOverrides{},
509
-	)
510
-	return loadingRules, clientConfig
511
-}
512
-
513
-func makeKubeconfig(server, user string) (clientcmd.ClientConfigLoadingRules, clientcmd.ClientConfig) {
514
-	// Set a non-empty CommandLinePath to trigger loading
515
-	loadingRules := clientcmd.ClientConfigLoadingRules{CommandLinePath: "specified"}
516
-
517
-	clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
518
-		// Set empty loading rules to avoid missing file errors
519
-		&clientcmd.ClientConfigLoadingRules{},
520
-		// Override the server and user in client config to simulate loading from a file
521
-		&clientcmd.ConfigOverrides{
522
-			ClusterInfo: clientcmdapi.Cluster{Server: server},
523
-			AuthInfo:    clientcmdapi.AuthInfo{Username: user},
524
-		},
525
-	)
526
-
527
-	return loadingRules, clientConfig
528
-}
... ...
@@ -20,92 +20,102 @@ import (
20 20
 	"strconv"
21 21
 	"time"
22 22
 
23
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
24
-	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
25
-	clientcmd "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
26
-	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
27 23
 	"github.com/golang/glog"
24
+
25
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
26
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
27
+
28 28
 	"github.com/openshift/origin/pkg/auth/authenticator/request/x509request"
29 29
 )
30 30
 
31
-func filenamesFromDir(dir string) (string, string, string) {
32
-	return filepath.Join(dir, "root.crt"), filepath.Join(dir, "cert.crt"), filepath.Join(dir, "key.key")
33
-}
34
-
35 31
 type TLSCertificateConfig struct {
36
-	CAFile   string
37
-	CertFile string
38
-	KeyFile  string
39
-
40
-	Roots []*x509.Certificate
41 32
 	Certs []*x509.Certificate
42 33
 	Key   crypto.PrivateKey
43 34
 }
44 35
 
45
-func (c *TLSCertificateConfig) writeDir(dir string) error {
46
-	c.CAFile, c.CertFile, c.KeyFile = filenamesFromDir(dir)
47
-
48
-	// mkdir
49
-	if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
50
-		return err
51
-	}
36
+type TLSCARoots struct {
37
+	Roots []*x509.Certificate
38
+}
52 39
 
53
-	// write certs and keys
54
-	if err := writeCertificates(c.CAFile, c.Roots...); err != nil {
40
+func (c *TLSCertificateConfig) writeCertConfig(certFile, keyFile string) error {
41
+	if err := writeCertificates(certFile, c.Certs...); err != nil {
55 42
 		return err
56 43
 	}
57
-	if err := writeCertificates(c.CertFile, c.Certs...); err != nil {
44
+	if err := writeKeyFile(keyFile, c.Key); err != nil {
58 45
 		return err
59 46
 	}
60
-	if err := writeKeyFile(c.KeyFile, c.Key); err != nil {
47
+	return nil
48
+}
49
+func (c *TLSCARoots) writeCARoots(rootFile string) error {
50
+	if err := writeCertificates(rootFile, c.Roots...); err != nil {
61 51
 		return err
62 52
 	}
63 53
 	return nil
64 54
 }
65 55
 
66
-func newTLSCertificateConfig(dir string) (*TLSCertificateConfig, error) {
67
-	caFile, certFile, keyFile := filenamesFromDir(dir)
68
-	config := &TLSCertificateConfig{
69
-		CAFile:   caFile,
70
-		CertFile: certFile,
71
-		KeyFile:  keyFile,
56
+func GetTLSCARoots(caFile string) (*TLSCARoots, error) {
57
+	if len(caFile) == 0 {
58
+		return nil, errors.New("caFile missing")
72 59
 	}
73 60
 
74
-	if caFile != "" {
75
-		caPEMBlock, err := ioutil.ReadFile(caFile)
76
-		if err != nil {
77
-			return nil, err
78
-		}
79
-		config.Roots, err = certsFromPEM(caPEMBlock)
80
-		if err != nil {
81
-			return nil, fmt.Errorf("Error reading %s: %s", caFile, err)
82
-		}
61
+	caPEMBlock, err := ioutil.ReadFile(caFile)
62
+	if err != nil {
63
+		return nil, err
64
+	}
65
+	roots, err := certsFromPEM(caPEMBlock)
66
+	if err != nil {
67
+		return nil, fmt.Errorf("Error reading %s: %s", caFile, err)
83 68
 	}
84 69
 
85
-	if certFile != "" {
86
-		certPEMBlock, err := ioutil.ReadFile(certFile)
87
-		if err != nil {
88
-			return nil, err
89
-		}
90
-		config.Certs, err = certsFromPEM(certPEMBlock)
91
-		if err != nil {
92
-			return nil, fmt.Errorf("Error reading %s: %s", caFile, err)
93
-		}
70
+	return &TLSCARoots{roots}, nil
71
+}
94 72
 
95
-		if keyFile != "" {
96
-			keyPEMBlock, err := ioutil.ReadFile(keyFile)
97
-			if err != nil {
98
-				return nil, err
99
-			}
100
-			keyPairCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
101
-			if err != nil {
102
-				return nil, err
103
-			}
104
-			config.Key = keyPairCert.PrivateKey
105
-		}
73
+func GetTLSCertificateConfig(certFile, keyFile string) (*TLSCertificateConfig, error) {
74
+	if len(certFile) == 0 {
75
+		return nil, errors.New("certFile missing")
76
+	}
77
+	if len(keyFile) == 0 {
78
+		return nil, errors.New("keyFile missing")
79
+	}
80
+
81
+	certPEMBlock, err := ioutil.ReadFile(certFile)
82
+	if err != nil {
83
+		return nil, err
84
+	}
85
+	certs, err := certsFromPEM(certPEMBlock)
86
+	if err != nil {
87
+		return nil, fmt.Errorf("Error reading %s: %s", certFile, err)
88
+	}
89
+
90
+	keyPEMBlock, err := ioutil.ReadFile(keyFile)
91
+	if err != nil {
92
+		return nil, err
93
+	}
94
+	keyPairCert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
95
+	if err != nil {
96
+		return nil, err
97
+	}
98
+	key := keyPairCert.PrivateKey
99
+
100
+	return &TLSCertificateConfig{certs, key}, nil
101
+}
102
+
103
+func CertPoolFromFile(filename string) (*x509.CertPool, error) {
104
+	pemBlock, err := ioutil.ReadFile(filename)
105
+	if err != nil {
106
+		return nil, err
107
+	}
108
+	certs, err := certsFromPEM(pemBlock)
109
+	if err != nil {
110
+		return nil, fmt.Errorf("Error reading %s: %s", filename, err)
106 111
 	}
107 112
 
108
-	return config, nil
113
+	roots := x509.NewCertPool()
114
+	for _, root := range certs {
115
+		roots.AddCert(root)
116
+	}
117
+
118
+	return roots, nil
109 119
 }
110 120
 
111 121
 func certsFromPEM(pemCerts []byte) ([]*x509.Certificate, error) {
... ...
@@ -145,55 +155,31 @@ var (
145 145
 )
146 146
 
147 147
 type CA struct {
148
-	Dir        string
149 148
 	SerialFile string
150 149
 	Serial     int64
151 150
 	Config     *TLSCertificateConfig
152 151
 }
153 152
 
154
-// InitCA ensures a certificate authority structure exists in the given directory, creating it if necessary:
155
-//	<dir>/
156
-//	  ca/
157
-//	root.crt	- Root certificate bundle.
158
-//	cert.crt	- Signing certificate
159
-//	key.key	 - Private key
160
-//	serial.txt  - Stores the highest serial number generated by this CA
161
-func InitCA(dir string, name string) (*CA, error) {
162
-	caDir := filepath.Join(dir, "ca")
153
+func EnsureCA(certFile, keyFile, serialFile, name string) (*CA, error) {
154
+	if ca, err := GetCA(certFile, keyFile, serialFile); err == nil {
155
+		return ca, nil
156
+	}
157
+
158
+	return MakeCA(certFile, keyFile, serialFile, name)
159
+}
163 160
 
164
-	caConfig, err := newTLSCertificateConfig(caDir)
161
+func GetCA(certFile, keyFile, serialFile string) (*CA, error) {
162
+	caConfig, err := GetTLSCertificateConfig(certFile, keyFile)
165 163
 	if err != nil {
166
-		glog.V(2).Infof("Generating new CA in %s", caDir)
167
-		// Create CA cert
168
-		rootcaPublicKey, rootcaPrivateKey, err := NewKeyPair()
169
-		if err != nil {
170
-			return nil, err
171
-		}
172
-		rootcaTemplate, err := newSigningCertificateTemplate(pkix.Name{CommonName: name})
173
-		if err != nil {
174
-			return nil, err
175
-		}
176
-		rootcaCert, err := signCertificate(rootcaTemplate, rootcaPublicKey, rootcaTemplate, rootcaPrivateKey)
177
-		if err != nil {
178
-			return nil, err
179
-		}
180
-		caConfig = &TLSCertificateConfig{
181
-			Roots: []*x509.Certificate{rootcaCert},
182
-			Certs: []*x509.Certificate{rootcaCert},
183
-			Key:   rootcaPrivateKey,
184
-		}
185
-		if err := caConfig.writeDir(caDir); err != nil {
186
-			return nil, err
187
-		}
188
-	} else {
189
-		glog.V(2).Infof("Using existing CA certificate in %s", caDir)
164
+		return nil, err
190 165
 	}
191 166
 
192 167
 	// read serial file
193 168
 	var serial int64
194
-	serialFile := filepath.Join(caDir, "serial.txt")
195 169
 	if serialData, err := ioutil.ReadFile(serialFile); err == nil {
196 170
 		serial, _ = strconv.ParseInt(string(serialData), 10, 64)
171
+	} else {
172
+		return nil, err
197 173
 	}
198 174
 	if serial < 1 {
199 175
 		serial = 1
... ...
@@ -202,159 +188,127 @@ func InitCA(dir string, name string) (*CA, error) {
202 202
 	return &CA{
203 203
 		Serial:     serial,
204 204
 		SerialFile: serialFile,
205
-		Dir:        dir,
206 205
 		Config:     caConfig,
207 206
 	}, nil
208 207
 }
209 208
 
210
-// MakeServerCert creates a folder containing certificates for the given server:
211
-//	<CA.dir>/
212
-//	 <name>/
213
-//	root.crt	- Root certificate bundle.
214
-//	cert.crt	- Server certificate
215
-//	key.key	 - Private key
216
-// The generated certificate has the following attributes:
217
-//	CommonName: hostnames[0]
218
-//	DNSNames subjectAltNames containing all specified hostnames
219
-//	IPAddresses subjectAltNames containing all specified hostnames which are IP addresses
220
-//	ExtKeyUsage: ExtKeyUsageServerAuth
221
-func (ca *CA) MakeServerCert(name string, hostnames []string) (*TLSCertificateConfig, error) {
222
-	serverDir := filepath.Join(ca.Dir, name)
223
-
224
-	server, err := newTLSCertificateConfig(serverDir)
225
-	if err == nil {
226
-		cert := server.Certs[0]
227
-		ips, dns := IPAddressesDNSNames(hostnames)
228
-		missingIps := ipsNotInSlice(ips, cert.IPAddresses)
229
-		missingDns := stringsNotInSlice(dns, cert.DNSNames)
230
-		if len(missingIps) == 0 && len(missingDns) == 0 {
231
-			glog.V(2).Infof("Using existing server certificate in %s", serverDir)
232
-			return server, nil
233
-		}
209
+func MakeCA(certFile, keyFile, serialFile, name string) (*CA, error) {
210
+	glog.V(2).Infof("Generating new CA for %s cert, and key in %s, %s", name, certFile, keyFile)
211
+	// Create CA cert
212
+	rootcaPublicKey, rootcaPrivateKey, err := NewKeyPair()
213
+	if err != nil {
214
+		return nil, err
215
+	}
216
+	rootcaTemplate, err := newSigningCertificateTemplate(pkix.Name{CommonName: name})
217
+	if err != nil {
218
+		return nil, err
219
+	}
220
+	rootcaCert, err := signCertificate(rootcaTemplate, rootcaPublicKey, rootcaTemplate, rootcaPrivateKey)
221
+	if err != nil {
222
+		return nil, err
223
+	}
224
+	caConfig := &TLSCertificateConfig{
225
+		Certs: []*x509.Certificate{rootcaCert},
226
+		Key:   rootcaPrivateKey,
227
+	}
228
+	if err := caConfig.writeCertConfig(certFile, keyFile); err != nil {
229
+		return nil, err
230
+	}
231
+
232
+	if err := ioutil.WriteFile(serialFile, []byte("0"), 0644); err != nil {
233
+		return nil, err
234
+	}
235
+
236
+	return &CA{
237
+		Serial:     0,
238
+		SerialFile: serialFile,
239
+		Config:     caConfig,
240
+	}, nil
241
+}
234 242
 
235
-		glog.V(2).Infof("Existing server certificate in %s was missing some hostnames (%v) or IP addresses (%v)", serverDir, missingDns, missingIps)
243
+func (ca *CA) EnsureServerCert(certFile, keyFile string, hostnames util.StringSet) (*TLSCertificateConfig, error) {
244
+	certConfig, err := GetServerCert(certFile, keyFile, hostnames)
245
+	if err != nil {
246
+		return ca.MakeServerCert(certFile, keyFile, hostnames)
236 247
 	}
237 248
 
238
-	glog.V(2).Infof("Generating server certificate in %s", serverDir)
249
+	return certConfig, nil
250
+}
251
+
252
+func GetServerCert(certFile, keyFile string, hostnames util.StringSet) (*TLSCertificateConfig, error) {
253
+	server, err := GetTLSCertificateConfig(certFile, keyFile)
254
+	if err != nil {
255
+		return nil, err
256
+	}
257
+
258
+	cert := server.Certs[0]
259
+	ips, dns := IPAddressesDNSNames(hostnames.List())
260
+	missingIps := ipsNotInSlice(ips, cert.IPAddresses)
261
+	missingDns := stringsNotInSlice(dns, cert.DNSNames)
262
+	if len(missingIps) == 0 && len(missingDns) == 0 {
263
+		glog.V(2).Infof("Found existing server certificate in %s", certFile)
264
+		return server, nil
265
+	}
266
+
267
+	return nil, fmt.Errorf("Existing server certificate in %s was missing some hostnames (%v) or IP addresses (%v).", certFile, missingDns, missingIps)
268
+}
269
+
270
+func (ca *CA) MakeServerCert(certFile, keyFile string, hostnames util.StringSet) (*TLSCertificateConfig, error) {
271
+	glog.V(2).Infof("Generating server certificate in %s, key in %s", certFile, keyFile)
272
+
239 273
 	serverPublicKey, serverPrivateKey, _ := NewKeyPair()
240
-	serverTemplate, _ := newServerCertificateTemplate(pkix.Name{CommonName: hostnames[0]}, hostnames)
274
+	serverTemplate, _ := newServerCertificateTemplate(pkix.Name{CommonName: hostnames.List()[0]}, hostnames.List())
241 275
 	serverCrt, _ := ca.signCertificate(serverTemplate, serverPublicKey)
242
-	server = &TLSCertificateConfig{
243
-		Roots: ca.Config.Roots,
276
+	server := &TLSCertificateConfig{
244 277
 		Certs: append([]*x509.Certificate{serverCrt}, ca.Config.Certs...),
245 278
 		Key:   serverPrivateKey,
246 279
 	}
247
-	if err := server.writeDir(serverDir); err != nil {
280
+	if err := server.writeCertConfig(certFile, keyFile); err != nil {
248 281
 		return server, err
249 282
 	}
250 283
 	return server, nil
251 284
 }
252 285
 
253
-// MakeClientConfig creates a folder containing certificates for the given client:
254
-//   <CA.dir>/
255
-//     <clientId>/
256
-//       root.crt - Root certificate bundle.
257
-//       cert.crt - Client certificate
258
-//       key.key  - Private key
259
-//       .kubeconfig - baseKubeconfig with root.crt added to all clusters, and client user added to all contexts
260
-// The generated certificate has the following attributes:
261
-//   Subject:
262
-//     SerialNumber: user.GetUID()
263
-//     CommonName:   user.GetName()
264
-//     Organization: user.GetGroups()
265
-//   ExtKeyUsage: ExtKeyUsageClientAuth
266
-func (ca *CA) MakeClientConfig(clientId string, u user.Info, baseKubeconfig clientcmdapi.Config) (kclient.Config, error) {
267
-	var (
268
-		client                    kclient.Config
269
-		err                       error
270
-		caFile, certFile, keyFile string
271
-		caData, certData, keyData []byte
272
-	)
273
-
274
-	// Ensure the folder exists
275
-	clientDir := filepath.Join(ca.Dir, clientId)
276
-	if err := os.MkdirAll(clientDir, os.FileMode(0755)); err != nil {
277
-		return client, err
278
-	}
279
-
280
-	caFile, certFile, keyFile = filenamesFromDir(clientDir)
281
-	if err == nil {
282
-		caData, err = ioutil.ReadFile(caFile)
283
-	}
284
-	if err == nil {
285
-		certData, err = ioutil.ReadFile(certFile)
286
-	}
287
-	if err == nil {
288
-		keyData, err = ioutil.ReadFile(keyFile)
289
-	}
290
-
291
-	if err == nil {
292
-		glog.V(2).Infof("Using existing client certificates in %s", clientDir)
293
-	} else {
294
-		glog.V(2).Infof("Generating client certificates in %s", clientDir)
295
-
296
-		clientPublicKey, clientPrivateKey, _ := NewKeyPair()
297
-		clientTemplate, _ := newClientCertificateTemplate(x509request.UserToSubject(u))
298
-		clientCrt, _ := ca.signCertificate(clientTemplate, clientPublicKey)
286
+func (ca *CA) EnsureClientCertificate(certFile, keyFile string, u user.Info) (*TLSCertificateConfig, error) {
287
+	certConfig, err := GetTLSCertificateConfig(certFile, keyFile)
288
+	if err != nil {
289
+		return ca.MakeClientCertificate(certFile, keyFile, u)
290
+	}
299 291
 
300
-		caData, err = encodeCertificates(ca.Config.Roots...)
301
-		if err != nil {
302
-			return client, err
303
-		}
304
-		certData, err = encodeCertificates(clientCrt)
305
-		if err != nil {
306
-			return client, err
307
-		}
308
-		keyData, err = encodeKey(clientPrivateKey)
309
-		if err != nil {
310
-			return client, err
311
-		}
292
+	return certConfig, nil
293
+}
312 294
 
313
-		// write files
314
-		if err = ioutil.WriteFile(caFile, caData, os.FileMode(0644)); err != nil {
315
-			return client, err
316
-		}
317
-		if err = ioutil.WriteFile(certFile, certData, os.FileMode(0644)); err != nil {
318
-			return client, err
319
-		}
320
-		if err = ioutil.WriteFile(keyFile, keyData, os.FileMode(0600)); err != nil {
321
-			return client, err
322
-		}
295
+func (ca *CA) MakeClientCertificate(certFile, keyFile string, u user.Info) (*TLSCertificateConfig, error) {
296
+	glog.V(2).Infof("Generating client cert in %s and key in %s", certFile, keyFile)
297
+	// ensure parent dirs
298
+	if err := os.MkdirAll(filepath.Dir(certFile), os.FileMode(0755)); err != nil {
299
+		return nil, err
323 300
 	}
324
-
325
-	// Set the generated certs on a user
326
-	baseKubeconfig.AuthInfos = map[string]clientcmdapi.AuthInfo{
327
-		clientId: {
328
-			ClientCertificateData: certData,
329
-			ClientKeyData:         keyData,
330
-		},
301
+	if err := os.MkdirAll(filepath.Dir(keyFile), os.FileMode(0755)); err != nil {
302
+		return nil, err
331 303
 	}
332 304
 
333
-	// Set the ca certs on all clusters
334
-	for clusterName, cluster := range baseKubeconfig.Clusters {
335
-		cluster.CertificateAuthorityData = caData
336
-		baseKubeconfig.Clusters[clusterName] = cluster
337
-	}
305
+	clientPublicKey, clientPrivateKey, _ := NewKeyPair()
306
+	clientTemplate, _ := newClientCertificateTemplate(x509request.UserToSubject(u))
307
+	clientCrt, _ := ca.signCertificate(clientTemplate, clientPublicKey)
338 308
 
339
-	// Use the new user in all contexts
340
-	for contextName, context := range baseKubeconfig.Contexts {
341
-		context.AuthInfo = clientId
342
-		baseKubeconfig.Contexts[contextName] = context
309
+	certData, err := encodeCertificates(clientCrt)
310
+	if err != nil {
311
+		return nil, err
343 312
 	}
344
-
345
-	if builtClient, err := clientcmd.NewDefaultClientConfig(baseKubeconfig, &clientcmd.ConfigOverrides{}).ClientConfig(); err != nil {
346
-		return client, err
347
-	} else {
348
-		client = *builtClient
313
+	keyData, err := encodeKey(clientPrivateKey)
314
+	if err != nil {
315
+		return nil, err
349 316
 	}
350 317
 
351
-	kubeConfigFile := filepath.Join(clientDir, ".kubeconfig")
352
-	glog.V(2).Infof("Writing client config in %s", kubeConfigFile)
353
-	if err := clientcmd.WriteToFile(baseKubeconfig, kubeConfigFile); err != nil {
354
-		return client, err
318
+	if err = ioutil.WriteFile(certFile, certData, os.FileMode(0644)); err != nil {
319
+		return nil, err
320
+	}
321
+	if err = ioutil.WriteFile(keyFile, keyData, os.FileMode(0600)); err != nil {
322
+		return nil, err
355 323
 	}
356 324
 
357
-	return client, nil
325
+	return GetTLSCertificateConfig(certFile, keyFile)
358 326
 }
359 327
 
360 328
 func (ca *CA) signCertificate(template *x509.Certificate, requestKey crypto.PublicKey) (*x509.Certificate, error) {
... ...
@@ -518,6 +472,11 @@ func encodeKey(key crypto.PrivateKey) ([]byte, error) {
518 518
 }
519 519
 
520 520
 func writeCertificates(path string, certs ...*x509.Certificate) error {
521
+	// ensure parent dir
522
+	if err := os.MkdirAll(filepath.Dir(path), os.FileMode(0755)); err != nil {
523
+		return err
524
+	}
525
+
521 526
 	bytes, err := encodeCertificates(certs...)
522 527
 	if err != nil {
523 528
 		return err
... ...
@@ -525,6 +484,11 @@ func writeCertificates(path string, certs ...*x509.Certificate) error {
525 525
 	return ioutil.WriteFile(path, bytes, os.FileMode(0644))
526 526
 }
527 527
 func writeKeyFile(path string, key crypto.PrivateKey) error {
528
+	// ensure parent dir
529
+	if err := os.MkdirAll(filepath.Dir(path), os.FileMode(0755)); err != nil {
530
+		return err
531
+	}
532
+
528 533
 	b, err := encodeKey(key)
529 534
 	if err != nil {
530 535
 		return err
531 536
deleted file mode 100644
... ...
@@ -1,3 +0,0 @@
1
-// Package server contains the main command for launching an OpenShift server. Subpackages
2
-// contain the specific components that the server may need.
3
-package server
... ...
@@ -1,12 +1,18 @@
1 1
 package etcd
2 2
 
3 3
 import (
4
+	"fmt"
4 5
 	"time"
5 6
 
6
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
7 7
 	etcdconfig "github.com/coreos/etcd/config"
8 8
 	"github.com/coreos/etcd/etcd"
9
+	etcdclient "github.com/coreos/go-etcd/etcd"
9 10
 	"github.com/golang/glog"
11
+
12
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
13
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
14
+
15
+	"github.com/openshift/origin/pkg/api/latest"
10 16
 )
11 17
 
12 18
 // Config is an object that can run an etcd server
... ...
@@ -34,3 +40,42 @@ func (c *Config) Run() {
34 34
 	}, 500*time.Millisecond)
35 35
 	<-server.ReadyNotify()
36 36
 }
37
+
38
+// getAndTestEtcdClient creates an etcd client based on the provided config and waits
39
+// until etcd server is reachable. It errors out and exits if the server cannot
40
+// be reached for a certain amount of time.
41
+func GetAndTestEtcdClient(etcdURL string) (*etcdclient.Client, error) {
42
+	etcdServers := []string{etcdURL}
43
+	etcdClient := etcdclient.NewClient(etcdServers)
44
+
45
+	for i := 0; ; i++ {
46
+		// TODO: make sure this works with etcd2 (root key may not exist)
47
+		_, err := etcdClient.Get("/", false, false)
48
+		if err == nil || tools.IsEtcdNotFound(err) {
49
+			break
50
+		}
51
+		if i > 100 {
52
+			return nil, fmt.Errorf("Could not reach etcd: %v", err)
53
+		}
54
+		time.Sleep(50 * time.Millisecond)
55
+	}
56
+
57
+	return etcdClient, nil
58
+}
59
+
60
+// newOpenShiftEtcdHelper returns an EtcdHelper for the provided arguments or an error if the version
61
+// is incorrect.
62
+func NewOpenShiftEtcdHelper(etcdURL string) (helper tools.EtcdHelper, err error) {
63
+	// Connect and setup etcd interfaces
64
+	client, err := GetAndTestEtcdClient(etcdURL)
65
+	if err != nil {
66
+		return tools.EtcdHelper{}, err
67
+	}
68
+
69
+	version := latest.Version
70
+	interfaces, err := latest.InterfacesFor(version)
71
+	if err != nil {
72
+		return helper, err
73
+	}
74
+	return tools.EtcdHelper{client, interfaces.Codec, tools.RuntimeVersionAdapter{interfaces.MetadataAccessor}}, nil
75
+}
37 76
deleted file mode 100644
... ...
@@ -1,71 +0,0 @@
1
-package server
2
-
3
-import (
4
-	"fmt"
5
-	"net"
6
-
7
-	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
8
-	klatest "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
9
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
10
-	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
11
-	kmaster "github.com/GoogleCloudPlatform/kubernetes/pkg/master"
12
-
13
-	"github.com/openshift/origin/pkg/cmd/server/kubernetes"
14
-
15
-	// Admission control plugins from upstream Kubernetes
16
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
17
-	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/admit"
18
-	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/limitranger"
19
-	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/namespace/exists"
20
-	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/resourcedefaults"
21
-	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/resourcequota"
22
-)
23
-
24
-func (cfg Config) BuildKubernetesMasterConfig(requestContextMapper kapi.RequestContextMapper, kubeClient *kclient.Client) (*kubernetes.MasterConfig, error) {
25
-	masterAddr, err := cfg.GetMasterAddress()
26
-	if err != nil {
27
-		return nil, err
28
-	}
29
-
30
-	// Connect and setup etcd interfaces
31
-	etcdClient, err := cfg.getAndTestEtcdClient()
32
-	if err != nil {
33
-		return nil, err
34
-	}
35
-	ketcdHelper, err := kmaster.NewEtcdHelper(etcdClient, klatest.Version)
36
-	if err != nil {
37
-		return nil, fmt.Errorf("Error setting up Kubernetes server storage: %v", err)
38
-	}
39
-
40
-	portalNet := net.IPNet(cfg.PortalNet)
41
-	masterIP := net.ParseIP(getHost(*masterAddr))
42
-	if masterIP == nil {
43
-		addrs, err := net.LookupIP(getHost(*masterAddr))
44
-		if err != nil {
45
-			return nil, fmt.Errorf("Unable to find an IP for %q - specify an IP directly? %v", getHost(*masterAddr), err)
46
-		}
47
-		if len(addrs) == 0 {
48
-			return nil, fmt.Errorf("Unable to find an IP for %q - specify an IP directly?", getHost(*masterAddr))
49
-		}
50
-		masterIP = addrs[0]
51
-	}
52
-
53
-	// in-order list of plug-ins that should intercept admission decisions
54
-	// TODO: add NamespaceExists
55
-	admissionControlPluginNames := []string{"LimitRanger", "ResourceQuota"}
56
-	admissionController := admission.NewFromPlugins(kubeClient, admissionControlPluginNames, "")
57
-
58
-	kmaster := &kubernetes.MasterConfig{
59
-		MasterIP:             masterIP,
60
-		MasterPort:           cfg.MasterAddr.Port,
61
-		NodeHosts:            cfg.NodeList,
62
-		PortalNet:            &portalNet,
63
-		RequestContextMapper: requestContextMapper,
64
-		EtcdHelper:           ketcdHelper,
65
-		KubeClient:           kubeClient,
66
-		Authorizer:           apiserver.NewAlwaysAllowAuthorizer(),
67
-		AdmissionControl:     admissionController,
68
-	}
69
-
70
-	return kmaster, nil
71
-}
... ...
@@ -2,26 +2,23 @@ package kubernetes
2 2
 
3 3
 import (
4 4
 	"fmt"
5
-	"net"
6 5
 	"time"
7 6
 
8
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
7
+	"github.com/emicklei/go-restful"
8
+	"github.com/golang/glog"
9
+
9 10
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
10 11
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
11
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer"
12 12
 	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
13 13
 	minionControllerPkg "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/controller"
14 14
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
15 15
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
16 16
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/resourcequota"
17 17
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/service"
18
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
19 18
 	kubeutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
20 19
 	"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler"
21 20
 	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/algorithmprovider"
22 21
 	"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory"
23
-	"github.com/emicklei/go-restful"
24
-	"github.com/golang/glog"
25 22
 )
26 23
 
27 24
 const (
... ...
@@ -31,22 +28,6 @@ const (
31 31
 	KubeAPIPrefixV1Beta3 = "/api/v1beta3"
32 32
 )
33 33
 
34
-// MasterConfig defines the required values to start a Kubernetes master
35
-type MasterConfig struct {
36
-	MasterIP   net.IP
37
-	MasterPort int
38
-	NodeHosts  []string
39
-	PortalNet  *net.IPNet
40
-
41
-	RequestContextMapper kapi.RequestContextMapper
42
-
43
-	EtcdHelper tools.EtcdHelper
44
-	KubeClient *kclient.Client
45
-
46
-	Authorizer       authorizer.Authorizer
47
-	AdmissionControl admission.Interface
48
-}
49
-
50 34
 // TODO: Longer term we should read this from some config store, rather than a flag.
51 35
 func (c *MasterConfig) EnsurePortalFlags() {
52 36
 	if c.PortalNet == nil {
53 37
new file mode 100644
... ...
@@ -0,0 +1,83 @@
0
+package kubernetes
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"net"
6
+	"strconv"
7
+
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
9
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
10
+	klatest "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
11
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
12
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/authorizer"
13
+	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
14
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/master"
15
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
16
+
17
+	"github.com/openshift/origin/pkg/cmd/flagtypes"
18
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
19
+	"github.com/openshift/origin/pkg/cmd/server/etcd"
20
+)
21
+
22
+// MasterConfig defines the required values to start a Kubernetes master
23
+type MasterConfig struct {
24
+	MasterIP   net.IP
25
+	MasterPort int
26
+	NodeHosts  []string
27
+	PortalNet  *net.IPNet
28
+
29
+	RequestContextMapper kapi.RequestContextMapper
30
+
31
+	EtcdHelper tools.EtcdHelper
32
+	KubeClient *kclient.Client
33
+
34
+	Authorizer       authorizer.Authorizer
35
+	AdmissionControl admission.Interface
36
+}
37
+
38
+func BuildKubernetesMasterConfig(options configapi.MasterConfig, requestContextMapper kapi.RequestContextMapper, kubeClient *kclient.Client) (*MasterConfig, error) {
39
+	if options.KubernetesMasterConfig == nil {
40
+		return nil, errors.New("insufficient information to build KubernetesMasterConfig")
41
+	}
42
+
43
+	// Connect and setup etcd interfaces
44
+	etcdClient, err := etcd.GetAndTestEtcdClient(options.EtcdClientInfo.URL)
45
+	if err != nil {
46
+		return nil, err
47
+	}
48
+	ketcdHelper, err := master.NewEtcdHelper(etcdClient, klatest.Version)
49
+	if err != nil {
50
+		return nil, fmt.Errorf("Error setting up Kubernetes server storage: %v", err)
51
+	}
52
+
53
+	portalNet := net.IPNet(flagtypes.DefaultIPNet(options.KubernetesMasterConfig.ServicesSubnet))
54
+
55
+	// in-order list of plug-ins that should intercept admission decisions
56
+	// TODO: add NamespaceExists
57
+	admissionControlPluginNames := []string{"LimitRanger", "ResourceQuota"}
58
+	admissionController := admission.NewFromPlugins(kubeClient, admissionControlPluginNames, "")
59
+
60
+	host, portString, err := net.SplitHostPort(options.ServingInfo.BindAddress)
61
+	if err != nil {
62
+		return nil, err
63
+	}
64
+	port, err := strconv.Atoi(portString)
65
+	if err != nil {
66
+		return nil, err
67
+	}
68
+
69
+	kmaster := &MasterConfig{
70
+		MasterIP:             net.ParseIP(host),
71
+		MasterPort:           port,
72
+		NodeHosts:            options.KubernetesMasterConfig.StaticNodeNames,
73
+		PortalNet:            &portalNet,
74
+		RequestContextMapper: requestContextMapper,
75
+		EtcdHelper:           ketcdHelper,
76
+		KubeClient:           kubeClient,
77
+		Authorizer:           apiserver.NewAlwaysAllowAuthorizer(),
78
+		AdmissionControl:     admissionController,
79
+	}
80
+
81
+	return kmaster, nil
82
+}
... ...
@@ -2,7 +2,6 @@ package kubernetes
2 2
 
3 3
 import (
4 4
 	"crypto/tls"
5
-	"crypto/x509"
6 5
 	"fmt"
7 6
 	"net"
8 7
 	"net/http"
... ...
@@ -10,11 +9,9 @@ import (
10 10
 	"os/exec"
11 11
 	"path/filepath"
12 12
 	"reflect"
13
-	"strconv"
14 13
 	"time"
15 14
 
16 15
 	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
17
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
18 16
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
19 17
 	kconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/config"
20 18
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
... ...
@@ -55,44 +52,6 @@ func (ce defaultCommandExecutor) Run(command string, args ...string) error {
55 55
 	return c.Run()
56 56
 }
57 57
 
58
-// NodeConfig represents the required parameters to start the OpenShift node
59
-// through Kubernetes. All fields are required.
60
-type NodeConfig struct {
61
-	// The address to bind to
62
-	BindHost string
63
-	// The name of this node that will be used to identify the node in the master.
64
-	// This value must match the value provided to the master on startup.
65
-	NodeHost string
66
-	// The host that the master can be reached at (not in use yet)
67
-	MasterHost string
68
-	// The directory that volumes will be stored under
69
-	VolumeDir string
70
-
71
-	ClusterDomain string
72
-	ClusterDNS    net.IP
73
-
74
-	// The image used as the Kubelet network namespace and volume container.
75
-	NetworkContainerImage string
76
-
77
-	// If true, the Kubelet will ignore errors from Docker
78
-	AllowDisabledDocker bool
79
-
80
-	// Whether to enable TLS serving
81
-	TLS bool
82
-
83
-	KubeletCertFile string
84
-	KubeletKeyFile  string
85
-
86
-	// ClientCAs will be used to request client certificates in connections to the node.
87
-	// This CertPool should contain all the CAs that will be used for client certificate verification.
88
-	ClientCAs *x509.CertPool
89
-
90
-	// A client to connect to the master.
91
-	Client *client.Client
92
-	// A client to connect to Docker
93
-	DockerClient dockertools.DockerInterface
94
-}
95
-
96 58
 // EnsureDocker attempts to connect to the Docker daemon defined by the helper,
97 59
 // and if it is unable to it will print a warning.
98 60
 func (c *NodeConfig) EnsureDocker(docker *dockerutil.Helper) {
... ...
@@ -186,7 +145,7 @@ func (c *NodeConfig) RunKubelet() {
186 186
 	handler := kubelet.NewServer(k, true)
187 187
 
188 188
 	server := &http.Server{
189
-		Addr:           net.JoinHostPort(c.BindHost, strconv.Itoa(NodePort)),
189
+		Addr:           c.BindAddress,
190 190
 		Handler:        &handler,
191 191
 		ReadTimeout:    5 * time.Minute,
192 192
 		WriteTimeout:   5 * time.Minute,
... ...
@@ -194,7 +153,7 @@ func (c *NodeConfig) RunKubelet() {
194 194
 	}
195 195
 
196 196
 	go util.Forever(func() {
197
-		glog.Infof("Started Kubelet for node %s, server at %s:%d", c.NodeHost, c.BindHost, NodePort)
197
+		glog.Infof("Started Kubelet for node %s, server at %s", c.NodeHost, c.BindAddress)
198 198
 		if clusterDNS != nil {
199 199
 			glog.Infof("  Kubelet is setting %s as a DNS nameserver for domain %q", clusterDNS, c.ClusterDomain)
200 200
 		}
... ...
@@ -245,9 +204,13 @@ func (c *NodeConfig) RunProxy() {
245 245
 	loadBalancer := proxy.NewLoadBalancerRR()
246 246
 	endpointsConfig.RegisterHandler(loadBalancer)
247 247
 
248
-	ip := net.ParseIP(c.BindHost)
248
+	host, _, err := net.SplitHostPort(c.BindAddress)
249
+	if err != nil {
250
+		glog.Fatalf("The provided value to bind to must be an ip:port %q", c.BindAddress)
251
+	}
252
+	ip := net.ParseIP(host)
249 253
 	if ip == nil {
250
-		glog.Fatalf("The provided value to bind to must be an IP: %q", c.BindHost)
254
+		glog.Fatalf("The provided value to bind to must be an ip:port: %q", c.BindAddress)
251 255
 	}
252 256
 
253 257
 	protocol := iptables.ProtocolIpv4
... ...
@@ -263,5 +226,5 @@ func (c *NodeConfig) RunProxy() {
263 263
 	}
264 264
 	serviceConfig.RegisterHandler(proxier)
265 265
 
266
-	glog.Infof("Started Kubernetes Proxy on %s", c.BindHost)
266
+	glog.Infof("Started Kubernetes Proxy on %s", host)
267 267
 }
268 268
new file mode 100644
... ...
@@ -0,0 +1,80 @@
0
+package kubernetes
1
+
2
+import (
3
+	"crypto/x509"
4
+	"fmt"
5
+	"net"
6
+
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
9
+
10
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
11
+)
12
+
13
+// NodeConfig represents the required parameters to start the OpenShift node
14
+// through Kubernetes. All fields are required.
15
+type NodeConfig struct {
16
+	// The address to bind to
17
+	BindAddress string
18
+	// The name of this node that will be used to identify the node in the master.
19
+	// This value must match the value provided to the master on startup.
20
+	NodeHost string
21
+	// The host that the master can be reached at (not in use yet)
22
+	MasterHost string
23
+	// The directory that volumes will be stored under
24
+	VolumeDir string
25
+
26
+	ClusterDomain string
27
+	ClusterDNS    net.IP
28
+
29
+	// The image used as the Kubelet network namespace and volume container.
30
+	NetworkContainerImage string
31
+
32
+	// If true, the Kubelet will ignore errors from Docker
33
+	AllowDisabledDocker bool
34
+
35
+	// Whether to enable TLS serving
36
+	TLS bool
37
+
38
+	KubeletCertFile string
39
+	KubeletKeyFile  string
40
+
41
+	// ClientCAs will be used to request client certificates in connections to the node.
42
+	// This CertPool should contain all the CAs that will be used for client certificate verification.
43
+	ClientCAs *x509.CertPool
44
+
45
+	// A client to connect to the master.
46
+	Client *client.Client
47
+	// A client to connect to Docker
48
+	DockerClient dockertools.DockerInterface
49
+}
50
+
51
+func BuildKubernetesNodeConfig(options configapi.NodeConfig) (*NodeConfig, error) {
52
+	kubeClient, _, err := configapi.GetKubeClient(options.MasterKubeConfig)
53
+	if err != nil {
54
+		return nil, err
55
+	}
56
+
57
+	var dnsIP net.IP
58
+	if len(options.DNSIP) > 0 {
59
+		dnsIP = net.ParseIP(options.DNSIP)
60
+		if dnsIP == nil {
61
+			return nil, fmt.Errorf("Invalid DNS IP: %s", options.DNSIP)
62
+		}
63
+	}
64
+
65
+	config := &NodeConfig{
66
+		NodeHost:    options.NodeName,
67
+		BindAddress: options.ServingInfo.BindAddress,
68
+
69
+		ClusterDomain: options.DNSDomain,
70
+		ClusterDNS:    dnsIP,
71
+
72
+		VolumeDir:             options.VolumeDirectory,
73
+		NetworkContainerImage: options.NetworkContainerImage,
74
+		AllowDisabledDocker:   options.AllowDisabledDocker,
75
+		Client:                kubeClient,
76
+	}
77
+
78
+	return config, nil
79
+}
... ...
@@ -11,12 +11,16 @@ import (
11 11
 )
12 12
 
13 13
 type ProxyConfig struct {
14
-	KubernetesAddr *url.URL
15
-	ClientConfig   *kclient.Config
14
+	ClientConfig *kclient.Config
16 15
 }
17 16
 
18 17
 func (c *ProxyConfig) InstallAPI(container *restful.Container) []string {
19
-	proxy, err := httpproxy.NewUpgradeAwareSingleHostReverseProxy(c.ClientConfig, c.KubernetesAddr)
18
+	kubeAddr, err := url.Parse(c.ClientConfig.Host)
19
+	if err != nil {
20
+		glog.Fatal(err)
21
+	}
22
+
23
+	proxy, err := httpproxy.NewUpgradeAwareSingleHostReverseProxy(c.ClientConfig, kubeAddr)
20 24
 	if err != nil {
21 25
 		glog.Fatalf("Unable to initialize the Kubernetes proxy: %v", err)
22 26
 	}
23 27
deleted file mode 100644
... ...
@@ -1,46 +0,0 @@
1
-package server
2
-
3
-import (
4
-	"net"
5
-
6
-	"github.com/openshift/origin/pkg/cmd/server/kubernetes"
7
-)
8
-
9
-func (cfg Config) BuildKubernetesNodeConfig() (*kubernetes.NodeConfig, error) {
10
-	kubernetesAddr, err := cfg.GetKubernetesAddress()
11
-	if err != nil {
12
-		return nil, err
13
-	}
14
-	kubeClient, _, err := cfg.GetKubeClient()
15
-	if err != nil {
16
-		return nil, err
17
-	}
18
-
19
-	dnsDomain := env("OPENSHIFT_DNS_DOMAIN", "local")
20
-	dnsIP := cfg.ClusterDNS
21
-	if clusterDNS := env("OPENSHIFT_DNS_ADDR", ""); len(clusterDNS) > 0 {
22
-		dnsIP = net.ParseIP(clusterDNS)
23
-	}
24
-
25
-	// define a function for resolving components to names
26
-	imageResolverFn := cfg.ImageTemplate.ExpandOrDie
27
-
28
-	nodeConfig := &kubernetes.NodeConfig{
29
-		BindHost:   cfg.BindAddr.Host,
30
-		NodeHost:   cfg.Hostname,
31
-		MasterHost: kubernetesAddr.String(),
32
-
33
-		ClusterDomain: dnsDomain,
34
-		ClusterDNS:    dnsIP,
35
-
36
-		VolumeDir: cfg.VolumeDir,
37
-
38
-		NetworkContainerImage: imageResolverFn("pod"),
39
-
40
-		AllowDisabledDocker: cfg.StartNode && cfg.StartMaster,
41
-
42
-		Client: kubeClient,
43
-	}
44
-
45
-	return nodeConfig, nil
46
-}
... ...
@@ -159,72 +159,6 @@ func ParseAuthRequestHandlerTypes(types string) []AuthRequestHandlerType {
159 159
 	return handlerTypes
160 160
 }
161 161
 
162
-type AuthConfig struct {
163
-	// URL to call internally during token request
164
-	MasterAddr string
165
-	// URL to direct browsers to the master on
166
-	MasterPublicAddr string
167
-	// Valid redirectURI prefixes to direct browsers to the web console
168
-	AssetPublicAddresses []string
169
-	MasterRoots          *x509.CertPool
170
-	EtcdHelper           tools.EtcdHelper
171
-
172
-	// Max age of authorize tokens
173
-	AuthorizeTokenMaxAgeSeconds int32
174
-	// Max age of access tokens
175
-	AccessTokenMaxAgeSeconds int32
176
-
177
-	// AuthRequestHandlers contains an ordered list of authenticators that decide if a request is authenticated
178
-	AuthRequestHandlers []AuthRequestHandlerType
179
-
180
-	// AuthHandler specifies what handles unauthenticated requests
181
-	AuthHandler AuthHandlerType
182
-
183
-	// GrantHandler specifies what handles requests for new client authorizations
184
-	GrantHandler GrantHandlerType
185
-
186
-	// PasswordAuth specifies how to validate username/passwords. Used by AuthRequestHandlerBasicAuth and AuthHandlerLogin
187
-	PasswordAuth PasswordAuthType
188
-	// BasicAuthURL specifies the remote URL to validate username/passwords against using basic auth. Used by PasswordAuthBasicAuthURL.
189
-	BasicAuthURL string
190
-	// HTPasswdFile specifies the path to an htpasswd file to validate username/passwords against. Used by PasswordAuthHTPasswd.
191
-	HTPasswdFile string
192
-
193
-	// TokenStore specifies how to validate bearer tokens. Used by AuthRequestHandlerBearer.
194
-	TokenStore TokenStoreType
195
-	// TokenFilePath is a path to a CSV file to load valid tokens from. Used by TokenStoreFile.
196
-	TokenFilePath string
197
-
198
-	// RequestHeaders lists the headers to check (in order) for a username. Used by AuthRequestHandlerRequestHeader
199
-	RequestHeaders []string
200
-	// RequestHeaderCAFile specifies the path to a PEM-encoded certificate bundle.
201
-	// If set, a client certificate must be presented and validate against the CA before the request headers are checked for usernames
202
-	RequestHeaderCAFile string
203
-
204
-	// SessionSecrets list the secret(s) to use to encrypt created sessions. Used by AuthRequestHandlerSession
205
-	SessionSecrets []string
206
-	// SessionMaxAgeSeconds specifies how long created sessions last. Used by AuthRequestHandlerSession
207
-	SessionMaxAgeSeconds int32
208
-	// SessionName is the cookie name used to store the session
209
-	SessionName string
210
-	// sessionAuth holds the Authenticator built from the exported Session* options. It should only be accessed via getSessionAuth(), since it is lazily built.
211
-	sessionAuth *session.Authenticator
212
-
213
-	// GoogleClientID is the client_id of a client registered with the Google OAuth provider.
214
-	// It must be authorized to redirect to {MasterPublicAddr}/oauth2callback/google
215
-	// Used by AuthHandlerGoogle
216
-	GoogleClientID string
217
-	// GoogleClientID is the client_secret of a client registered with the Google OAuth provider.
218
-	GoogleClientSecret string
219
-
220
-	// GithubClientID is the client_id of a client registered with the GitHub OAuth provider.
221
-	// It must be authorized to redirect to {MasterPublicAddr}/oauth2callback/github
222
-	// Used by AuthHandlerGithub
223
-	GithubClientID string
224
-	// GithubClientID is the client_secret of a client registered with the GitHub OAuth provider.
225
-	GithubClientSecret string
226
-}
227
-
228 162
 // InstallSupport registers endpoints for an OAuth2 server into the provided mux,
229 163
 // then returns an array of strings indicating what endpoints were started
230 164
 // (these are format strings that will expect to be sent a single string value).
231 165
new file mode 100644
... ...
@@ -0,0 +1,144 @@
0
+package origin
1
+
2
+import (
3
+	"crypto/x509"
4
+	"fmt"
5
+	"strings"
6
+
7
+	"code.google.com/p/go-uuid/uuid"
8
+
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
10
+
11
+	"github.com/openshift/origin/pkg/auth/server/session"
12
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
13
+	"github.com/openshift/origin/pkg/cmd/server/etcd"
14
+	cmdutil "github.com/openshift/origin/pkg/cmd/util"
15
+)
16
+
17
+type AuthConfig struct {
18
+	// URL to call internally during token request
19
+	MasterAddr string
20
+	// URL to direct browsers to the master on
21
+	MasterPublicAddr string
22
+	// Valid redirectURI prefixes to direct browsers to the web console
23
+	AssetPublicAddresses []string
24
+	MasterRoots          *x509.CertPool
25
+	EtcdHelper           tools.EtcdHelper
26
+
27
+	// Max age of authorize tokens
28
+	AuthorizeTokenMaxAgeSeconds int32
29
+	// Max age of access tokens
30
+	AccessTokenMaxAgeSeconds int32
31
+
32
+	// AuthRequestHandlers contains an ordered list of authenticators that decide if a request is authenticated
33
+	AuthRequestHandlers []AuthRequestHandlerType
34
+
35
+	// AuthHandler specifies what handles unauthenticated requests
36
+	AuthHandler AuthHandlerType
37
+
38
+	// GrantHandler specifies what handles requests for new client authorizations
39
+	GrantHandler GrantHandlerType
40
+
41
+	// PasswordAuth specifies how to validate username/passwords. Used by AuthRequestHandlerBasicAuth and AuthHandlerLogin
42
+	PasswordAuth PasswordAuthType
43
+	// BasicAuthURL specifies the remote URL to validate username/passwords against using basic auth. Used by PasswordAuthBasicAuthURL.
44
+	BasicAuthURL string
45
+	// HTPasswdFile specifies the path to an htpasswd file to validate username/passwords against. Used by PasswordAuthHTPasswd.
46
+	HTPasswdFile string
47
+
48
+	// TokenStore specifies how to validate bearer tokens. Used by AuthRequestHandlerBearer.
49
+	TokenStore TokenStoreType
50
+	// TokenFilePath is a path to a CSV file to load valid tokens from. Used by TokenStoreFile.
51
+	TokenFilePath string
52
+
53
+	// RequestHeaders lists the headers to check (in order) for a username. Used by AuthRequestHandlerRequestHeader
54
+	RequestHeaders []string
55
+	// RequestHeaderCAFile specifies the path to a PEM-encoded certificate bundle.
56
+	// If set, a client certificate must be presented and validate against the CA before the request headers are checked for usernames
57
+	RequestHeaderCAFile string
58
+
59
+	// SessionSecrets list the secret(s) to use to encrypt created sessions. Used by AuthRequestHandlerSession
60
+	SessionSecrets []string
61
+	// SessionMaxAgeSeconds specifies how long created sessions last. Used by AuthRequestHandlerSession
62
+	SessionMaxAgeSeconds int32
63
+	// SessionName is the cookie name used to store the session
64
+	SessionName string
65
+	// sessionAuth holds the Authenticator built from the exported Session* options. It should only be accessed via getSessionAuth(), since it is lazily built.
66
+	sessionAuth *session.Authenticator
67
+
68
+	// GoogleClientID is the client_id of a client registered with the Google OAuth provider.
69
+	// It must be authorized to redirect to {MasterPublicAddr}/oauth2callback/google
70
+	// Used by AuthHandlerGoogle
71
+	GoogleClientID string
72
+	// GoogleClientID is the client_secret of a client registered with the Google OAuth provider.
73
+	GoogleClientSecret string
74
+
75
+	// GithubClientID is the client_id of a client registered with the GitHub OAuth provider.
76
+	// It must be authorized to redirect to {MasterPublicAddr}/oauth2callback/github
77
+	// Used by AuthHandlerGithub
78
+	GithubClientID string
79
+	// GithubClientID is the client_secret of a client registered with the GitHub OAuth provider.
80
+	GithubClientSecret string
81
+}
82
+
83
+func BuildAuthConfig(options configapi.MasterConfig) (*AuthConfig, error) {
84
+	etcdHelper, err := etcd.NewOpenShiftEtcdHelper(options.EtcdClientInfo.URL)
85
+	if err != nil {
86
+		return nil, fmt.Errorf("Error setting up server storage: %v", err)
87
+	}
88
+
89
+	apiServerCAs, err := configapi.GetAPIServerCertCAPool(options)
90
+	if err != nil {
91
+		return nil, err
92
+	}
93
+
94
+	// Build the list of valid redirect_uri prefixes for a login using the openshift-web-console client to redirect to
95
+	// TODO: allow configuring this
96
+	// TODO: remove hard-coding of development UI server
97
+	assetPublicURLs := []string{options.OAuthConfig.AssetPublicURL, "http://localhost:9000", "https://localhost:9000"}
98
+
99
+	// Default to a session authenticator (for browsers), and a basicauth authenticator (for clients responding to WWW-Authenticate challenges)
100
+	defaultAuthRequestHandlers := strings.Join([]string{
101
+		string(AuthRequestHandlerSession),
102
+		string(AuthRequestHandlerBasicAuth),
103
+	}, ",")
104
+
105
+	ret := &AuthConfig{
106
+		MasterAddr:           options.OAuthConfig.MasterURL,
107
+		MasterPublicAddr:     options.OAuthConfig.MasterPublicURL,
108
+		AssetPublicAddresses: assetPublicURLs,
109
+		MasterRoots:          apiServerCAs,
110
+		EtcdHelper:           etcdHelper,
111
+
112
+		// Max token ages
113
+		AuthorizeTokenMaxAgeSeconds: cmdutil.EnvInt("OPENSHIFT_OAUTH_AUTHORIZE_TOKEN_MAX_AGE_SECONDS", 300, 1),
114
+		AccessTokenMaxAgeSeconds:    cmdutil.EnvInt("OPENSHIFT_OAUTH_ACCESS_TOKEN_MAX_AGE_SECONDS", 3600, 1),
115
+		// Handlers
116
+		AuthRequestHandlers: ParseAuthRequestHandlerTypes(cmdutil.Env("OPENSHIFT_OAUTH_REQUEST_HANDLERS", defaultAuthRequestHandlers)),
117
+		AuthHandler:         AuthHandlerType(cmdutil.Env("OPENSHIFT_OAUTH_HANDLER", string(AuthHandlerLogin))),
118
+		GrantHandler:        GrantHandlerType(cmdutil.Env("OPENSHIFT_OAUTH_GRANT_HANDLER", string(GrantHandlerAuto))),
119
+		// RequestHeader config
120
+		RequestHeaders:      strings.Split(cmdutil.Env("OPENSHIFT_OAUTH_REQUEST_HEADERS", "X-Remote-User"), ","),
121
+		RequestHeaderCAFile: options.OAuthConfig.ProxyCA,
122
+		// Session config (default to unknowable secret)
123
+		SessionSecrets:       []string{cmdutil.Env("OPENSHIFT_OAUTH_SESSION_SECRET", uuid.NewUUID().String())},
124
+		SessionMaxAgeSeconds: cmdutil.EnvInt("OPENSHIFT_OAUTH_SESSION_MAX_AGE_SECONDS", 300, 1),
125
+		SessionName:          cmdutil.Env("OPENSHIFT_OAUTH_SESSION_NAME", "ssn"),
126
+		// Password config
127
+		PasswordAuth: PasswordAuthType(cmdutil.Env("OPENSHIFT_OAUTH_PASSWORD_AUTH", string(PasswordAuthAnyPassword))),
128
+		BasicAuthURL: cmdutil.Env("OPENSHIFT_OAUTH_BASIC_AUTH_URL", ""),
129
+		HTPasswdFile: cmdutil.Env("OPENSHIFT_OAUTH_HTPASSWD_FILE", ""),
130
+		// Token config
131
+		TokenStore:    TokenStoreType(cmdutil.Env("OPENSHIFT_OAUTH_TOKEN_STORE", string(TokenStoreOAuth))),
132
+		TokenFilePath: cmdutil.Env("OPENSHIFT_OAUTH_TOKEN_FILE_PATH", ""),
133
+		// Google config
134
+		GoogleClientID:     cmdutil.Env("OPENSHIFT_OAUTH_GOOGLE_CLIENT_ID", ""),
135
+		GoogleClientSecret: cmdutil.Env("OPENSHIFT_OAUTH_GOOGLE_CLIENT_SECRET", ""),
136
+		// GitHub config
137
+		GithubClientID:     cmdutil.Env("OPENSHIFT_OAUTH_GITHUB_CLIENT_ID", ""),
138
+		GithubClientSecret: cmdutil.Env("OPENSHIFT_OAUTH_GITHUB_CLIENT_SECRET", ""),
139
+	}
140
+
141
+	return ret, nil
142
+
143
+}
0 144
deleted file mode 100644
... ...
@@ -1,212 +0,0 @@
1
-package origin
2
-
3
-import (
4
-	"crypto/x509"
5
-	"net/http"
6
-	"strings"
7
-
8
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
9
-	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
10
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
11
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
12
-	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
13
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
14
-	kutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
15
-
16
-	"github.com/openshift/origin/pkg/api/latest"
17
-	"github.com/openshift/origin/pkg/auth/authenticator"
18
-	"github.com/openshift/origin/pkg/auth/authenticator/request/bearertoken"
19
-	"github.com/openshift/origin/pkg/auth/authenticator/request/paramtoken"
20
-	"github.com/openshift/origin/pkg/auth/authenticator/request/unionrequest"
21
-	"github.com/openshift/origin/pkg/auth/authenticator/request/x509request"
22
-	"github.com/openshift/origin/pkg/auth/group"
23
-	authnregistry "github.com/openshift/origin/pkg/auth/oauth/registry"
24
-	"github.com/openshift/origin/pkg/authorization/authorizer"
25
-	policycache "github.com/openshift/origin/pkg/authorization/cache"
26
-	authorizationetcd "github.com/openshift/origin/pkg/authorization/registry/etcd"
27
-	"github.com/openshift/origin/pkg/authorization/rulevalidation"
28
-	osclient "github.com/openshift/origin/pkg/client"
29
-	oauthetcd "github.com/openshift/origin/pkg/oauth/registry/etcd"
30
-	projectauth "github.com/openshift/origin/pkg/project/auth"
31
-)
32
-
33
-const (
34
-	unauthenticatedUsername = "system:anonymous"
35
-
36
-	authenticatedGroup   = "system:authenticated"
37
-	unauthenticatedGroup = "system:unauthenticated"
38
-)
39
-
40
-type MasterConfigParameters struct {
41
-	// host:port to bind master to
42
-	MasterBindAddr string
43
-	// host:port to bind asset server to
44
-	AssetBindAddr string
45
-	// url to access the master API on within the cluster
46
-	MasterAddr string
47
-	// url to access kubernetes API on within the cluster
48
-	KubernetesAddr string
49
-	// external clients may need to access APIs at different addresses than internal components do
50
-	MasterPublicAddr     string
51
-	KubernetesPublicAddr string
52
-	AssetPublicAddr      string
53
-	// host:port to bind DNS on. The default is port 53.
54
-	DNSBindAddr string
55
-	// LogoutURI is an optional, absolute URI to redirect web browsers to after logging out of the web console.
56
-	// If not specified, the built-in logout page is shown.
57
-	LogoutURI string
58
-
59
-	CORSAllowedOrigins []string
60
-
61
-	EtcdHelper tools.EtcdHelper
62
-
63
-	MasterCertFile string
64
-	MasterKeyFile  string
65
-	AssetCertFile  string
66
-	AssetKeyFile   string
67
-
68
-	// ClientCAs will be used to request client certificates in connections to the API or OAuth server.
69
-	// This CertPool should contain all the CAs that will be used for client certificate verification (the union
70
-	// of APIClientCAs and OAuthClientCAs).
71
-	ClientCAs *x509.CertPool
72
-	// APIClientCAs is used to verify client certificates presented for API auth
73
-	APIClientCAs *x509.CertPool
74
-
75
-	MasterAuthorizationNamespace      string
76
-	OpenshiftSharedResourcesNamespace string
77
-
78
-	// a function that returns the appropriate image to use for a named component
79
-	ImageFor func(component string) string
80
-
81
-	// kubeClient is the client used to call Kubernetes APIs from system components, built from KubeClientConfig.
82
-	// It should only be accessed via the *Client() helper methods.
83
-	// To apply different access control to a system component, create a separate client/config specifically for that component.
84
-	KubeClient *kclient.Client
85
-	// KubeClientConfig is the client configuration used to call Kubernetes APIs from system components.
86
-	// To apply different access control to a system component, create a client config specifically for that component.
87
-	KubeClientConfig kclient.Config
88
-
89
-	// osClient is the client used to call OpenShift APIs from system components, built from OSClientConfig.
90
-	// It should only be accessed via the *Client() helper methods.
91
-	// To apply different access control to a system component, create a separate client/config specifically for that component.
92
-	OSClient *osclient.Client
93
-	// OSClientConfig is the client configuration used to call OpenShift APIs from system components
94
-	// To apply different access control to a system component, create a client config specifically for that component.
95
-	OSClientConfig kclient.Config
96
-
97
-	// DeployerOSClientConfig is the client configuration used to call OpenShift APIs from launched deployer pods
98
-	DeployerOSClientConfig kclient.Config
99
-}
100
-
101
-// MasterConfig defines the required parameters for starting the OpenShift master
102
-type MasterConfig struct {
103
-	MasterConfigParameters
104
-
105
-	Authenticator                 authenticator.Request
106
-	Authorizer                    authorizer.Authorizer
107
-	AuthorizationAttributeBuilder authorizer.AuthorizationAttributeBuilder
108
-
109
-	PolicyCache               *policycache.PolicyCache
110
-	ProjectAuthorizationCache *projectauth.AuthorizationCache
111
-
112
-	// Map requests to contexts
113
-	RequestContextMapper kapi.RequestContextMapper
114
-
115
-	AdmissionControl admission.Interface
116
-
117
-	TLS bool
118
-}
119
-
120
-func BuildMasterConfig(configParams MasterConfigParameters) (*MasterConfig, error) {
121
-
122
-	policyCache := configParams.newPolicyCache()
123
-	requestContextMapper := kapi.NewRequestContextMapper()
124
-
125
-	// in-order list of plug-ins that should intercept admission decisions (origin only intercepts)
126
-	admissionControlPluginNames := []string{"AlwaysAdmit"}
127
-	admissionController := admission.NewFromPlugins(configParams.KubeClient, admissionControlPluginNames, "")
128
-
129
-	config := &MasterConfig{
130
-		MasterConfigParameters: configParams,
131
-
132
-		Authenticator:                 configParams.newAuthenticator(),
133
-		Authorizer:                    newAuthorizer(policyCache, configParams.MasterAuthorizationNamespace),
134
-		AuthorizationAttributeBuilder: newAuthorizationAttributeBuilder(requestContextMapper),
135
-
136
-		PolicyCache:               policyCache,
137
-		ProjectAuthorizationCache: configParams.newProjectAuthorizationCache(),
138
-
139
-		RequestContextMapper: requestContextMapper,
140
-
141
-		AdmissionControl: admissionController,
142
-
143
-		TLS: strings.HasPrefix(configParams.MasterAddr, "https://"),
144
-	}
145
-
146
-	return config, nil
147
-}
148
-
149
-func (c MasterConfigParameters) newAuthenticator() authenticator.Request {
150
-	useTLS := strings.HasPrefix(c.MasterAddr, "https://")
151
-
152
-	tokenAuthenticator := getEtcdTokenAuthenticator(c.EtcdHelper)
153
-
154
-	authenticators := []authenticator.Request{}
155
-	authenticators = append(authenticators, bearertoken.New(tokenAuthenticator, true))
156
-	// Allow token as access_token param for WebSockets
157
-	// TODO: make the param name configurable
158
-	// TODO: limit this authenticator to watch methods, if possible
159
-	// TODO: prevent access_token param from getting logged, if possible
160
-	authenticators = append(authenticators, paramtoken.New("access_token", tokenAuthenticator, true))
161
-
162
-	if useTLS {
163
-		// build cert authenticator
164
-		// TODO: add cert users to etcd?
165
-		opts := x509request.DefaultVerifyOptions()
166
-		opts.Roots = c.APIClientCAs
167
-		certauth := x509request.New(opts, x509request.SubjectToUserConversion)
168
-		authenticators = append(authenticators, certauth)
169
-	}
170
-
171
-	// TODO: make anonymous auth optional?
172
-	ret := &unionrequest.Authenticator{
173
-		FailOnError: true,
174
-		Handlers: []authenticator.Request{
175
-			group.NewGroupAdder(unionrequest.NewUnionAuthentication(authenticators...), []string{authenticatedGroup}),
176
-			authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) {
177
-				return &user.DefaultInfo{Name: unauthenticatedUsername, Groups: []string{unauthenticatedGroup}}, true, nil
178
-			}),
179
-		},
180
-	}
181
-
182
-	return ret
183
-}
184
-
185
-func (c MasterConfigParameters) newProjectAuthorizationCache() *projectauth.AuthorizationCache {
186
-	return projectauth.NewAuthorizationCache(
187
-		projectauth.NewReviewer(c.OSClient),
188
-		c.KubeClient.Namespaces(),
189
-		c.OSClient,
190
-		c.OSClient,
191
-		c.MasterAuthorizationNamespace)
192
-}
193
-
194
-func (c MasterConfigParameters) newPolicyCache() *policycache.PolicyCache {
195
-	authorizationEtcd := authorizationetcd.New(c.EtcdHelper)
196
-	return policycache.NewPolicyCache(authorizationEtcd, authorizationEtcd)
197
-}
198
-
199
-func newAuthorizer(policyCache *policycache.PolicyCache, masterAuthorizationNamespace string) authorizer.Authorizer {
200
-	authorizer := authorizer.NewAuthorizer(masterAuthorizationNamespace, rulevalidation.NewDefaultRuleResolver(policyCache, policyCache))
201
-	return authorizer
202
-}
203
-
204
-func newAuthorizationAttributeBuilder(requestContextMapper kapi.RequestContextMapper) authorizer.AuthorizationAttributeBuilder {
205
-	authorizationAttributeBuilder := authorizer.NewAuthorizationAttributeBuilder(requestContextMapper, &apiserver.APIRequestInfoResolver{kutil.NewStringSet("api", "osapi"), latest.RESTMapper})
206
-	return authorizationAttributeBuilder
207
-}
208
-
209
-func getEtcdTokenAuthenticator(etcdHelper tools.EtcdHelper) authenticator.Token {
210
-	oauthRegistry := oauthetcd.New(etcdHelper)
211
-	return authnregistry.NewTokenAuthenticator(oauthRegistry)
212
-}
... ...
@@ -86,6 +86,7 @@ import (
86 86
 	roleregistry "github.com/openshift/origin/pkg/authorization/registry/role"
87 87
 	rolebindingregistry "github.com/openshift/origin/pkg/authorization/registry/rolebinding"
88 88
 	subjectaccessreviewregistry "github.com/openshift/origin/pkg/authorization/registry/subjectaccessreview"
89
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
89 90
 	"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
90 91
 	routeplugin "github.com/openshift/origin/plugins/route/allocation/simple"
91 92
 )
... ...
@@ -114,7 +115,7 @@ func (fn APIInstallFunc) InstallAPI(container *restful.Container) []string {
114 114
 
115 115
 // KubeClient returns the kubernetes client object
116 116
 func (c *MasterConfig) KubeClient() *kclient.Client {
117
-	return c.MasterConfigParameters.KubeClient
117
+	return c.KubernetesClient
118 118
 }
119 119
 
120 120
 // PolicyClient returns the policy client object
... ...
@@ -128,19 +129,19 @@ func (c *MasterConfig) PolicyClient() *osclient.Client {
128 128
 
129 129
 // DeploymentClient returns the deployment client object
130 130
 func (c *MasterConfig) DeploymentClient() *kclient.Client {
131
-	return c.MasterConfigParameters.KubeClient
131
+	return c.KubernetesClient
132 132
 }
133 133
 
134 134
 // DNSServerClient returns the DNS server client object
135 135
 // It must have the following capabilities:
136 136
 //   list, watch all services in all namespaces
137 137
 func (c *MasterConfig) DNSServerClient() *kclient.Client {
138
-	return c.MasterConfigParameters.KubeClient
138
+	return c.KubernetesClient
139 139
 }
140 140
 
141 141
 // BuildLogClient returns the build log client object
142 142
 func (c *MasterConfig) BuildLogClient() *kclient.Client {
143
-	return c.MasterConfigParameters.KubeClient
143
+	return c.KubernetesClient
144 144
 }
145 145
 
146 146
 // WebHookClient returns the webhook client object
... ...
@@ -150,7 +151,7 @@ func (c *MasterConfig) WebHookClient() *osclient.Client {
150 150
 
151 151
 // BuildControllerClients returns the build controller client objects
152 152
 func (c *MasterConfig) BuildControllerClients() (*osclient.Client, *kclient.Client) {
153
-	return c.OSClient, c.MasterConfigParameters.KubeClient
153
+	return c.OSClient, c.KubernetesClient
154 154
 }
155 155
 
156 156
 // ImageChangeControllerClient returns the openshift client object
... ...
@@ -160,7 +161,7 @@ func (c *MasterConfig) ImageChangeControllerClient() *osclient.Client {
160 160
 
161 161
 // DeploymentControllerClients returns the deployment controller client object
162 162
 func (c *MasterConfig) DeploymentControllerClients() (*osclient.Client, *kclient.Client) {
163
-	return c.OSClient, c.MasterConfigParameters.KubeClient
163
+	return c.OSClient, c.KubernetesClient
164 164
 }
165 165
 
166 166
 // DeployerClientConfig returns the client configuration a Deployer instance launched in a pod
... ...
@@ -170,10 +171,10 @@ func (c *MasterConfig) DeployerClientConfig() *kclient.Config {
170 170
 }
171 171
 
172 172
 func (c *MasterConfig) DeploymentConfigControllerClients() (*osclient.Client, *kclient.Client) {
173
-	return c.OSClient, c.MasterConfigParameters.KubeClient
173
+	return c.OSClient, c.KubernetesClient
174 174
 }
175 175
 func (c *MasterConfig) DeploymentConfigChangeControllerClients() (*osclient.Client, *kclient.Client) {
176
-	return c.OSClient, c.MasterConfigParameters.KubeClient
176
+	return c.OSClient, c.KubernetesClient
177 177
 }
178 178
 func (c *MasterConfig) DeploymentImageChangeControllerClient() *osclient.Client {
179 179
 	return c.OSClient
... ...
@@ -258,7 +259,7 @@ func (c *MasterConfig) InstallProtectedAPI(container *restful.Container) []strin
258 258
 		"policies":              policyregistry.NewREST(authorizationEtcd),
259 259
 		"policyBindings":        policybindingregistry.NewREST(authorizationEtcd),
260 260
 		"roles":                 roleregistry.NewREST(roleregistry.NewVirtualRegistry(authorizationEtcd)),
261
-		"roleBindings":          rolebindingregistry.NewREST(rolebindingregistry.NewVirtualRegistry(authorizationEtcd, authorizationEtcd, c.MasterAuthorizationNamespace)),
261
+		"roleBindings":          rolebindingregistry.NewREST(rolebindingregistry.NewVirtualRegistry(authorizationEtcd, authorizationEtcd, c.Options.MasterAuthorizationNamespace)),
262 262
 		"resourceAccessReviews": resourceaccessreviewregistry.NewREST(c.Authorizer),
263 263
 		"subjectAccessReviews":  subjectaccessreviewregistry.NewREST(c.Authorizer),
264 264
 	}
... ...
@@ -365,7 +366,7 @@ func (c *MasterConfig) Run(protected []APIInstaller, unprotected []APIInstaller)
365 365
 	}
366 366
 
367 367
 	server := &http.Server{
368
-		Addr:           c.MasterBindAddr,
368
+		Addr:           c.Options.ServingInfo.BindAddress,
369 369
 		Handler:        handler,
370 370
 		ReadTimeout:    5 * time.Minute,
371 371
 		WriteTimeout:   5 * time.Minute,
... ...
@@ -374,7 +375,7 @@ func (c *MasterConfig) Run(protected []APIInstaller, unprotected []APIInstaller)
374 374
 
375 375
 	go util.Forever(func() {
376 376
 		for _, s := range extra {
377
-			glog.Infof(s, c.MasterAddr)
377
+			glog.Infof(s, c.Options.ServingInfo.BindAddress)
378 378
 		}
379 379
 		if c.TLS {
380 380
 			server.TLSConfig = &tls.Config{
... ...
@@ -385,14 +386,14 @@ func (c *MasterConfig) Run(protected []APIInstaller, unprotected []APIInstaller)
385 385
 				ClientAuth: tls.RequestClientCert,
386 386
 				ClientCAs:  c.ClientCAs,
387 387
 			}
388
-			glog.Fatal(server.ListenAndServeTLS(c.MasterCertFile, c.MasterKeyFile))
388
+			glog.Fatal(server.ListenAndServeTLS(c.Options.ServingInfo.ServerCert.CertFile, c.Options.ServingInfo.ServerCert.KeyFile))
389 389
 		} else {
390 390
 			glog.Fatal(server.ListenAndServe())
391 391
 		}
392 392
 	}, 0)
393 393
 
394 394
 	// Attempt to verify the server came up for 20 seconds (100 tries * 100ms, 100ms timeout per try)
395
-	cmdutil.WaitForSuccessfulDial(c.TLS, "tcp", c.MasterBindAddr, 100*time.Millisecond, 100*time.Millisecond, 100)
395
+	cmdutil.WaitForSuccessfulDial(c.TLS, "tcp", c.Options.ServingInfo.BindAddress, 100*time.Millisecond, 100*time.Millisecond, 100)
396 396
 
397 397
 	// Attempt to create the required policy rules now, and then stick in a forever loop to make sure they are always available
398 398
 	c.ensureComponentAuthorizationRules()
... ...
@@ -415,9 +416,9 @@ func (c *MasterConfig) getRequestContextMapper() kapi.RequestContextMapper {
415 415
 func (c *MasterConfig) ensureMasterAuthorizationNamespace() {
416 416
 
417 417
 	// ensure that master namespace actually exists
418
-	namespace, err := c.KubeClient().Namespaces().Get(c.MasterAuthorizationNamespace)
418
+	namespace, err := c.KubeClient().Namespaces().Get(c.Options.MasterAuthorizationNamespace)
419 419
 	if err != nil {
420
-		namespace = &kapi.Namespace{ObjectMeta: kapi.ObjectMeta{Name: c.MasterAuthorizationNamespace}}
420
+		namespace = &kapi.Namespace{ObjectMeta: kapi.ObjectMeta{Name: c.Options.MasterAuthorizationNamespace}}
421 421
 		kapi.FillObjectMetaSystemFields(api.NewContext(), &namespace.ObjectMeta)
422 422
 		_, err = c.KubeClient().Namespaces().Create(namespace)
423 423
 		if err != nil {
... ...
@@ -431,7 +432,7 @@ func (c *MasterConfig) ensureComponentAuthorizationRules() {
431 431
 	registry := authorizationetcd.New(c.EtcdHelper)
432 432
 
433 433
 	roleRegistry := roleregistry.NewVirtualRegistry(registry)
434
-	for _, role := range bootstrappolicy.GetBootstrapRoles(c.MasterAuthorizationNamespace, c.OpenshiftSharedResourcesNamespace) {
434
+	for _, role := range bootstrappolicy.GetBootstrapRoles(c.Options.MasterAuthorizationNamespace, c.Options.OpenShiftSharedResourcesNamespace) {
435 435
 		ctx := kapi.WithNamespace(kapi.NewContext(), role.Namespace)
436 436
 
437 437
 		if _, err := roleRegistry.GetRole(ctx, role.Name); kapierror.IsNotFound(err) {
... ...
@@ -444,13 +445,13 @@ func (c *MasterConfig) ensureComponentAuthorizationRules() {
444 444
 		}
445 445
 	}
446 446
 
447
-	roleBindingRegistry := rolebindingregistry.NewVirtualRegistry(registry, registry, c.MasterAuthorizationNamespace)
448
-	for _, roleBinding := range bootstrappolicy.GetBootstrapRoleBindings(c.MasterAuthorizationNamespace, c.OpenshiftSharedResourcesNamespace) {
447
+	roleBindingRegistry := rolebindingregistry.NewVirtualRegistry(registry, registry, c.Options.MasterAuthorizationNamespace)
448
+	for _, roleBinding := range bootstrappolicy.GetBootstrapRoleBindings(c.Options.MasterAuthorizationNamespace, c.Options.OpenShiftSharedResourcesNamespace) {
449 449
 		ctx := kapi.WithNamespace(kapi.NewContext(), roleBinding.Namespace)
450 450
 
451 451
 		if _, err := roleBindingRegistry.GetRoleBinding(ctx, roleBinding.Name); kapierror.IsNotFound(err) {
452 452
 			// if this is a binding for a non-master namespaced role.  That means that the policy binding must be provisioned
453
-			if roleBinding.RoleRef.Namespace != c.MasterAuthorizationNamespace {
453
+			if roleBinding.RoleRef.Namespace != c.Options.MasterAuthorizationNamespace {
454 454
 				policyBindingName := roleBinding.RoleRef.Namespace
455 455
 				if _, err := registry.GetPolicyBinding(ctx, policyBindingName); kapierror.IsNotFound(err) {
456 456
 					policyBinding := &authorizationapi.PolicyBinding{
... ...
@@ -540,12 +541,12 @@ func (c *MasterConfig) RunAssetServer() {
540 540
 	// TODO use	version.Get().GitCommit as an etag cache header
541 541
 	mux := http.NewServeMux()
542 542
 
543
-	masterURL, err := url.Parse(c.MasterPublicAddr)
543
+	masterURL, err := url.Parse(c.Options.AssetConfig.MasterPublicURL)
544 544
 	if err != nil {
545 545
 		glog.Fatalf("Error parsing master url: %v", err)
546 546
 	}
547 547
 
548
-	k8sURL, err := url.Parse(c.KubernetesPublicAddr)
548
+	k8sURL, err := url.Parse(c.Options.AssetConfig.KubernetesPublicURL)
549 549
 	if err != nil {
550 550
 		glog.Fatalf("Error parsing kubernetes url: %v", err)
551 551
 	}
... ...
@@ -556,9 +557,9 @@ func (c *MasterConfig) RunAssetServer() {
556 556
 		KubernetesAddr:    k8sURL.Host,
557 557
 		KubernetesPrefix:  "/api",
558 558
 		OAuthAuthorizeURI: OpenShiftOAuthAuthorizeURL(masterURL.String()),
559
-		OAuthRedirectBase: c.AssetPublicAddr,
559
+		OAuthRedirectBase: c.Options.AssetConfig.PublicURL,
560 560
 		OAuthClientID:     OpenShiftWebConsoleClientID,
561
-		LogoutURI:         c.LogoutURI,
561
+		LogoutURI:         c.Options.AssetConfig.LogoutURI,
562 562
 	}
563 563
 
564 564
 	assets.RegisterMimeTypes()
... ...
@@ -588,7 +589,7 @@ func (c *MasterConfig) RunAssetServer() {
588 588
 	)
589 589
 
590 590
 	server := &http.Server{
591
-		Addr:           c.AssetBindAddr,
591
+		Addr:           c.Options.AssetConfig.ServingInfo.BindAddress,
592 592
 		Handler:        mux,
593 593
 		ReadTimeout:    5 * time.Minute,
594 594
 		WriteTimeout:   5 * time.Minute,
... ...
@@ -601,18 +602,18 @@ func (c *MasterConfig) RunAssetServer() {
601 601
 				// Change default from SSLv3 to TLSv1.0 (because of POODLE vulnerability)
602 602
 				MinVersion: tls.VersionTLS10,
603 603
 			}
604
-			glog.Infof("OpenShift UI listening at https://%s", c.AssetBindAddr)
605
-			glog.Fatal(server.ListenAndServeTLS(c.AssetCertFile, c.AssetKeyFile))
604
+			glog.Infof("OpenShift UI listening at https://%s", c.Options.AssetConfig.ServingInfo.BindAddress)
605
+			glog.Fatal(server.ListenAndServeTLS(c.Options.AssetConfig.ServingInfo.ServerCert.CertFile, c.Options.AssetConfig.ServingInfo.ServerCert.KeyFile))
606 606
 		} else {
607
-			glog.Infof("OpenShift UI listening at https://%s", c.AssetBindAddr)
607
+			glog.Infof("OpenShift UI listening at https://%s", c.Options.AssetConfig.ServingInfo.BindAddress)
608 608
 			glog.Fatal(server.ListenAndServe())
609 609
 		}
610 610
 	}, 0)
611 611
 
612 612
 	// Attempt to verify the server came up for 20 seconds (100 tries * 100ms, 100ms timeout per try)
613
-	cmdutil.WaitForSuccessfulDial(c.TLS, "tcp", c.AssetBindAddr, 100*time.Millisecond, 100*time.Millisecond, 100)
613
+	cmdutil.WaitForSuccessfulDial(c.TLS, "tcp", c.Options.AssetConfig.ServingInfo.BindAddress, 100*time.Millisecond, 100*time.Millisecond, 100)
614 614
 
615
-	glog.Infof("OpenShift UI available at %s", c.AssetPublicAddr)
615
+	glog.Infof("OpenShift UI available at %s", c.Options.AssetConfig.PublicURL)
616 616
 }
617 617
 
618 618
 func (c *MasterConfig) RunDNSServer() {
... ...
@@ -620,22 +621,29 @@ func (c *MasterConfig) RunDNSServer() {
620 620
 	if err != nil {
621 621
 		glog.Fatalf("Could not start DNS: %v", err)
622 622
 	}
623
+	config.DnsAddr = c.Options.DNSConfig.BindAddress
623 624
 
624
-	if _, port, err := net.SplitHostPort(c.DNSBindAddr); err == nil {
625
-		if len(port) != 0 && port != "53" {
626
-			glog.Warningf("Unable to bind DNS on port 53 (you may need to run as root), using %s which will not resolve from all locations", c.DNSBindAddr)
627
-		}
625
+	_, port, err := net.SplitHostPort(c.Options.DNSConfig.BindAddress)
626
+	if err != nil {
627
+		glog.Fatalf("Could not start DNS: %v", err)
628
+	}
629
+	if port != "53" {
630
+		glog.Warningf("Binding DNS on port %v instead of 53 (you may need to run as root and update your config), using %s which will not resolve from all locations", port, c.Options.DNSConfig.BindAddress)
631
+	}
632
+
633
+	if ok, err := cmdutil.TryListen(c.Options.DNSConfig.BindAddress); !ok {
634
+		glog.Warningf("Could not start DNS: %v", err)
635
+		return
628 636
 	}
629 637
 
630
-	config.DnsAddr = c.DNSBindAddr
631 638
 	go func() {
632 639
 		err := dns.ListenAndServe(config, c.DNSServerClient(), c.EtcdHelper.Client.(*etcdclient.Client))
633 640
 		glog.Fatalf("Could not start DNS: %v", err)
634 641
 	}()
635 642
 
636
-	cmdutil.WaitForSuccessfulDial(false, "tcp", c.DNSBindAddr, 100*time.Millisecond, 100*time.Millisecond, 100)
643
+	cmdutil.WaitForSuccessfulDial(false, "tcp", c.Options.DNSConfig.BindAddress, 100*time.Millisecond, 100*time.Millisecond, 100)
637 644
 
638
-	glog.Infof("OpenShift DNS listening at %s", c.DNSBindAddr)
645
+	glog.Infof("OpenShift DNS listening at %s", c.Options.DNSConfig.BindAddress)
639 646
 }
640 647
 
641 648
 // RunBuildController starts the build sync loop for builds and buildConfig processing.
... ...
@@ -682,7 +690,7 @@ func (c *MasterConfig) RunBuildPodController() {
682 682
 	controller.Run()
683 683
 }
684 684
 
685
-// RunDeploymentController starts the build image change trigger controller process.
685
+// RunBuildImageChangeTriggerController starts the build image change trigger controller process.
686 686
 func (c *MasterConfig) RunBuildImageChangeTriggerController() {
687 687
 	bcClient, _ := c.BuildControllerClients()
688 688
 	bcUpdater := buildclient.NewOSClientBuildConfigClient(bcClient)
... ...
@@ -692,11 +700,17 @@ func (c *MasterConfig) RunBuildImageChangeTriggerController() {
692 692
 }
693 693
 
694 694
 // RunDeploymentController starts the deployment controller process.
695
-func (c *MasterConfig) RunDeploymentController() {
695
+func (c *MasterConfig) RunDeploymentController() error {
696 696
 	_, kclient := c.DeploymentControllerClients()
697
+
698
+	_, kclientConfig, err := configapi.GetKubeClient(c.Options.MasterClients.OpenShiftLoopbackKubeConfig)
699
+	if err != nil {
700
+		return err
701
+	}
702
+	// TODO eliminate these environment variables once we figure out what they do
697 703
 	env := []api.EnvVar{
698
-		{Name: "KUBERNETES_MASTER", Value: c.MasterAddr},
699
-		{Name: "OPENSHIFT_MASTER", Value: c.MasterAddr},
704
+		{Name: "KUBERNETES_MASTER", Value: kclientConfig.Host},
705
+		{Name: "OPENSHIFT_MASTER", Value: kclientConfig.Host},
700 706
 	}
701 707
 	env = append(env, clientcmd.EnvVarsFromConfig(c.DeployerClientConfig())...)
702 708
 
... ...
@@ -709,6 +723,8 @@ func (c *MasterConfig) RunDeploymentController() {
709 709
 
710 710
 	controller := factory.Create()
711 711
 	controller.Run()
712
+
713
+	return nil
712 714
 }
713 715
 
714 716
 // RunDeployerPodController starts the deployer pod controller process.
... ...
@@ -774,10 +790,10 @@ func (c *MasterConfig) RouteAllocator() *routeallocationcontroller.RouteAllocati
774 774
 // ensureCORSAllowedOrigins takes a string list of origins and attempts to covert them to CORS origin
775 775
 // regexes, or exits if it cannot.
776 776
 func (c *MasterConfig) ensureCORSAllowedOrigins() []*regexp.Regexp {
777
-	if len(c.CORSAllowedOrigins) == 0 {
777
+	if len(c.Options.CORSAllowedOrigins) == 0 {
778 778
 		return []*regexp.Regexp{}
779 779
 	}
780
-	allowedOriginRegexps, err := util.CompileRegexps(util.StringList(c.CORSAllowedOrigins))
780
+	allowedOriginRegexps, err := util.CompileRegexps(util.StringList(c.Options.CORSAllowedOrigins))
781 781
 	if err != nil {
782 782
 		glog.Fatalf("Invalid --cors-allowed-origins: %v", err)
783 783
 	}
784 784
new file mode 100644
... ...
@@ -0,0 +1,225 @@
0
+package origin
1
+
2
+import (
3
+	"crypto/x509"
4
+	"fmt"
5
+	"net/http"
6
+
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
8
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
11
+	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
12
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
13
+	kutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
14
+
15
+	"github.com/openshift/origin/pkg/api/latest"
16
+	"github.com/openshift/origin/pkg/auth/authenticator"
17
+	"github.com/openshift/origin/pkg/auth/authenticator/request/bearertoken"
18
+	"github.com/openshift/origin/pkg/auth/authenticator/request/paramtoken"
19
+	"github.com/openshift/origin/pkg/auth/authenticator/request/unionrequest"
20
+	"github.com/openshift/origin/pkg/auth/authenticator/request/x509request"
21
+	"github.com/openshift/origin/pkg/auth/group"
22
+	authnregistry "github.com/openshift/origin/pkg/auth/oauth/registry"
23
+	"github.com/openshift/origin/pkg/authorization/authorizer"
24
+	policycache "github.com/openshift/origin/pkg/authorization/cache"
25
+	authorizationetcd "github.com/openshift/origin/pkg/authorization/registry/etcd"
26
+	"github.com/openshift/origin/pkg/authorization/rulevalidation"
27
+	osclient "github.com/openshift/origin/pkg/client"
28
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
29
+	oauthetcd "github.com/openshift/origin/pkg/oauth/registry/etcd"
30
+	projectauth "github.com/openshift/origin/pkg/project/auth"
31
+
32
+	"github.com/openshift/origin/pkg/cmd/server/etcd"
33
+	"github.com/openshift/origin/pkg/cmd/util/variable"
34
+)
35
+
36
+const (
37
+	unauthenticatedUsername = "system:anonymous"
38
+
39
+	authenticatedGroup   = "system:authenticated"
40
+	unauthenticatedGroup = "system:unauthenticated"
41
+)
42
+
43
+// MasterConfig defines the required parameters for starting the OpenShift master
44
+type MasterConfig struct {
45
+	Options configapi.MasterConfig
46
+
47
+	Authenticator                 authenticator.Request
48
+	Authorizer                    authorizer.Authorizer
49
+	AuthorizationAttributeBuilder authorizer.AuthorizationAttributeBuilder
50
+
51
+	PolicyCache               *policycache.PolicyCache
52
+	ProjectAuthorizationCache *projectauth.AuthorizationCache
53
+
54
+	// Map requests to contexts
55
+	RequestContextMapper kapi.RequestContextMapper
56
+
57
+	AdmissionControl admission.Interface
58
+
59
+	TLS bool
60
+
61
+	// a function that returns the appropriate image to use for a named component
62
+	ImageFor func(component string) string
63
+
64
+	EtcdHelper tools.EtcdHelper
65
+
66
+	// ClientCAs will be used to request client certificates in connections to the API.
67
+	// This CertPool should contain all the CAs that will be used for client certificate verification.
68
+	ClientCAs *x509.CertPool
69
+	// APIClientCAs is used to verify client certificates presented for API auth
70
+	APIClientCAs *x509.CertPool
71
+
72
+	// KubeClientConfig is the client configuration used to call Kubernetes APIs from system components.
73
+	// To apply different access control to a system component, create a client config specifically for that component.
74
+	KubeClientConfig kclient.Config
75
+	// OSClientConfig is the client configuration used to call OpenShift APIs from system components
76
+	// To apply different access control to a system component, create a client config specifically for that component.
77
+	OSClientConfig kclient.Config
78
+	// DeployerOSClientConfig is the client configuration used to call OpenShift APIs from launched deployer pods
79
+	DeployerOSClientConfig kclient.Config
80
+
81
+	// kubeClient is the client used to call Kubernetes APIs from system components, built from KubeClientConfig.
82
+	// It should only be accessed via the *Client() helper methods.
83
+	// To apply different access control to a system component, create a separate client/config specifically for that component.
84
+	KubernetesClient *kclient.Client
85
+	// osClient is the client used to call OpenShift APIs from system components, built from OSClientConfig.
86
+	// It should only be accessed via the *Client() helper methods.
87
+	// To apply different access control to a system component, create a separate client/config specifically for that component.
88
+	OSClient *osclient.Client
89
+}
90
+
91
+func BuildMasterConfig(options configapi.MasterConfig) (*MasterConfig, error) {
92
+
93
+	etcdHelper, err := etcd.NewOpenShiftEtcdHelper(options.EtcdClientInfo.URL)
94
+	if err != nil {
95
+		return nil, fmt.Errorf("Error setting up server storage: %v", err)
96
+	}
97
+
98
+	clientCAs, err := configapi.GetClientCertCAPool(options)
99
+	if err != nil {
100
+		return nil, err
101
+	}
102
+	apiClientCAs, err := configapi.GetAPIClientCertCAPool(options)
103
+	if err != nil {
104
+		return nil, err
105
+	}
106
+
107
+	kubeClient, kubeClientConfig, err := configapi.GetKubeClient(options.MasterClients.KubernetesKubeConfig)
108
+	if err != nil {
109
+		return nil, err
110
+	}
111
+	openshiftClient, osClientConfig, err := configapi.GetOpenShiftClient(options.MasterClients.OpenShiftLoopbackKubeConfig)
112
+	if err != nil {
113
+		return nil, err
114
+	}
115
+	_, deployerOSClientConfig, err := configapi.GetOpenShiftClient(options.MasterClients.DeployerKubeConfig)
116
+	if err != nil {
117
+		return nil, err
118
+	}
119
+
120
+	imageTemplate := variable.NewDefaultImageTemplate()
121
+	imageTemplate.Format = options.ImageConfig.Format
122
+	imageTemplate.Latest = options.ImageConfig.Latest
123
+
124
+	policyCache := newPolicyCache(etcdHelper)
125
+	requestContextMapper := kapi.NewRequestContextMapper()
126
+
127
+	// in-order list of plug-ins that should intercept admission decisions (origin only intercepts)
128
+	admissionControlPluginNames := []string{"AlwaysAdmit"}
129
+	admissionController := admission.NewFromPlugins(kubeClient, admissionControlPluginNames, "")
130
+
131
+	config := &MasterConfig{
132
+		Options: options,
133
+
134
+		Authenticator:                 newAuthenticator(options.ServingInfo, etcdHelper, apiClientCAs),
135
+		Authorizer:                    newAuthorizer(policyCache, options.MasterAuthorizationNamespace),
136
+		AuthorizationAttributeBuilder: newAuthorizationAttributeBuilder(requestContextMapper),
137
+
138
+		PolicyCache:               policyCache,
139
+		ProjectAuthorizationCache: newProjectAuthorizationCache(options.MasterAuthorizationNamespace, openshiftClient, kubeClient),
140
+
141
+		RequestContextMapper: requestContextMapper,
142
+
143
+		AdmissionControl: admissionController,
144
+
145
+		TLS: configapi.UseTLS(options.ServingInfo),
146
+
147
+		ImageFor:   imageTemplate.ExpandOrDie,
148
+		EtcdHelper: etcdHelper,
149
+
150
+		ClientCAs:    clientCAs,
151
+		APIClientCAs: apiClientCAs,
152
+
153
+		KubeClientConfig:       *kubeClientConfig,
154
+		OSClientConfig:         *osClientConfig,
155
+		DeployerOSClientConfig: *deployerOSClientConfig,
156
+		OSClient:               openshiftClient,
157
+		KubernetesClient:       kubeClient,
158
+	}
159
+
160
+	return config, nil
161
+}
162
+
163
+func newAuthenticator(servingInfo configapi.ServingInfo, etcdHelper tools.EtcdHelper, apiClientCAs *x509.CertPool) authenticator.Request {
164
+	tokenAuthenticator := getEtcdTokenAuthenticator(etcdHelper)
165
+
166
+	authenticators := []authenticator.Request{}
167
+	authenticators = append(authenticators, bearertoken.New(tokenAuthenticator, true))
168
+	// Allow token as access_token param for WebSockets
169
+	// TODO: make the param name configurable
170
+	// TODO: limit this authenticator to watch methods, if possible
171
+	// TODO: prevent access_token param from getting logged, if possible
172
+	authenticators = append(authenticators, paramtoken.New("access_token", tokenAuthenticator, true))
173
+
174
+	if configapi.UseTLS(servingInfo) {
175
+		// build cert authenticator
176
+		// TODO: add cert users to etcd?
177
+		opts := x509request.DefaultVerifyOptions()
178
+		opts.Roots = apiClientCAs
179
+		certauth := x509request.New(opts, x509request.SubjectToUserConversion)
180
+		authenticators = append(authenticators, certauth)
181
+	}
182
+
183
+	// TODO: make anonymous auth optional?
184
+	ret := &unionrequest.Authenticator{
185
+		FailOnError: true,
186
+		Handlers: []authenticator.Request{
187
+			group.NewGroupAdder(unionrequest.NewUnionAuthentication(authenticators...), []string{authenticatedGroup}),
188
+			authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) {
189
+				return &user.DefaultInfo{Name: unauthenticatedUsername, Groups: []string{unauthenticatedGroup}}, true, nil
190
+			}),
191
+		},
192
+	}
193
+
194
+	return ret
195
+}
196
+
197
+func newProjectAuthorizationCache(masterAuthorizationNamespace string, openshiftClient *osclient.Client, kubeClient *kclient.Client) *projectauth.AuthorizationCache {
198
+	return projectauth.NewAuthorizationCache(
199
+		projectauth.NewReviewer(openshiftClient),
200
+		kubeClient.Namespaces(),
201
+		openshiftClient,
202
+		openshiftClient,
203
+		masterAuthorizationNamespace)
204
+}
205
+
206
+func newPolicyCache(etcdHelper tools.EtcdHelper) *policycache.PolicyCache {
207
+	authorizationEtcd := authorizationetcd.New(etcdHelper)
208
+	return policycache.NewPolicyCache(authorizationEtcd, authorizationEtcd)
209
+}
210
+
211
+func newAuthorizer(policyCache *policycache.PolicyCache, masterAuthorizationNamespace string) authorizer.Authorizer {
212
+	authorizer := authorizer.NewAuthorizer(masterAuthorizationNamespace, rulevalidation.NewDefaultRuleResolver(policyCache, policyCache))
213
+	return authorizer
214
+}
215
+
216
+func newAuthorizationAttributeBuilder(requestContextMapper kapi.RequestContextMapper) authorizer.AuthorizationAttributeBuilder {
217
+	authorizationAttributeBuilder := authorizer.NewAuthorizationAttributeBuilder(requestContextMapper, &apiserver.APIRequestInfoResolver{kutil.NewStringSet("api", "osapi"), latest.RESTMapper})
218
+	return authorizationAttributeBuilder
219
+}
220
+
221
+func getEtcdTokenAuthenticator(etcdHelper tools.EtcdHelper) authenticator.Token {
222
+	oauthRegistry := oauthetcd.New(etcdHelper)
223
+	return authnregistry.NewTokenAuthenticator(oauthRegistry)
224
+}
0 225
deleted file mode 100644
... ...
@@ -1,506 +0,0 @@
1
-package server
2
-
3
-import (
4
-	"crypto/x509"
5
-	"fmt"
6
-	"io/ioutil"
7
-	"net"
8
-	"strings"
9
-	"time"
10
-
11
-	"code.google.com/p/go-uuid/uuid"
12
-
13
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
14
-	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
15
-	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
16
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
17
-
18
-	osclient "github.com/openshift/origin/pkg/client"
19
-	"github.com/openshift/origin/pkg/cmd/server/crypto"
20
-	"github.com/openshift/origin/pkg/cmd/server/origin"
21
-	cmdutil "github.com/openshift/origin/pkg/cmd/util"
22
-)
23
-
24
-func (cfg Config) BuildOriginMasterConfig() (*origin.MasterConfig, error) {
25
-	masterAddr, err := cfg.GetMasterAddress()
26
-	if err != nil {
27
-		return nil, err
28
-	}
29
-	kubeAddr, err := cfg.GetKubernetesAddress()
30
-	if err != nil {
31
-		return nil, err
32
-	}
33
-	masterPublicAddr, err := cfg.GetMasterPublicAddress()
34
-	if err != nil {
35
-		return nil, err
36
-	}
37
-	kubePublicAddr, err := cfg.GetKubernetesPublicAddress()
38
-	if err != nil {
39
-		return nil, err
40
-	}
41
-	assetPublicAddr, err := cfg.GetAssetPublicAddress()
42
-	if err != nil {
43
-		return nil, err
44
-	}
45
-
46
-	corsAllowedOrigins := []string{}
47
-	corsAllowedOrigins = append(corsAllowedOrigins, cfg.CORSAllowedOrigins...)
48
-	// always include the all-in-one server's web console as an allowed CORS origin
49
-	// always include localhost as an allowed CORS origin
50
-	// always include master public address as an allowed CORS origin
51
-	for _, origin := range []string{assetPublicAddr.Host, masterPublicAddr.Host, "localhost", "127.0.0.1"} {
52
-		// TODO: check if origin is already allowed
53
-		corsAllowedOrigins = append(corsAllowedOrigins, origin)
54
-	}
55
-
56
-	etcdHelper, err := cfg.newOpenShiftEtcdHelper()
57
-	if err != nil {
58
-		return nil, fmt.Errorf("Error setting up server storage: %v", err)
59
-	}
60
-
61
-	masterCertFile, masterKeyFile, err := cfg.GetMasterCert()
62
-	if err != nil {
63
-		return nil, err
64
-	}
65
-	assetCertFile, assetKeyFile, err := cfg.GetAssetCert()
66
-	if err != nil {
67
-		return nil, err
68
-	}
69
-
70
-	clientCAs, err := cfg.GetClientCertCAPool()
71
-	if err != nil {
72
-		return nil, err
73
-	}
74
-	apiClientCAs, err := cfg.GetAPIClientCertCAPool()
75
-	if err != nil {
76
-		return nil, err
77
-	}
78
-
79
-	kubeClient, kubeClientConfig, err := cfg.GetKubeClient()
80
-	if err != nil {
81
-		return nil, err
82
-	}
83
-	openshiftClient, openshiftClientConfig, err := cfg.GetOpenshiftClient()
84
-	if err != nil {
85
-		return nil, err
86
-	}
87
-	deployerClientConfig, err := cfg.GetOpenshiftDeployerClientConfig()
88
-	if err != nil {
89
-		return nil, err
90
-	}
91
-
92
-	dnsAddr := cfg.DNSBindAddr
93
-	if !cmdutil.TryListen(dnsAddr.URL.Host) {
94
-		dnsAddr.DefaultPort = 8053
95
-		dnsAddr = dnsAddr.Default()
96
-	}
97
-
98
-	openshiftConfigParameters := origin.MasterConfigParameters{
99
-		MasterBindAddr:       cfg.BindAddr.URL.Host,
100
-		AssetBindAddr:        cfg.GetAssetBindAddress(),
101
-		DNSBindAddr:          dnsAddr.URL.Host,
102
-		MasterAddr:           masterAddr.String(),
103
-		KubernetesAddr:       kubeAddr.String(),
104
-		MasterPublicAddr:     masterPublicAddr.String(),
105
-		KubernetesPublicAddr: kubePublicAddr.String(),
106
-		AssetPublicAddr:      assetPublicAddr.String(),
107
-
108
-		CORSAllowedOrigins:                corsAllowedOrigins,
109
-		MasterAuthorizationNamespace:      "master",
110
-		OpenshiftSharedResourcesNamespace: "openshift",
111
-		LogoutURI:                         env("OPENSHIFT_LOGOUT_URI", ""),
112
-
113
-		EtcdHelper: etcdHelper,
114
-
115
-		MasterCertFile: masterCertFile,
116
-		MasterKeyFile:  masterKeyFile,
117
-		AssetCertFile:  assetCertFile,
118
-		AssetKeyFile:   assetKeyFile,
119
-		ClientCAs:      clientCAs,
120
-		APIClientCAs:   apiClientCAs,
121
-
122
-		KubeClient:             kubeClient,
123
-		KubeClientConfig:       *kubeClientConfig,
124
-		OSClient:               openshiftClient,
125
-		OSClientConfig:         *openshiftClientConfig,
126
-		DeployerOSClientConfig: *deployerClientConfig,
127
-
128
-		ImageFor: cfg.ImageTemplate.ExpandOrDie,
129
-	}
130
-	openshiftConfig, err := origin.BuildMasterConfig(openshiftConfigParameters)
131
-	if err != nil {
132
-		return nil, err
133
-	}
134
-
135
-	return openshiftConfig, nil
136
-}
137
-
138
-func (cfg Config) BuildAuthConfig() (*origin.AuthConfig, error) {
139
-	masterAddr, err := cfg.GetMasterAddress()
140
-	if err != nil {
141
-		return nil, err
142
-	}
143
-	masterPublicAddr, err := cfg.GetMasterPublicAddress()
144
-	if err != nil {
145
-		return nil, err
146
-	}
147
-	assetPublicAddr, err := cfg.GetAssetPublicAddress()
148
-	if err != nil {
149
-		return nil, err
150
-	}
151
-
152
-	apiServerCAs, err := cfg.GetAPIServerCertCAPool()
153
-	if err != nil {
154
-		return nil, err
155
-	}
156
-
157
-	// Build the list of valid redirect_uri prefixes for a login using the openshift-web-console client to redirect to
158
-	// TODO: allow configuring this
159
-	// TODO: remove hard-coding of development UI server
160
-	assetPublicAddresses := []string{assetPublicAddr.String(), "http://localhost:9000", "https://localhost:9000"}
161
-
162
-	etcdHelper, err := cfg.newOpenShiftEtcdHelper()
163
-	if err != nil {
164
-		return nil, fmt.Errorf("Error setting up server storage: %v", err)
165
-	}
166
-	// Default to a session authenticator (for browsers), and a basicauth authenticator (for clients responding to WWW-Authenticate challenges)
167
-	defaultAuthRequestHandlers := strings.Join([]string{
168
-		string(origin.AuthRequestHandlerSession),
169
-		string(origin.AuthRequestHandlerBasicAuth),
170
-	}, ",")
171
-
172
-	ret := &origin.AuthConfig{
173
-		MasterAddr:           masterAddr.String(),
174
-		MasterPublicAddr:     masterPublicAddr.String(),
175
-		AssetPublicAddresses: assetPublicAddresses,
176
-		MasterRoots:          apiServerCAs,
177
-		EtcdHelper:           etcdHelper,
178
-
179
-		// Max token ages
180
-		AuthorizeTokenMaxAgeSeconds: envInt("OPENSHIFT_OAUTH_AUTHORIZE_TOKEN_MAX_AGE_SECONDS", 300, 1),
181
-		AccessTokenMaxAgeSeconds:    envInt("OPENSHIFT_OAUTH_ACCESS_TOKEN_MAX_AGE_SECONDS", 3600, 1),
182
-		// Handlers
183
-		AuthRequestHandlers: origin.ParseAuthRequestHandlerTypes(env("OPENSHIFT_OAUTH_REQUEST_HANDLERS", defaultAuthRequestHandlers)),
184
-		AuthHandler:         origin.AuthHandlerType(env("OPENSHIFT_OAUTH_HANDLER", string(origin.AuthHandlerLogin))),
185
-		GrantHandler:        origin.GrantHandlerType(env("OPENSHIFT_OAUTH_GRANT_HANDLER", string(origin.GrantHandlerAuto))),
186
-		// RequestHeader config
187
-		RequestHeaders:      strings.Split(env("OPENSHIFT_OAUTH_REQUEST_HEADERS", "X-Remote-User"), ","),
188
-		RequestHeaderCAFile: GetOAuthRequestHeaderCAFile(),
189
-		// Session config (default to unknowable secret)
190
-		SessionSecrets:       []string{env("OPENSHIFT_OAUTH_SESSION_SECRET", uuid.NewUUID().String())},
191
-		SessionMaxAgeSeconds: envInt("OPENSHIFT_OAUTH_SESSION_MAX_AGE_SECONDS", 300, 1),
192
-		SessionName:          env("OPENSHIFT_OAUTH_SESSION_NAME", "ssn"),
193
-		// Password config
194
-		PasswordAuth: origin.PasswordAuthType(env("OPENSHIFT_OAUTH_PASSWORD_AUTH", string(origin.PasswordAuthAnyPassword))),
195
-		BasicAuthURL: env("OPENSHIFT_OAUTH_BASIC_AUTH_URL", ""),
196
-		HTPasswdFile: env("OPENSHIFT_OAUTH_HTPASSWD_FILE", ""),
197
-		// Token config
198
-		TokenStore:    origin.TokenStoreType(env("OPENSHIFT_OAUTH_TOKEN_STORE", string(origin.TokenStoreOAuth))),
199
-		TokenFilePath: env("OPENSHIFT_OAUTH_TOKEN_FILE_PATH", ""),
200
-		// Google config
201
-		GoogleClientID:     env("OPENSHIFT_OAUTH_GOOGLE_CLIENT_ID", ""),
202
-		GoogleClientSecret: env("OPENSHIFT_OAUTH_GOOGLE_CLIENT_SECRET", ""),
203
-		// GitHub config
204
-		GithubClientID:     env("OPENSHIFT_OAUTH_GITHUB_CLIENT_ID", ""),
205
-		GithubClientSecret: env("OPENSHIFT_OAUTH_GITHUB_CLIENT_SECRET", ""),
206
-	}
207
-
208
-	return ret, nil
209
-
210
-}
211
-
212
-func GetOAuthRequestHeaderCAFile() string {
213
-	return env("OPENSHIFT_OAUTH_REQUEST_HEADER_CA_FILE", "")
214
-}
215
-
216
-func (cfg Config) newCA() (*crypto.CA, error) {
217
-	masterAddr, err := cfg.GetMasterAddress()
218
-	if err != nil {
219
-		return nil, err
220
-	}
221
-
222
-	// Bootstrap CA
223
-	// TODO: store this (or parts of this) in etcd?
224
-	ca, err := crypto.InitCA(cfg.CertDir, fmt.Sprintf("%s@%d", masterAddr.Host, time.Now().Unix()))
225
-	if err != nil {
226
-		return nil, fmt.Errorf("Unable to configure certificate authority: %v", err)
227
-	}
228
-
229
-	return ca, nil
230
-}
231
-
232
-// GetAPIClientCertCAPool returns the cert pool used to validate client certificates to the API server
233
-func (cfg Config) GetAPIClientCertCAPool() (*x509.CertPool, error) {
234
-	certs, err := cfg.getAPIClientCertCAs()
235
-	if err != nil {
236
-		return nil, err
237
-	}
238
-	roots := x509.NewCertPool()
239
-	for _, root := range certs {
240
-		roots.AddCert(root)
241
-	}
242
-	return roots, nil
243
-}
244
-
245
-// GetClientCertCAPool returns a cert pool containing all client CAs that could be presented (union of API and OAuth)
246
-func (cfg Config) GetClientCertCAPool() (*x509.CertPool, error) {
247
-	roots := x509.NewCertPool()
248
-
249
-	// Add CAs for OAuth
250
-	certs, err := cfg.getOAuthClientCertCAs()
251
-	if err != nil {
252
-		return nil, err
253
-	}
254
-	for _, root := range certs {
255
-		roots.AddCert(root)
256
-	}
257
-
258
-	// Add CAs for API
259
-	certs, err = cfg.getAPIClientCertCAs()
260
-	if err != nil {
261
-		return nil, err
262
-	}
263
-	for _, root := range certs {
264
-		roots.AddCert(root)
265
-	}
266
-
267
-	return roots, nil
268
-}
269
-
270
-// GetAPIServerCertCAPool returns the cert pool containing the roots for the API server cert
271
-func (cfg Config) GetAPIServerCertCAPool() (*x509.CertPool, error) {
272
-	ca, err := cfg.newCA()
273
-	if err != nil {
274
-		return nil, err
275
-	}
276
-	roots := x509.NewCertPool()
277
-	for _, root := range ca.Config.Roots {
278
-		roots.AddCert(root)
279
-	}
280
-	return roots, nil
281
-}
282
-
283
-func (cfg Config) getOAuthClientCertCAs() ([]*x509.Certificate, error) {
284
-	caFile := GetOAuthRequestHeaderCAFile()
285
-	if len(caFile) == 0 {
286
-		return nil, nil
287
-	}
288
-	caPEMBlock, err := ioutil.ReadFile(caFile)
289
-	if err != nil {
290
-		return nil, err
291
-	}
292
-	certs, err := crypto.CertsFromPEM(caPEMBlock)
293
-	if err != nil {
294
-		return nil, fmt.Errorf("Error reading %s: %s", caFile, err)
295
-	}
296
-	return certs, nil
297
-}
298
-
299
-func (cfg Config) getAPIClientCertCAs() ([]*x509.Certificate, error) {
300
-	ca, err := cfg.newCA()
301
-	if err != nil {
302
-		return nil, err
303
-	}
304
-	return ca.Config.Roots, nil
305
-}
306
-
307
-func (cfg Config) GetServerCertHostnames() ([]string, error) {
308
-	masterAddr, err := cfg.GetMasterAddress()
309
-	if err != nil {
310
-		return nil, err
311
-	}
312
-	masterPublicAddr, err := cfg.GetMasterPublicAddress()
313
-	if err != nil {
314
-		return nil, err
315
-	}
316
-	kubePublicAddr, err := cfg.GetKubernetesPublicAddress()
317
-	if err != nil {
318
-		return nil, err
319
-	}
320
-	assetPublicAddr, err := cfg.GetAssetPublicAddress()
321
-	if err != nil {
322
-		return nil, err
323
-	}
324
-
325
-	// 172.17.42.1 enables the router to call back out to the master
326
-	// TODO: Remove 172.17.42.1 once we can figure out how to validate the master's cert from inside a pod, or tell pods the real IP for the master
327
-	allHostnames := util.NewStringSet("localhost", "127.0.0.1", "172.17.42.1", masterAddr.Host, masterPublicAddr.Host, kubePublicAddr.Host, assetPublicAddr.Host)
328
-	certHostnames := util.StringSet{}
329
-	for hostname := range allHostnames {
330
-		if host, _, err := net.SplitHostPort(hostname); err == nil {
331
-			// add the hostname without the port
332
-			certHostnames.Insert(host)
333
-		} else {
334
-			// add the originally specified hostname
335
-			certHostnames.Insert(hostname)
336
-		}
337
-	}
338
-
339
-	return certHostnames.List(), nil
340
-}
341
-
342
-func (cfg Config) GetMasterCert() (certFile string, keyFile string, err error) {
343
-	ca, err := cfg.newCA()
344
-	if err != nil {
345
-		return "", "", err
346
-	}
347
-
348
-	certHostnames, err := cfg.GetServerCertHostnames()
349
-	if err != nil {
350
-		return "", "", err
351
-	}
352
-
353
-	serverCert, err := ca.MakeServerCert("master", certHostnames)
354
-	if err != nil {
355
-		return "", "", err
356
-	}
357
-
358
-	return serverCert.CertFile, serverCert.KeyFile, nil
359
-}
360
-
361
-func (cfg Config) GetAssetCert() (certFile string, keyFile string, err error) {
362
-	ca, err := cfg.newCA()
363
-	if err != nil {
364
-		return "", "", err
365
-	}
366
-
367
-	certHostnames, err := cfg.GetServerCertHostnames()
368
-	if err != nil {
369
-		return "", "", err
370
-	}
371
-
372
-	serverCert, err := ca.MakeServerCert("master", certHostnames)
373
-	if err != nil {
374
-		return "", "", err
375
-	}
376
-
377
-	return serverCert.CertFile, serverCert.KeyFile, nil
378
-}
379
-
380
-func (cfg Config) newClientConfigTemplate() (*clientcmdapi.Config, error) {
381
-	masterAddr, err := cfg.GetMasterAddress()
382
-	if err != nil {
383
-		return nil, err
384
-	}
385
-	masterPublicAddr, err := cfg.GetMasterPublicAddress()
386
-	if err != nil {
387
-		return nil, err
388
-	}
389
-
390
-	return &clientcmdapi.Config{
391
-		Clusters: map[string]clientcmdapi.Cluster{
392
-			"master":        {Server: masterAddr.String()},
393
-			"public-master": {Server: masterPublicAddr.String()},
394
-		},
395
-		Contexts: map[string]clientcmdapi.Context{
396
-			"master":        {Cluster: "master"},
397
-			"public-master": {Cluster: "public-master"},
398
-		},
399
-		CurrentContext: "master",
400
-	}, nil
401
-}
402
-
403
-func (cfg Config) GetKubeClient() (*kclient.Client, *kclient.Config, error) {
404
-	var err error
405
-	var kubeClientConfig *kclient.Config
406
-
407
-	// if we're starting an all in one, make credentials for a kube client.
408
-	if cfg.StartKube {
409
-		kubeClientConfig, err = cfg.MintSystemClientCert("kube-client")
410
-		if err != nil {
411
-			return nil, nil, err
412
-		}
413
-
414
-	} else {
415
-		// Get the kubernetes address we're using
416
-		kubeAddr, err := cfg.GetKubernetesAddress()
417
-		if err != nil {
418
-			return nil, nil, err
419
-		}
420
-
421
-		// Try to get the kubeconfig
422
-		kubeCfg, ok, err := cfg.GetExternalKubernetesClientConfig()
423
-		if err != nil {
424
-			return nil, nil, err
425
-		}
426
-		if !ok {
427
-			// No kubeconfig was provided, so just make one that points at the specified host
428
-			// It probably won't work (since it has no auth), but they'll get to see failures logged
429
-			kubeCfg = &kclient.Config{Host: kubeAddr.String()}
430
-		}
431
-
432
-		// Ensure the kubernetes address matches the one in the config
433
-		if kubeAddr.String() != kubeCfg.Host {
434
-			return nil, nil, fmt.Errorf("The Kubernetes server (%s) must match the server in the provided kubeconfig (%s)", kubeAddr.String(), kubeCfg.Host)
435
-		}
436
-
437
-		kubeClientConfig = kubeCfg
438
-	}
439
-
440
-	kubeClient, err := kclient.New(kubeClientConfig)
441
-	if err != nil {
442
-		return nil, nil, err
443
-	}
444
-
445
-	return kubeClient, kubeClientConfig, nil
446
-}
447
-
448
-func (cfg Config) GetOpenshiftClient() (*osclient.Client, *kclient.Config, error) {
449
-	clientConfig, err := cfg.MintSystemClientCert("openshift-client")
450
-	if err != nil {
451
-		return nil, nil, err
452
-	}
453
-
454
-	client, err := osclient.New(clientConfig)
455
-	if err != nil {
456
-		return nil, nil, err
457
-	}
458
-
459
-	return client, clientConfig, nil
460
-}
461
-
462
-func (cfg Config) GetOpenshiftDeployerClientConfig() (*kclient.Config, error) {
463
-	clientConfig, err := cfg.MintSystemClientCert("openshift-deployer", "system:deployers")
464
-	if err != nil {
465
-		return nil, err
466
-	}
467
-
468
-	return clientConfig, nil
469
-}
470
-
471
-// known certs:
472
-// openshiftClientUser := &user.DefaultInfo{Name: "system:openshift-client"}
473
-// openshiftDeployerUser := &user.DefaultInfo{Name: "system:openshift-deployer", Groups: []string{"system:deployers"}}
474
-// adminUser := &user.DefaultInfo{Name: "system:admin", Groups: []string{"system:cluster-admins"}}
475
-// kubeClientUser := &user.DefaultInfo{Name: "system:kube-client"}
476
-// // One for each node in cfg.GetNodeList()
477
-func (cfg Config) MintSystemClientCert(username string, groups ...string) (*kclient.Config, error) {
478
-	ca, err := cfg.newCA()
479
-	if err != nil {
480
-		return nil, err
481
-	}
482
-	clientConfigTemplate, err := cfg.newClientConfigTemplate()
483
-	if err != nil {
484
-		return nil, err
485
-	}
486
-
487
-	qualifiedUsername := "system:" + username
488
-	user := &user.DefaultInfo{Name: qualifiedUsername, Groups: groups}
489
-	config, err := ca.MakeClientConfig(username, user, *clientConfigTemplate)
490
-	if err != nil {
491
-		return nil, err
492
-	}
493
-
494
-	return &config, nil
495
-}
496
-
497
-func (cfg Config) MintNodeCerts() error {
498
-	for _, node := range cfg.NodeList {
499
-		username := "node-" + node
500
-		if _, err := cfg.MintSystemClientCert(username, "system:nodes"); err != nil {
501
-			return err
502
-		}
503
-	}
504
-
505
-	return nil
506
-}
507 1
deleted file mode 100644
... ...
@@ -1,229 +0,0 @@
1
-package server
2
-
3
-import (
4
-	"fmt"
5
-	"net"
6
-	"net/http"
7
-	"net/url"
8
-	"os"
9
-	"strconv"
10
-	"strings"
11
-
12
-	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
13
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
14
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
15
-	"github.com/coreos/go-systemd/daemon"
16
-	"github.com/golang/glog"
17
-	"github.com/openshift/origin/pkg/cmd/server/etcd"
18
-	"github.com/openshift/origin/pkg/cmd/server/kubernetes"
19
-	"github.com/openshift/origin/pkg/cmd/server/origin"
20
-)
21
-
22
-func (cfg Config) startMaster() error {
23
-	// Allow privileged containers
24
-	// TODO: make this configurable and not the default https://github.com/openshift/origin/issues/662
25
-	capabilities.Initialize(capabilities.Capabilities{
26
-		AllowPrivileged: true,
27
-	})
28
-
29
-	cfg.MintNodeCerts()
30
-	cfg.MintSystemClientCert("admin", "system:cluster-admins")
31
-	cfg.MintSystemClientCert("openshift-deployer", "system:deployers")
32
-	cfg.MintSystemClientCert("openshift-client")
33
-	if cfg.StartKube {
34
-		cfg.MintSystemClientCert("kube-client")
35
-	}
36
-	glog.Infof("Client certificates and .kubeconfig files generated in %v", cfg.CertDir)
37
-
38
-	openshiftConfig, err := cfg.BuildOriginMasterConfig()
39
-	if err != nil {
40
-		return err
41
-	}
42
-
43
-	//	 must start policy caching immediately
44
-	openshiftConfig.RunPolicyCache()
45
-
46
-	authConfig, err := cfg.BuildAuthConfig()
47
-	if err != nil {
48
-		return err
49
-	}
50
-
51
-	glog.Infof("Nodes: %v", cfg.NodeList)
52
-
53
-	if strings.Contains(openshiftConfig.MasterAddr, "127.0.0.1") {
54
-		glog.Infof("WARNING: Your server is being advertised only to the host - containers will not be able to communicate with the master without a proxy")
55
-	}
56
-
57
-	if cfg.StartKube {
58
-		kubeConfig, err := cfg.BuildKubernetesMasterConfig(openshiftConfig.RequestContextMapper, openshiftConfig.KubeClient())
59
-		if err != nil {
60
-			return err
61
-		}
62
-		kubeConfig.EnsurePortalFlags()
63
-
64
-		openshiftConfig.Run([]origin.APIInstaller{kubeConfig}, []origin.APIInstaller{authConfig})
65
-
66
-		kubeConfig.RunScheduler()
67
-		kubeConfig.RunReplicationController()
68
-		kubeConfig.RunEndpointController()
69
-		kubeConfig.RunMinionController()
70
-		kubeConfig.RunResourceQuotaManager()
71
-
72
-	} else {
73
-		kubeAddr, err := cfg.GetKubernetesAddress()
74
-		if err != nil {
75
-			return err
76
-		}
77
-		proxy := &kubernetes.ProxyConfig{
78
-			KubernetesAddr: kubeAddr,
79
-			ClientConfig:   &openshiftConfig.KubeClientConfig,
80
-		}
81
-
82
-		openshiftConfig.Run([]origin.APIInstaller{proxy}, []origin.APIInstaller{authConfig})
83
-	}
84
-
85
-	// TODO: recording should occur in individual components
86
-	record.StartRecording(openshiftConfig.KubeClient().Events(""), kapi.EventSource{Component: "master"})
87
-
88
-	glog.Infof("Using images from %q", openshiftConfig.ImageFor("<component>"))
89
-
90
-	openshiftConfig.RunDNSServer()
91
-	openshiftConfig.RunAssetServer()
92
-	openshiftConfig.RunBuildController()
93
-	openshiftConfig.RunBuildPodController()
94
-	openshiftConfig.RunBuildImageChangeTriggerController()
95
-	openshiftConfig.RunDeploymentController()
96
-	openshiftConfig.RunDeployerPodController()
97
-	openshiftConfig.RunDeploymentConfigController()
98
-	openshiftConfig.RunDeploymentConfigChangeController()
99
-	openshiftConfig.RunDeploymentImageChangeTriggerController()
100
-	openshiftConfig.RunProjectAuthorizationCache()
101
-
102
-	return nil
103
-}
104
-
105
-// run launches the appropriate startup modes or returns an error.
106
-func (cfg Config) Start(args []string) error {
107
-	if cfg.WriteConfigOnly {
108
-		return nil
109
-	}
110
-
111
-	switch {
112
-	case cfg.StartMaster && cfg.StartNode:
113
-		glog.Infof("Starting an OpenShift all-in-one, reachable at %s (etcd: %s)", cfg.MasterAddr.String(), cfg.EtcdAddr.String())
114
-		if cfg.MasterPublicAddr.Provided {
115
-			glog.Infof("OpenShift master public address is %s", cfg.MasterPublicAddr.String())
116
-		}
117
-
118
-	case cfg.StartMaster:
119
-		glog.Infof("Starting an OpenShift master, reachable at %s (etcd: %s)", cfg.MasterAddr.String(), cfg.EtcdAddr.String())
120
-		if cfg.MasterPublicAddr.Provided {
121
-			glog.Infof("OpenShift master public address is %s", cfg.MasterPublicAddr.String())
122
-		}
123
-
124
-	case cfg.StartNode:
125
-		glog.Infof("Starting an OpenShift node, connecting to %s", cfg.MasterAddr.String())
126
-
127
-	}
128
-
129
-	if cfg.StartEtcd {
130
-		if err := cfg.RunEtcd(); err != nil {
131
-			return err
132
-		}
133
-	}
134
-
135
-	if env("OPENSHIFT_PROFILE", "") == "web" {
136
-		go func() {
137
-			glog.Infof("Starting profiling endpoint at http://127.0.0.1:6060/debug/pprof/")
138
-			glog.Fatal(http.ListenAndServe("127.0.0.1:6060", nil))
139
-		}()
140
-	}
141
-
142
-	if cfg.StartMaster {
143
-		if err := cfg.startMaster(); err != nil {
144
-			return err
145
-		}
146
-	}
147
-
148
-	if cfg.StartNode {
149
-		kubeClient, _, err := cfg.GetKubeClient()
150
-		if err != nil {
151
-			return err
152
-		}
153
-
154
-		if !cfg.StartMaster {
155
-			// TODO: recording should occur in individual components
156
-			record.StartRecording(kubeClient.Events(""), kapi.EventSource{Component: "node"})
157
-		}
158
-
159
-		nodeConfig, err := cfg.BuildKubernetesNodeConfig()
160
-		if err != nil {
161
-			return err
162
-		}
163
-
164
-		nodeConfig.EnsureVolumeDir()
165
-		nodeConfig.EnsureDocker(cfg.Docker)
166
-		nodeConfig.RunProxy()
167
-		nodeConfig.RunKubelet()
168
-	}
169
-
170
-	daemon.SdNotify("READY=1")
171
-	select {}
172
-
173
-	return nil
174
-}
175
-
176
-func envInt(key string, defaultValue int32, minValue int32) int32 {
177
-	value, err := strconv.ParseInt(env(key, fmt.Sprintf("%d", defaultValue)), 10, 32)
178
-	if err != nil || int32(value) < minValue {
179
-		glog.Warningf("Invalid %s. Defaulting to %d.", key, defaultValue)
180
-		return defaultValue
181
-	}
182
-	return int32(value)
183
-}
184
-
185
-// env returns an environment variable or a default value if not specified.
186
-func env(key string, defaultValue string) string {
187
-	val := os.Getenv(key)
188
-	if len(val) == 0 {
189
-		return defaultValue
190
-	}
191
-	return val
192
-}
193
-
194
-func (cfg Config) RunEtcd() error {
195
-	etcdAddr, err := cfg.GetEtcdAddress()
196
-	if err != nil {
197
-		return err
198
-	}
199
-
200
-	etcdConfig := &etcd.Config{
201
-		BindAddr:     cfg.GetEtcdBindAddress(),
202
-		PeerBindAddr: cfg.GetEtcdPeerBindAddress(),
203
-		MasterAddr:   etcdAddr.Host,
204
-		EtcdDir:      cfg.EtcdDir,
205
-	}
206
-
207
-	etcdConfig.Run()
208
-
209
-	return nil
210
-}
211
-
212
-func getHost(theURL url.URL) string {
213
-	host, _, err := net.SplitHostPort(theURL.Host)
214
-	if err != nil {
215
-		return theURL.Host
216
-	}
217
-
218
-	return host
219
-}
220
-
221
-func getPort(theURL url.URL) int {
222
-	_, port, err := net.SplitHostPort(theURL.Host)
223
-	if err != nil {
224
-		return 0
225
-	}
226
-
227
-	intport, _ := strconv.Atoi(port)
228
-	return intport
229
-}
230 1
new file mode 100644
... ...
@@ -0,0 +1,24 @@
0
+package start
1
+
2
+import (
3
+	"github.com/spf13/pflag"
4
+
5
+	"github.com/openshift/origin/pkg/cmd/flagtypes"
6
+)
7
+
8
+// BindAddrArg is a struct that the command stores flag values into.
9
+type BindAddrArg struct {
10
+	BindAddr flagtypes.Addr
11
+}
12
+
13
+func BindBindAddrArg(args *BindAddrArg, flags *pflag.FlagSet, prefix string) {
14
+	flags.Var(&args.BindAddr, prefix+"listen", "The address to listen for connections on (host, host:port, or URL).")
15
+}
16
+
17
+func NewDefaultBindAddrArg() *BindAddrArg {
18
+	config := &BindAddrArg{
19
+		BindAddr: flagtypes.Addr{Value: "0.0.0.0:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default(),
20
+	}
21
+
22
+	return config
23
+}
0 24
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+package start
1
+
2
+import (
3
+	"github.com/spf13/pflag"
4
+)
5
+
6
+type CertArgs struct {
7
+	CertDir        string
8
+	CreateCerts    bool
9
+	OverwriteCerts bool
10
+}
11
+
12
+func BindCertArgs(args *CertArgs, flags *pflag.FlagSet, prefix string) {
13
+	flags.BoolVar(&args.CreateCerts, prefix+"create-certs", true, "Create any missing certificates required for launch or for writing the config file.")
14
+	flags.StringVar(&args.CertDir, prefix+"cert-dir", "openshift.local.certificates", "The certificate data directory.")
15
+}
16
+
17
+func NewDefaultCertArgs() *CertArgs {
18
+	return &CertArgs{CreateCerts: true}
19
+}
0 20
new file mode 100644
... ...
@@ -0,0 +1,338 @@
0
+package start
1
+
2
+import (
3
+	"io/ioutil"
4
+	"os"
5
+	"strconv"
6
+	"strings"
7
+	"testing"
8
+
9
+	"github.com/spf13/cobra"
10
+
11
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
12
+)
13
+
14
+func TestCommandBindingListen(t *testing.T) {
15
+	valueToSet := "http://example.org:9123"
16
+	actualCfg := executeMasterCommand([]string{"--listen=" + valueToSet})
17
+
18
+	expectedArgs := NewDefaultMasterArgs()
19
+	expectedArgs.BindAddrArg.BindAddr.Set(valueToSet)
20
+
21
+	if expectedArgs.BindAddrArg.BindAddr.String() != actualCfg.BindAddrArg.BindAddr.String() {
22
+		t.Errorf("expected %v, got %v", expectedArgs.BindAddrArg.BindAddr.String(), actualCfg.BindAddrArg.BindAddr.String())
23
+	}
24
+}
25
+
26
+func TestCommandBindingMaster(t *testing.T) {
27
+	valueToSet := "http://example.org:9123"
28
+	actualCfg := executeMasterCommand([]string{"--master=" + valueToSet})
29
+
30
+	expectedArgs := NewDefaultMasterArgs()
31
+	expectedArgs.MasterAddr.Set(valueToSet)
32
+
33
+	if expectedArgs.MasterAddr.String() != actualCfg.MasterAddr.String() {
34
+		t.Errorf("expected %v, got %v", expectedArgs.MasterAddr.String(), actualCfg.MasterAddr.String())
35
+	}
36
+}
37
+
38
+func TestCommandBindingMasterPublic(t *testing.T) {
39
+	valueToSet := "http://example.org:9123"
40
+	actualCfg := executeMasterCommand([]string{"--public-master=" + valueToSet})
41
+
42
+	expectedArgs := NewDefaultMasterArgs()
43
+	expectedArgs.MasterPublicAddr.Set(valueToSet)
44
+
45
+	if expectedArgs.MasterPublicAddr.String() != actualCfg.MasterPublicAddr.String() {
46
+		t.Errorf("expected %v, got %v", expectedArgs.MasterPublicAddr.String(), actualCfg.MasterPublicAddr.String())
47
+	}
48
+}
49
+
50
+func TestCommandBindingEtcd(t *testing.T) {
51
+	valueToSet := "http://example.org:9123"
52
+	actualCfg := executeMasterCommand([]string{"--etcd=" + valueToSet})
53
+
54
+	expectedArgs := NewDefaultMasterArgs()
55
+	expectedArgs.EtcdAddr.Set(valueToSet)
56
+
57
+	if expectedArgs.EtcdAddr.String() != actualCfg.EtcdAddr.String() {
58
+		t.Errorf("expected %v, got %v", expectedArgs.EtcdAddr.String(), actualCfg.EtcdAddr.String())
59
+	}
60
+}
61
+
62
+func TestCommandBindingKubernetes(t *testing.T) {
63
+	valueToSet := "http://example.org:9123"
64
+	actualCfg := executeMasterCommand([]string{"--kubernetes=" + valueToSet})
65
+
66
+	expectedArgs := NewDefaultMasterArgs()
67
+	expectedArgs.KubeConnectionArgs.KubernetesAddr.Set(valueToSet)
68
+
69
+	if expectedArgs.KubeConnectionArgs.KubernetesAddr.String() != actualCfg.KubeConnectionArgs.KubernetesAddr.String() {
70
+		t.Errorf("expected %v, got %v", expectedArgs.KubeConnectionArgs.KubernetesAddr.String(), actualCfg.KubeConnectionArgs.KubernetesAddr.String())
71
+	}
72
+}
73
+
74
+func TestCommandBindingKubernetesPublic(t *testing.T) {
75
+	valueToSet := "http://example.org:9123"
76
+	actualCfg := executeMasterCommand([]string{"--public-kubernetes=" + valueToSet})
77
+
78
+	expectedArgs := NewDefaultMasterArgs()
79
+	expectedArgs.KubernetesPublicAddr.Set(valueToSet)
80
+
81
+	if expectedArgs.KubernetesPublicAddr.String() != actualCfg.KubernetesPublicAddr.String() {
82
+		t.Errorf("expected %v, got %v", expectedArgs.KubernetesPublicAddr.String(), actualCfg.KubernetesPublicAddr.String())
83
+	}
84
+}
85
+
86
+func TestCommandBindingPortalNet(t *testing.T) {
87
+	valueToSet := "192.168.0.0/16"
88
+	actualCfg := executeMasterCommand([]string{"--portal-net=" + valueToSet})
89
+
90
+	expectedArgs := NewDefaultMasterArgs()
91
+	expectedArgs.PortalNet.Set(valueToSet)
92
+
93
+	if expectedArgs.PortalNet.String() != actualCfg.PortalNet.String() {
94
+		t.Errorf("expected %v, got %v", expectedArgs.PortalNet.String(), actualCfg.PortalNet.String())
95
+	}
96
+}
97
+
98
+func TestCommandBindingImageTemplateFormat(t *testing.T) {
99
+	valueToSet := "some-format-string"
100
+	actualCfg := executeMasterCommand([]string{"--images=" + valueToSet})
101
+
102
+	expectedArgs := NewDefaultMasterArgs()
103
+	expectedArgs.ImageFormatArgs.ImageTemplate.Format = valueToSet
104
+
105
+	if expectedArgs.ImageFormatArgs.ImageTemplate.Format != actualCfg.ImageFormatArgs.ImageTemplate.Format {
106
+		t.Errorf("expected %v, got %v", expectedArgs.ImageFormatArgs.ImageTemplate.Format, actualCfg.ImageFormatArgs.ImageTemplate.Format)
107
+	}
108
+}
109
+
110
+func TestCommandBindingImageLatest(t *testing.T) {
111
+	expectedArgs := NewDefaultMasterArgs()
112
+
113
+	valueToSet := strconv.FormatBool(!expectedArgs.ImageFormatArgs.ImageTemplate.Latest)
114
+	actualCfg := executeMasterCommand([]string{"--latest-images=" + valueToSet})
115
+
116
+	expectedArgs.ImageFormatArgs.ImageTemplate.Latest = !expectedArgs.ImageFormatArgs.ImageTemplate.Latest
117
+
118
+	if expectedArgs.ImageFormatArgs.ImageTemplate.Latest != actualCfg.ImageFormatArgs.ImageTemplate.Latest {
119
+		t.Errorf("expected %v, got %v", expectedArgs.ImageFormatArgs.ImageTemplate.Latest, actualCfg.ImageFormatArgs.ImageTemplate.Latest)
120
+	}
121
+}
122
+
123
+func TestCommandBindingVolumeDir(t *testing.T) {
124
+	valueToSet := "some-string"
125
+	actualCfg := executeNodeCommand([]string{"--volume-dir=" + valueToSet})
126
+
127
+	expectedArgs := NewDefaultNodeArgs()
128
+	expectedArgs.VolumeDir = valueToSet
129
+
130
+	if expectedArgs.VolumeDir != actualCfg.VolumeDir {
131
+		t.Errorf("expected %v, got %v", expectedArgs.VolumeDir, actualCfg.VolumeDir)
132
+	}
133
+}
134
+
135
+func TestCommandBindingEtcdDir(t *testing.T) {
136
+	valueToSet := "some-string"
137
+	actualCfg := executeMasterCommand([]string{"--etcd-dir=" + valueToSet})
138
+
139
+	expectedArgs := NewDefaultMasterArgs()
140
+	expectedArgs.EtcdDir = valueToSet
141
+
142
+	if expectedArgs.EtcdDir != actualCfg.EtcdDir {
143
+		t.Errorf("expected %v, got %v", expectedArgs.EtcdDir, actualCfg.EtcdDir)
144
+	}
145
+}
146
+
147
+func TestCommandBindingCertDir(t *testing.T) {
148
+	valueToSet := "some-string"
149
+	actualCfg := executeMasterCommand([]string{"--cert-dir=" + valueToSet})
150
+
151
+	expectedArgs := NewDefaultMasterArgs()
152
+	expectedArgs.CertArgs.CertDir = valueToSet
153
+
154
+	if expectedArgs.CertArgs.CertDir != actualCfg.CertArgs.CertDir {
155
+		t.Errorf("expected %v, got %v", expectedArgs.CertArgs.CertDir, actualCfg.CertArgs.CertDir)
156
+	}
157
+}
158
+
159
+func TestCommandBindingHostname(t *testing.T) {
160
+	valueToSet := "some-string"
161
+	actualCfg := executeNodeCommand([]string{"--hostname=" + valueToSet})
162
+
163
+	expectedArgs := NewDefaultNodeArgs()
164
+	expectedArgs.NodeName = valueToSet
165
+
166
+	if expectedArgs.NodeName != actualCfg.NodeName {
167
+		t.Errorf("expected %v, got %v", expectedArgs.NodeName, actualCfg.NodeName)
168
+	}
169
+}
170
+
171
+// AllInOne always adds the default hostname
172
+func TestCommandBindingNodesForAllInOneAppend(t *testing.T) {
173
+	valueToSet := "first,second,third"
174
+	actualMasterCfg, actualNodeConfig := executeAllInOneCommand([]string{"--nodes=" + valueToSet})
175
+
176
+	expectedArgs := NewDefaultMasterArgs()
177
+
178
+	stringList := util.StringList{}
179
+	stringList.Set(valueToSet + "," + strings.ToLower(actualNodeConfig.NodeName))
180
+	expectedArgs.NodeList.Set(strings.Join(util.NewStringSet(stringList...).List(), ","))
181
+
182
+	if expectedArgs.NodeList.String() != actualMasterCfg.NodeList.String() {
183
+		t.Errorf("expected %v, got %v", expectedArgs.NodeList, actualMasterCfg.NodeList)
184
+	}
185
+}
186
+
187
+// AllInOne always adds the default hostname
188
+func TestCommandBindingNodesForAllInOneAppendNoDupes(t *testing.T) {
189
+	valueToSet := "first,localhost,second,third"
190
+	actualMasterCfg, _ := executeAllInOneCommand([]string{"--nodes=" + valueToSet, "--hostname=LOCALHOST"})
191
+
192
+	expectedArgs := NewDefaultMasterArgs()
193
+	expectedArgs.NodeList.Set(valueToSet)
194
+
195
+	util.NewStringSet()
196
+
197
+	if expectedArgs.NodeList.String() != actualMasterCfg.NodeList.String() {
198
+		t.Errorf("expected %v, got %v", expectedArgs.NodeList, actualMasterCfg.NodeList)
199
+	}
200
+}
201
+
202
+// AllInOne always adds the default hostname
203
+func TestCommandBindingNodesDefaultingAllInOne(t *testing.T) {
204
+	actualMasterCfg, _ := executeAllInOneCommand([]string{})
205
+
206
+	expectedArgs := NewDefaultMasterArgs()
207
+	expectedNodeArgs := NewDefaultNodeArgs()
208
+	expectedArgs.NodeList.Set(strings.ToLower(expectedNodeArgs.NodeName))
209
+
210
+	if expectedArgs.NodeList.String() != actualMasterCfg.NodeList.String() {
211
+		t.Errorf("expected %v, got %v", expectedArgs.NodeList, actualMasterCfg.NodeList)
212
+	}
213
+}
214
+
215
+// explicit start master never modifies the NodeList
216
+func TestCommandBindingNodesForMaster(t *testing.T) {
217
+	valueToSet := "first,second,third"
218
+	actualCfg := executeMasterCommand([]string{"master", "--nodes=" + valueToSet})
219
+
220
+	expectedArgs := NewDefaultMasterArgs()
221
+	expectedArgs.NodeList.Set(valueToSet)
222
+
223
+	if expectedArgs.NodeList.String() != actualCfg.NodeList.String() {
224
+		t.Errorf("expected %v, got %v", expectedArgs.NodeList, actualCfg.NodeList)
225
+	}
226
+}
227
+
228
+// explicit start master never modifies the NodeList
229
+func TestCommandBindingNodesDefaultingMaster(t *testing.T) {
230
+	actualCfg := executeMasterCommand([]string{"master"})
231
+
232
+	expectedArgs := NewDefaultMasterArgs()
233
+	expectedArgs.NodeList.Set("")
234
+
235
+	if expectedArgs.NodeList.String() != actualCfg.NodeList.String() {
236
+		t.Errorf("expected %v, got %v", expectedArgs.NodeList, actualCfg.NodeList)
237
+	}
238
+}
239
+
240
+func TestCommandBindingCors(t *testing.T) {
241
+	valueToSet := "first,second,third"
242
+	actualCfg := executeMasterCommand([]string{"--cors-allowed-origins=" + valueToSet})
243
+
244
+	expectedArgs := NewDefaultMasterArgs()
245
+	expectedArgs.CORSAllowedOrigins.Set(valueToSet)
246
+
247
+	if expectedArgs.CORSAllowedOrigins.String() != actualCfg.CORSAllowedOrigins.String() {
248
+		t.Errorf("expected %v, got %v", expectedArgs.CORSAllowedOrigins, actualCfg.CORSAllowedOrigins)
249
+	}
250
+}
251
+
252
+func executeMasterCommand(args []string) *MasterArgs {
253
+	fakeConfigFile, _ := ioutil.TempFile("", "")
254
+	defer os.Remove(fakeConfigFile.Name())
255
+
256
+	argsToUse := make([]string, 0, 4+len(args))
257
+	argsToUse = append(argsToUse, "master")
258
+	argsToUse = append(argsToUse, args...)
259
+	argsToUse = append(argsToUse, "--write-config")
260
+	argsToUse = append(argsToUse, "--create-certs=false")
261
+	argsToUse = append(argsToUse, "--config="+fakeConfigFile.Name())
262
+
263
+	root := &cobra.Command{
264
+		Use:   "openshift",
265
+		Short: "test",
266
+		Long:  "",
267
+		Run: func(c *cobra.Command, args []string) {
268
+			c.Help()
269
+		},
270
+	}
271
+
272
+	openshiftStartCommand, cfg := NewCommandStartMaster()
273
+	root.AddCommand(openshiftStartCommand)
274
+	root.SetArgs(argsToUse)
275
+	root.Execute()
276
+
277
+	return cfg.MasterArgs
278
+}
279
+
280
+func executeAllInOneCommand(args []string) (*MasterArgs, *NodeArgs) {
281
+	fakeMasterConfigFile, _ := ioutil.TempFile("", "")
282
+	defer os.Remove(fakeMasterConfigFile.Name())
283
+	fakeNodeConfigFile, _ := ioutil.TempFile("", "")
284
+	defer os.Remove(fakeNodeConfigFile.Name())
285
+
286
+	argsToUse := make([]string, 0, 4+len(args))
287
+	argsToUse = append(argsToUse, "start")
288
+	argsToUse = append(argsToUse, args...)
289
+	argsToUse = append(argsToUse, "--write-config")
290
+	argsToUse = append(argsToUse, "--create-certs=false")
291
+	argsToUse = append(argsToUse, "--master-config="+fakeMasterConfigFile.Name())
292
+	argsToUse = append(argsToUse, "--node-config="+fakeNodeConfigFile.Name())
293
+
294
+	root := &cobra.Command{
295
+		Use:   "openshift",
296
+		Short: "test",
297
+		Long:  "",
298
+		Run: func(c *cobra.Command, args []string) {
299
+			c.Help()
300
+		},
301
+	}
302
+
303
+	openshiftStartCommand, cfg := NewCommandStartAllInOne()
304
+	root.AddCommand(openshiftStartCommand)
305
+	root.SetArgs(argsToUse)
306
+	root.Execute()
307
+
308
+	return cfg.MasterArgs, cfg.NodeArgs
309
+}
310
+
311
+func executeNodeCommand(args []string) *NodeArgs {
312
+	fakeConfigFile, _ := ioutil.TempFile("", "")
313
+	defer os.Remove(fakeConfigFile.Name())
314
+
315
+	argsToUse := make([]string, 0, 4+len(args))
316
+	argsToUse = append(argsToUse, "node")
317
+	argsToUse = append(argsToUse, args...)
318
+	argsToUse = append(argsToUse, "--write-config")
319
+	argsToUse = append(argsToUse, "--create-certs=false")
320
+	argsToUse = append(argsToUse, "--config="+fakeConfigFile.Name())
321
+
322
+	root := &cobra.Command{
323
+		Use:   "openshift",
324
+		Short: "test",
325
+		Long:  "",
326
+		Run: func(c *cobra.Command, args []string) {
327
+			c.Help()
328
+		},
329
+	}
330
+
331
+	openshiftStartCommand, cfg := NewCommandStartNode()
332
+	root.AddCommand(openshiftStartCommand)
333
+	root.SetArgs(argsToUse)
334
+	root.Execute()
335
+
336
+	return cfg.NodeArgs
337
+}
0 338
new file mode 100644
... ...
@@ -0,0 +1,467 @@
0
+package start
1
+
2
+import (
3
+	"testing"
4
+
5
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
6
+	clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
7
+	"github.com/openshift/origin/pkg/cmd/util"
8
+)
9
+
10
+func TestMasterPublicAddressDefaulting(t *testing.T) {
11
+	expected := "http://example.com:9012"
12
+
13
+	masterArgs := NewDefaultMasterArgs()
14
+	masterArgs.MasterAddr.Set(expected)
15
+
16
+	actual, err := masterArgs.GetMasterPublicAddress()
17
+	if err != nil {
18
+		t.Errorf("unexpected error: %v", err)
19
+	}
20
+	if expected != actual.String() {
21
+		t.Errorf("expected %v, got %v", expected, actual)
22
+	}
23
+}
24
+
25
+func TestMasterPublicAddressExplicit(t *testing.T) {
26
+	expected := "http://external.com:12445"
27
+
28
+	masterArgs := NewDefaultMasterArgs()
29
+	masterArgs.MasterAddr.Set("http://internal.com:9012")
30
+	masterArgs.MasterPublicAddr.Set(expected)
31
+
32
+	actual, err := masterArgs.GetMasterPublicAddress()
33
+	if err != nil {
34
+		t.Errorf("unexpected error: %v", err)
35
+	}
36
+	if expected != actual.String() {
37
+		t.Errorf("expected %v, got %v", expected, actual)
38
+	}
39
+}
40
+
41
+func TestAssetPublicAddressDefaulting(t *testing.T) {
42
+	master := "http://example.com:9011"
43
+	expected := "http://example.com:9012"
44
+
45
+	masterArgs := NewDefaultMasterArgs()
46
+	masterArgs.MasterAddr.Set(master)
47
+
48
+	actual, err := masterArgs.GetAssetPublicAddress()
49
+	if err != nil {
50
+		t.Errorf("unexpected error: %v", err)
51
+	}
52
+	if expected != actual.String() {
53
+		t.Errorf("expected %v, got %v", expected, actual)
54
+	}
55
+}
56
+
57
+func TestAssetPublicAddressExplicit(t *testing.T) {
58
+	master := "http://example.com:9011"
59
+	expected := "https://example.com:9014"
60
+
61
+	masterArgs := NewDefaultMasterArgs()
62
+	masterArgs.MasterAddr.Set(master)
63
+	masterArgs.AssetPublicAddr.Set(expected)
64
+
65
+	actual, err := masterArgs.GetAssetPublicAddress()
66
+	if err != nil {
67
+		t.Errorf("unexpected error: %v", err)
68
+	}
69
+	if expected != actual.String() {
70
+		t.Errorf("expected %v, got %v", expected, actual)
71
+	}
72
+}
73
+
74
+func TestAssetBindAddressDefaulting(t *testing.T) {
75
+	bind := "1.2.3.4:9011"
76
+	expected := "1.2.3.4:9012"
77
+
78
+	masterArgs := NewDefaultMasterArgs()
79
+	masterArgs.BindAddrArg.BindAddr.Set(bind)
80
+
81
+	actual := masterArgs.GetAssetBindAddress()
82
+	if expected != actual {
83
+		t.Errorf("expected %v, got %v", expected, actual)
84
+	}
85
+}
86
+
87
+func TestAssetBindAddressExplicit(t *testing.T) {
88
+	bind := "1.2.3.4:9011"
89
+	expected := "2.3.4.5:1234"
90
+
91
+	masterArgs := NewDefaultMasterArgs()
92
+	masterArgs.BindAddrArg.BindAddr.Set(bind)
93
+	masterArgs.AssetBindAddr.Set(expected)
94
+
95
+	actual := masterArgs.GetAssetBindAddress()
96
+	if expected != actual {
97
+		t.Errorf("expected %v, got %v", expected, actual)
98
+	}
99
+}
100
+
101
+func TestKubernetesPublicAddressDefaultToKubernetesAddress(t *testing.T) {
102
+	expected := "http://example.com:9012"
103
+
104
+	masterArgs := NewDefaultMasterArgs()
105
+	masterArgs.KubeConnectionArgs.KubernetesAddr.Set(expected)
106
+	masterArgs.MasterPublicAddr.Set("unexpectedpublicmaster")
107
+	masterArgs.MasterAddr.Set("unexpectedmaster")
108
+
109
+	actual, err := masterArgs.GetKubernetesPublicAddress()
110
+	if err != nil {
111
+		t.Fatalf("unexpected error: %v", err)
112
+	}
113
+	if expected != actual.String() {
114
+		t.Fatalf("expected %v, got %v", expected, actual)
115
+	}
116
+}
117
+
118
+func TestKubernetesPublicAddressDefaultToPublicMasterAddress(t *testing.T) {
119
+	expected := "http://example.com:9012"
120
+
121
+	masterArgs := NewDefaultMasterArgs()
122
+	masterArgs.MasterPublicAddr.Set(expected)
123
+	masterArgs.MasterAddr.Set("unexpectedmaster")
124
+
125
+	actual, err := masterArgs.GetKubernetesPublicAddress()
126
+	if err != nil {
127
+		t.Fatalf("unexpected error: %v", err)
128
+	}
129
+	if expected != actual.String() {
130
+		t.Fatalf("expected %v, got %v", expected, actual)
131
+	}
132
+}
133
+
134
+func TestKubernetesPublicAddressDefaultToMasterAddress(t *testing.T) {
135
+	expected := "http://example.com:9012"
136
+
137
+	masterArgs := NewDefaultMasterArgs()
138
+	masterArgs.MasterAddr.Set(expected)
139
+
140
+	actual, err := masterArgs.GetKubernetesPublicAddress()
141
+	if err != nil {
142
+		t.Fatalf("unexpected error: %v", err)
143
+	}
144
+	if expected != actual.String() {
145
+		t.Fatalf("expected %v, got %v", expected, actual)
146
+	}
147
+}
148
+
149
+func TestKubernetesPublicAddressExplicit(t *testing.T) {
150
+	expected := "http://external.com:12445"
151
+
152
+	masterArgs := NewDefaultMasterArgs()
153
+	masterArgs.MasterAddr.Set("http://internal.com:9012")
154
+	masterArgs.KubeConnectionArgs.KubernetesAddr.Set("http://internal.com:9013")
155
+	masterArgs.MasterPublicAddr.Set("http://internal.com:9014")
156
+	masterArgs.KubernetesPublicAddr.Set(expected)
157
+
158
+	actual, err := masterArgs.GetKubernetesPublicAddress()
159
+	if err != nil {
160
+		t.Fatalf("unexpected error: %v", err)
161
+	}
162
+	if expected != actual.String() {
163
+		t.Fatalf("expected %v, got %v", expected, actual)
164
+	}
165
+}
166
+
167
+func TestKubernetesAddressDefaulting(t *testing.T) {
168
+	expected := "http://example.com:9012"
169
+
170
+	masterArgs := NewDefaultMasterArgs()
171
+	masterArgs.MasterAddr.Set(expected)
172
+	masterAddr, _ := masterArgs.GetMasterAddress()
173
+
174
+	actual, err := masterArgs.KubeConnectionArgs.GetKubernetesAddress(masterAddr)
175
+	if err != nil {
176
+		t.Fatalf("unexpected error: %v", err)
177
+	}
178
+	if expected != actual.String() {
179
+		t.Fatalf("expected %v, got %v", expected, actual)
180
+	}
181
+}
182
+
183
+func TestKubernetesAddressExplicit(t *testing.T) {
184
+	expected := "http://external.com:12445"
185
+
186
+	masterArgs := NewDefaultMasterArgs()
187
+	masterArgs.MasterAddr.Set("http://internal.com:9012")
188
+	masterArgs.KubeConnectionArgs.KubernetesAddr.Set(expected)
189
+	masterAddr, _ := masterArgs.GetMasterAddress()
190
+
191
+	actual, err := masterArgs.KubeConnectionArgs.GetKubernetesAddress(masterAddr)
192
+	if err != nil {
193
+		t.Fatalf("unexpected error: %v", err)
194
+	}
195
+	if expected != actual.String() {
196
+		t.Fatalf("expected %v, got %v", expected, actual)
197
+	}
198
+}
199
+
200
+func TestEtcdAddressDefaulting(t *testing.T) {
201
+	expected := "http://example.com:4001"
202
+	master := "https://example.com:9012"
203
+
204
+	masterArgs := NewDefaultMasterArgs()
205
+	masterArgs.MasterAddr.Set(master)
206
+
207
+	actual, err := masterArgs.GetEtcdAddress()
208
+	if err != nil {
209
+		t.Fatalf("unexpected error: %v", err)
210
+	}
211
+	if expected != actual.String() {
212
+		t.Fatalf("expected %v, got %v", expected, actual)
213
+	}
214
+}
215
+
216
+func TestEtcdAddressExplicit(t *testing.T) {
217
+	expected := "http://external.com:12445"
218
+
219
+	masterArgs := NewDefaultMasterArgs()
220
+	masterArgs.MasterAddr.Set("http://internal.com:9012")
221
+	masterArgs.EtcdAddr.Set(expected)
222
+
223
+	actual, err := masterArgs.GetEtcdAddress()
224
+	if err != nil {
225
+		t.Fatalf("unexpected error: %v", err)
226
+	}
227
+	if expected != actual.String() {
228
+		t.Fatalf("expected %v, got %v", expected, actual)
229
+	}
230
+}
231
+
232
+func TestEtcdBindAddressDefault(t *testing.T) {
233
+	expected := "0.0.0.0:4001"
234
+
235
+	masterArgs := NewDefaultMasterArgs()
236
+	actual := masterArgs.GetEtcdBindAddress()
237
+	if expected != actual {
238
+		t.Fatalf("expected %v, got %v", expected, actual)
239
+	}
240
+}
241
+
242
+func TestEtcdPeerAddressDefault(t *testing.T) {
243
+	expected := "0.0.0.0:7001"
244
+
245
+	masterArgs := NewDefaultMasterArgs()
246
+	actual := masterArgs.GetEtcdPeerBindAddress()
247
+	if expected != actual {
248
+		t.Fatalf("expected %v, got %v", expected, actual)
249
+	}
250
+}
251
+
252
+func TestEtcdBindAddressDefaultToBind(t *testing.T) {
253
+	expected := "1.2.3.4:4001"
254
+
255
+	masterArgs := NewDefaultMasterArgs()
256
+	masterArgs.BindAddrArg.BindAddr.Set("https://1.2.3.4:8080")
257
+
258
+	actual := masterArgs.GetEtcdBindAddress()
259
+	if expected != actual {
260
+		t.Fatalf("expected %v, got %v", expected, actual)
261
+	}
262
+}
263
+
264
+func TestMasterAddressDefaultingToBindValues(t *testing.T) {
265
+	defaultIP, err := util.DefaultLocalIP4()
266
+	if err != nil {
267
+		t.Fatalf("unexpected error: %v", err)
268
+	}
269
+	expected := "http://" + defaultIP.String() + ":9012"
270
+
271
+	masterArgs := NewDefaultMasterArgs()
272
+	masterArgs.BindAddrArg.BindAddr.Set("http://0.0.0.0:9012")
273
+
274
+	actual, err := masterArgs.GetMasterAddress()
275
+	if err != nil {
276
+		t.Fatalf("unexpected error: %v", err)
277
+	}
278
+	if expected != actual.String() {
279
+		t.Fatalf("expected %v, got %v", expected, actual)
280
+	}
281
+}
282
+
283
+func TestMasterAddressExplicit(t *testing.T) {
284
+	expected := "http://external.com:12445"
285
+
286
+	masterArgs := NewDefaultMasterArgs()
287
+	masterArgs.MasterAddr.Set(expected)
288
+
289
+	actual, err := masterArgs.GetMasterAddress()
290
+	if err != nil {
291
+		t.Fatalf("unexpected error: %v", err)
292
+	}
293
+	if expected != actual.String() {
294
+		t.Fatalf("expected %v, got %v", expected, actual)
295
+	}
296
+}
297
+
298
+func TestKubeClientForExternalKubernetesMasterWithNoConfig(t *testing.T) {
299
+	expected := "https://localhost:8443"
300
+
301
+	masterArgs := NewDefaultMasterArgs()
302
+	masterArgs.MasterAddr.Set(expected)
303
+	masterAddr, _ := masterArgs.GetMasterAddress()
304
+
305
+	actual, err := masterArgs.KubeConnectionArgs.GetKubernetesAddress(masterAddr)
306
+	if err != nil {
307
+		t.Fatalf("unexpected error: %v", err)
308
+	}
309
+	if expected != actual.String() {
310
+		t.Fatalf("expected %v, got %v", expected, actual)
311
+	}
312
+}
313
+
314
+func TestKubeClientForNodeWithNoConfig(t *testing.T) {
315
+	expected := "https://localhost:8443"
316
+
317
+	masterArgs := NewDefaultMasterArgs()
318
+	masterArgs.MasterAddr.Set(expected)
319
+	masterAddr, _ := masterArgs.GetMasterAddress()
320
+
321
+	actual, err := masterArgs.KubeConnectionArgs.GetKubernetesAddress(masterAddr)
322
+	if err != nil {
323
+		t.Fatalf("unexpected error: %v", err)
324
+	}
325
+	if expected != actual.String() {
326
+		t.Fatalf("expected %v, got %v", expected, actual)
327
+	}
328
+}
329
+
330
+func TestKubeClientForExternalKubernetesMasterWithConfig(t *testing.T) {
331
+	expectedServer := "https://some-other-server:1234"
332
+	expectedUser := "myuser"
333
+
334
+	masterArgs := NewDefaultMasterArgs()
335
+	masterArgs.KubeConnectionArgs.ClientConfigLoadingRules, masterArgs.KubeConnectionArgs.ClientConfig = makeKubeconfig(expectedServer, expectedUser)
336
+
337
+	actualPublic, err := masterArgs.GetKubernetesPublicAddress()
338
+	if err != nil {
339
+		t.Fatalf("unexpected error: %v", err)
340
+	}
341
+	if expectedServer != actualPublic.String() {
342
+		t.Fatalf("expected %v, got %v", expectedServer, actualPublic)
343
+	}
344
+
345
+	masterAddr, _ := masterArgs.GetMasterAddress()
346
+
347
+	actual, err := masterArgs.KubeConnectionArgs.GetKubernetesAddress(masterAddr)
348
+	if err != nil {
349
+		t.Fatalf("unexpected error: %v", err)
350
+	}
351
+	if expectedServer != actual.String() {
352
+		t.Fatalf("expected %v, got %v", expectedServer, actual)
353
+	}
354
+}
355
+
356
+func TestKubeClientForNodeWithConfig(t *testing.T) {
357
+	expectedServer := "https://some-other-server:1234"
358
+	expectedUser := "myuser"
359
+
360
+	nodeArgs := NewDefaultNodeArgs()
361
+	nodeArgs.KubeConnectionArgs.ClientConfigLoadingRules, nodeArgs.KubeConnectionArgs.ClientConfig = makeKubeconfig(expectedServer, expectedUser)
362
+
363
+	actual, err := nodeArgs.KubeConnectionArgs.GetKubernetesAddress(nil)
364
+	if err != nil {
365
+		t.Fatalf("unexpected error: %v", err)
366
+	}
367
+	if expectedServer != actual.String() {
368
+		t.Fatalf("expected %v, got %v", expectedServer, actual)
369
+	}
370
+}
371
+
372
+func TestKubeClientForExternalKubernetesMasterWithErrorKubeconfig(t *testing.T) {
373
+	masterArgs := NewDefaultMasterArgs()
374
+	masterArgs.KubeConnectionArgs.ClientConfigLoadingRules, masterArgs.KubeConnectionArgs.ClientConfig = makeErrorKubeconfig()
375
+
376
+	// GetKubernetesPublicAddress hits the invalid kubeconfig in the fallback chain
377
+	_, err := masterArgs.GetKubernetesPublicAddress()
378
+	if err == nil {
379
+		t.Fatalf("expected error, got none")
380
+	}
381
+
382
+	// GetKubernetesAddress hits the invalid kubeconfig in the fallback chain
383
+	masterAddr, _ := masterArgs.GetMasterAddress()
384
+	_, err = masterArgs.KubeConnectionArgs.GetKubernetesAddress(masterAddr)
385
+	if err == nil {
386
+		t.Fatalf("expected error, got none")
387
+	}
388
+}
389
+
390
+func TestKubeClientForExternalKubernetesMasterWithConflictingKubernetesAddress(t *testing.T) {
391
+	expectedServer := "https://some-other-server:1234"
392
+	expectedUser := "myuser"
393
+
394
+	masterArgs := NewDefaultMasterArgs()
395
+	// Explicitly set --kubernetes must match --kubeconfig or return an error
396
+	masterArgs.KubeConnectionArgs.KubernetesAddr.Set(expectedServer)
397
+	masterArgs.KubeConnectionArgs.ClientConfigLoadingRules, masterArgs.KubeConnectionArgs.ClientConfig = makeKubeconfig("https://another-server:2345", expectedUser)
398
+
399
+	// GetKubernetesAddress returns the explicitly set address
400
+	masterAddr, _ := masterArgs.GetMasterAddress()
401
+	actual, err := masterArgs.KubeConnectionArgs.GetKubernetesAddress(masterAddr)
402
+	if err != nil {
403
+		t.Fatalf("unexpected error: %v", err)
404
+	}
405
+	if expectedServer != actual.String() {
406
+		t.Fatalf("expected %v, got %v", expectedServer, actual)
407
+	}
408
+}
409
+
410
+func TestKubeClientForNodeWithConflictingKubernetesAddress(t *testing.T) {
411
+	expectedServer := "https://some-other-server:1234"
412
+	expectedUser := "myuser"
413
+
414
+	nodeArgs := NewDefaultNodeArgs()
415
+	nodeArgs.KubeConnectionArgs.KubernetesAddr.Set(expectedServer)
416
+	nodeArgs.KubeConnectionArgs.ClientConfigLoadingRules, nodeArgs.KubeConnectionArgs.ClientConfig = makeKubeconfig("https://another-server:2345", expectedUser)
417
+
418
+	// GetKubernetesAddress returns the explicitly set address
419
+	actualServer, err := nodeArgs.KubeConnectionArgs.GetKubernetesAddress(nil)
420
+	if err != nil {
421
+		t.Fatalf("unexpected error: %v", err)
422
+	}
423
+	if expectedServer != actualServer.String() {
424
+		t.Fatalf("expected %v, got %v", expectedServer, actualServer)
425
+	}
426
+}
427
+
428
+func makeEmptyKubeconfig() (clientcmd.ClientConfigLoadingRules, clientcmd.ClientConfig) {
429
+	// Set a non-empty CommandLinePath to trigger loading
430
+	loadingRules := clientcmd.ClientConfigLoadingRules{CommandLinePath: "specified"}
431
+
432
+	clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
433
+		// Set empty loading rules to avoid missing file errors
434
+		&clientcmd.ClientConfigLoadingRules{},
435
+		&clientcmd.ConfigOverrides{},
436
+	)
437
+	return loadingRules, clientConfig
438
+}
439
+
440
+func makeErrorKubeconfig() (clientcmd.ClientConfigLoadingRules, clientcmd.ClientConfig) {
441
+	// Set a non-empty CommandLinePath to trigger loading
442
+	loadingRules := clientcmd.ClientConfigLoadingRules{CommandLinePath: "missing-file"}
443
+
444
+	clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
445
+		&loadingRules,
446
+		&clientcmd.ConfigOverrides{},
447
+	)
448
+	return loadingRules, clientConfig
449
+}
450
+
451
+func makeKubeconfig(server, user string) (clientcmd.ClientConfigLoadingRules, clientcmd.ClientConfig) {
452
+	// Set a non-empty CommandLinePath to trigger loading
453
+	loadingRules := clientcmd.ClientConfigLoadingRules{CommandLinePath: "specified"}
454
+
455
+	clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
456
+		// Set empty loading rules to avoid missing file errors
457
+		&clientcmd.ClientConfigLoadingRules{},
458
+		// Override the server and user in client config to simulate loading from a file
459
+		&clientcmd.ConfigOverrides{
460
+			ClusterInfo: clientcmdapi.Cluster{Server: server},
461
+			AuthInfo:    clientcmdapi.AuthInfo{Username: user},
462
+		},
463
+	)
464
+
465
+	return loadingRules, clientConfig
466
+}
0 467
new file mode 100644
... ...
@@ -0,0 +1,25 @@
0
+package start
1
+
2
+import (
3
+	"github.com/spf13/pflag"
4
+
5
+	"github.com/openshift/origin/pkg/cmd/util/variable"
6
+)
7
+
8
+// OriginMasterArgs is a struct that the command stores flag values into.
9
+type ImageFormatArgs struct {
10
+	ImageTemplate variable.ImageTemplate
11
+}
12
+
13
+func BindImageFormatArgs(args *ImageFormatArgs, flags *pflag.FlagSet, prefix string) {
14
+	flags.StringVar(&args.ImageTemplate.Format, "images", args.ImageTemplate.Format, "When fetching images used by the cluster for important components, use this format on both master and nodes. The latest release will be used by default.")
15
+	flags.BoolVar(&args.ImageTemplate.Latest, "latest-images", args.ImageTemplate.Latest, "If true, attempt to use the latest images for the cluster instead of the latest release.")
16
+}
17
+
18
+func NewDefaultImageFormatArgs() *ImageFormatArgs {
19
+	config := &ImageFormatArgs{
20
+		ImageTemplate: variable.NewDefaultImageTemplate(),
21
+	}
22
+
23
+	return config
24
+}
0 25
new file mode 100644
... ...
@@ -0,0 +1,72 @@
0
+package start
1
+
2
+import (
3
+	"errors"
4
+	"net/url"
5
+
6
+	"github.com/spf13/pflag"
7
+
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
10
+
11
+	"github.com/openshift/origin/pkg/cmd/flagtypes"
12
+)
13
+
14
+type KubeConnectionArgs struct {
15
+	KubernetesAddr flagtypes.Addr
16
+
17
+	// ClientConfig is used when connecting to Kubernetes from the master, or
18
+	// when connecting to the master from a detached node. If StartKube is true,
19
+	// this value is not used.
20
+	ClientConfig clientcmd.ClientConfig
21
+	// ClientConfigLoadingRules is the ruleset used to load the client config.
22
+	// Only the CommandLinePath is expected to be used.
23
+	ClientConfigLoadingRules clientcmd.ClientConfigLoadingRules
24
+
25
+	CertArgs *CertArgs
26
+}
27
+
28
+func BindKubeConnectionArgs(args *KubeConnectionArgs, flags *pflag.FlagSet, prefix string) {
29
+	flags.Var(&args.KubernetesAddr, prefix+"kubernetes", "The address of the Kubernetes server (host, host:port, or URL). If specified, no Kubernetes components will be started.")
30
+	flags.StringVar(&args.ClientConfigLoadingRules.CommandLinePath, prefix+"kubeconfig", "", "Path to the kubeconfig file to use for requests to the Kubernetes API.")
31
+}
32
+
33
+func NewDefaultKubeConnectionArgs() *KubeConnectionArgs {
34
+	config := &KubeConnectionArgs{}
35
+
36
+	config.KubernetesAddr = flagtypes.Addr{Value: "localhost:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default()
37
+	config.ClientConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&config.ClientConfigLoadingRules, &clientcmd.ConfigOverrides{})
38
+	config.CertArgs = NewDefaultCertArgs()
39
+
40
+	return config
41
+}
42
+
43
+func (args KubeConnectionArgs) GetExternalKubernetesClientConfig() (*client.Config, bool, error) {
44
+	if len(args.ClientConfigLoadingRules.CommandLinePath) == 0 || args.ClientConfig == nil {
45
+		return nil, false, nil
46
+	}
47
+	clientConfig, err := args.ClientConfig.ClientConfig()
48
+	if err != nil {
49
+		return nil, false, err
50
+	}
51
+	return clientConfig, true, nil
52
+}
53
+
54
+func (args KubeConnectionArgs) GetKubernetesAddress(defaultAddress *url.URL) (*url.URL, error) {
55
+	if args.KubernetesAddr.Provided {
56
+		return args.KubernetesAddr.URL, nil
57
+	}
58
+
59
+	config, ok, err := args.GetExternalKubernetesClientConfig()
60
+	if err != nil {
61
+		return nil, err
62
+	}
63
+	if ok && len(config.Host) > 0 {
64
+		return url.Parse(config.Host)
65
+	}
66
+
67
+	if defaultAddress == nil {
68
+		return nil, errors.New("no default KubernetesAddress present")
69
+	}
70
+	return defaultAddress, nil
71
+}
0 72
new file mode 100644
... ...
@@ -0,0 +1,412 @@
0
+package start
1
+
2
+import (
3
+	"fmt"
4
+	"net"
5
+	"net/url"
6
+	"strconv"
7
+
8
+	"github.com/ghodss/yaml"
9
+	"github.com/spf13/pflag"
10
+
11
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
12
+
13
+	"github.com/openshift/origin/pkg/cmd/flagtypes"
14
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
15
+	latestconfigapi "github.com/openshift/origin/pkg/cmd/server/api/latest"
16
+	"github.com/openshift/origin/pkg/cmd/server/certs"
17
+	cmdutil "github.com/openshift/origin/pkg/cmd/util"
18
+)
19
+
20
+// MasterArgs is a struct that the command stores flag values into.  It holds a partially complete set of parameters for starting the master
21
+// This object should hold the common set values, but not attempt to handle all cases.  The expected path is to use this object to create
22
+// a fully specified config later on.  If you need something not set here, then create a fully specified config file and pass that as argument
23
+// to starting the master.
24
+type MasterArgs struct {
25
+	MasterAddr flagtypes.Addr
26
+	EtcdAddr   flagtypes.Addr
27
+	PortalNet  flagtypes.IPNet
28
+	// addresses for external clients
29
+	MasterPublicAddr     flagtypes.Addr
30
+	AssetPublicAddr      flagtypes.Addr
31
+	KubernetesPublicAddr flagtypes.Addr
32
+
33
+	// AssetBindAddr exposed for integration tests to set
34
+	AssetBindAddr flagtypes.Addr
35
+	// DNSBindAddr exposed for integration tests to set
36
+	DNSBindAddr flagtypes.Addr
37
+
38
+	EtcdDir string
39
+
40
+	NodeList util.StringList
41
+
42
+	CORSAllowedOrigins util.StringList
43
+
44
+	BindAddrArg        *BindAddrArg
45
+	ImageFormatArgs    *ImageFormatArgs
46
+	KubeConnectionArgs *KubeConnectionArgs
47
+	CertArgs           *CertArgs
48
+}
49
+
50
+// BindMasterArgs binds the options to the flags with prefix + default flag names
51
+func BindMasterArgs(args *MasterArgs, flags *pflag.FlagSet, prefix string) {
52
+	flags.Var(&args.MasterAddr, prefix+"master", "The master address for use by OpenShift components (host, host:port, or URL). Scheme and port default to the --listen scheme and port.")
53
+	flags.Var(&args.MasterPublicAddr, prefix+"public-master", "The master address for use by public clients, if different (host, host:port, or URL). Defaults to same as --master.")
54
+	flags.Var(&args.EtcdAddr, prefix+"etcd", "The address of the etcd server (host, host:port, or URL). If specified, no built-in etcd will be started.")
55
+	flags.Var(&args.KubernetesPublicAddr, prefix+"public-kubernetes", "The Kubernetes server address for use by public clients, if different. (host, host:port, or URL). Defaults to same as --kubernetes.")
56
+	flags.Var(&args.PortalNet, prefix+"portal-net", "A CIDR notation IP range from which to assign portal IPs. This must not overlap with any IP ranges assigned to nodes for pods.")
57
+
58
+	flags.StringVar(&args.EtcdDir, prefix+"etcd-dir", "openshift.local.etcd", "The etcd data directory.")
59
+
60
+	flags.Var(&args.NodeList, prefix+"nodes", "The hostnames of each node. This currently must be specified up front. Comma delimited list")
61
+	flags.Var(&args.CORSAllowedOrigins, prefix+"cors-allowed-origins", "List of allowed origins for CORS, comma separated.  An allowed origin can be a regular expression to support subdomain matching.  CORS is enabled for localhost, 127.0.0.1, and the asset server by default.")
62
+}
63
+
64
+// NewDefaultMasterArgs creates MasterArgs with sub-objects created and default values set.
65
+func NewDefaultMasterArgs() *MasterArgs {
66
+	config := &MasterArgs{
67
+		MasterAddr:           flagtypes.Addr{Value: "localhost:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default(),
68
+		EtcdAddr:             flagtypes.Addr{Value: "0.0.0.0:4001", DefaultScheme: "http", DefaultPort: 4001}.Default(),
69
+		PortalNet:            flagtypes.DefaultIPNet("172.30.17.0/24"),
70
+		MasterPublicAddr:     flagtypes.Addr{Value: "localhost:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default(),
71
+		KubernetesPublicAddr: flagtypes.Addr{Value: "localhost:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default(),
72
+		AssetPublicAddr:      flagtypes.Addr{Value: "localhost:8444", DefaultScheme: "https", DefaultPort: 8444, AllowPrefix: true}.Default(),
73
+		AssetBindAddr:        flagtypes.Addr{Value: "0.0.0.0:8444", DefaultScheme: "https", DefaultPort: 8444, AllowPrefix: true}.Default(),
74
+		DNSBindAddr:          flagtypes.Addr{Value: "0.0.0.0:53", DefaultScheme: "http", DefaultPort: 53, AllowPrefix: true}.Default(),
75
+
76
+		BindAddrArg:        NewDefaultBindAddrArg(),
77
+		ImageFormatArgs:    NewDefaultImageFormatArgs(),
78
+		KubeConnectionArgs: NewDefaultKubeConnectionArgs(),
79
+		CertArgs:           NewDefaultCertArgs(),
80
+	}
81
+
82
+	return config
83
+}
84
+
85
+// BuildSerializeableMasterConfig takes the MasterArgs (partially complete config) and uses them along with defaulting behavior to create the fully specified
86
+// config object for starting the master
87
+func (args MasterArgs) BuildSerializeableMasterConfig() (*configapi.MasterConfig, error) {
88
+	masterAddr, err := args.GetMasterAddress()
89
+	if err != nil {
90
+		return nil, err
91
+	}
92
+	masterPublicAddr, err := args.GetMasterPublicAddress()
93
+	if err != nil {
94
+		return nil, err
95
+	}
96
+	kubePublicAddr, err := args.GetKubernetesPublicAddress()
97
+	if err != nil {
98
+		return nil, err
99
+	}
100
+	assetPublicAddr, err := args.GetAssetPublicAddress()
101
+	if err != nil {
102
+		return nil, err
103
+	}
104
+	dnsBindAddr, err := args.GetDNSBindAddress()
105
+	if err != nil {
106
+		return nil, err
107
+	}
108
+
109
+	corsAllowedOrigins := []string{}
110
+	corsAllowedOrigins = append(corsAllowedOrigins, args.CORSAllowedOrigins...)
111
+	// always include the all-in-one server's web console as an allowed CORS origin
112
+	// always include localhost as an allowed CORS origin
113
+	// always include master public address as an allowed CORS origin
114
+	for _, origin := range []string{assetPublicAddr.Host, masterPublicAddr.Host, "localhost", "127.0.0.1"} {
115
+		corsAllowedOrigins = append(corsAllowedOrigins, origin)
116
+	}
117
+
118
+	etcdAddress, err := args.GetEtcdAddress()
119
+	if err != nil {
120
+		return nil, err
121
+	}
122
+
123
+	var etcdConfig *configapi.EtcdConfig
124
+	if !args.EtcdAddr.Provided {
125
+		etcdConfig, err = args.BuildSerializeableEtcdConfig()
126
+		if err != nil {
127
+			return nil, err
128
+		}
129
+	}
130
+	var kubernetesMasterConfig *configapi.KubernetesMasterConfig
131
+	if !args.KubeConnectionArgs.KubernetesAddr.Provided && len(args.KubeConnectionArgs.ClientConfigLoadingRules.CommandLinePath) == 0 {
132
+		kubernetesMasterConfig, err = args.BuildSerializeableKubeMasterConfig()
133
+		if err != nil {
134
+			return nil, err
135
+		}
136
+	}
137
+
138
+	config := &configapi.MasterConfig{
139
+		ServingInfo: configapi.ServingInfo{
140
+			BindAddress: args.BindAddrArg.BindAddr.URL.Host,
141
+			ServerCert:  certs.DefaultMasterServingCertInfo(args.CertArgs.CertDir),
142
+			ClientCA:    certs.DefaultRootCAFile(args.CertArgs.CertDir),
143
+		},
144
+		CORSAllowedOrigins: corsAllowedOrigins,
145
+
146
+		KubernetesMasterConfig: kubernetesMasterConfig,
147
+		EtcdConfig:             etcdConfig,
148
+
149
+		OAuthConfig: &configapi.OAuthConfig{
150
+			ProxyCA:         cmdutil.Env("OPENSHIFT_OAUTH_REQUEST_HEADER_CA_FILE", ""),
151
+			MasterURL:       masterAddr.String(),
152
+			MasterPublicURL: masterPublicAddr.String(),
153
+			AssetPublicURL:  assetPublicAddr.String(),
154
+		},
155
+
156
+		AssetConfig: &configapi.AssetConfig{
157
+			ServingInfo: configapi.ServingInfo{
158
+				BindAddress: args.GetAssetBindAddress(),
159
+				ServerCert:  certs.DefaultAssetServingCertInfo(args.CertArgs.CertDir),
160
+				ClientCA:    certs.DefaultRootCAFile(args.CertArgs.CertDir),
161
+			},
162
+
163
+			LogoutURI:           cmdutil.Env("OPENSHIFT_LOGOUT_URI", ""),
164
+			MasterPublicURL:     masterPublicAddr.String(),
165
+			PublicURL:           assetPublicAddr.String(),
166
+			KubernetesPublicURL: kubePublicAddr.String(),
167
+		},
168
+
169
+		DNSConfig: &configapi.DNSConfig{
170
+			BindAddress: dnsBindAddr.URL.Host,
171
+		},
172
+
173
+		MasterClients: configapi.MasterClients{
174
+			DeployerKubeConfig:          certs.DefaultKubeConfigFilename(args.CertArgs.CertDir, "openshift-deployer"),
175
+			OpenShiftLoopbackKubeConfig: certs.DefaultKubeConfigFilename(args.CertArgs.CertDir, "openshift-client"),
176
+			KubernetesKubeConfig:        certs.DefaultKubeConfigFilename(args.CertArgs.CertDir, "kube-client"),
177
+		},
178
+
179
+		EtcdClientInfo: configapi.RemoteConnectionInfo{
180
+			URL: etcdAddress.String(),
181
+			// TODO allow for https etcd
182
+			CA:         "",
183
+			ClientCert: configapi.CertInfo{},
184
+		},
185
+
186
+		MasterAuthorizationNamespace:      "master",
187
+		OpenShiftSharedResourcesNamespace: "openshift",
188
+
189
+		ImageConfig: configapi.ImageConfig{
190
+			Format: args.ImageFormatArgs.ImageTemplate.Format,
191
+			Latest: args.ImageFormatArgs.ImageTemplate.Latest,
192
+		},
193
+	}
194
+
195
+	return config, nil
196
+}
197
+
198
+// BuildSerializeableEtcdConfig creates a fully specified etcd startup configuration based on MasterArgs
199
+func (args MasterArgs) BuildSerializeableEtcdConfig() (*configapi.EtcdConfig, error) {
200
+	etcdAddr, err := args.GetEtcdAddress()
201
+	if err != nil {
202
+		return nil, err
203
+	}
204
+
205
+	config := &configapi.EtcdConfig{
206
+		ServingInfo: configapi.ServingInfo{
207
+			BindAddress: args.GetEtcdBindAddress(),
208
+		},
209
+		PeerAddress:   args.GetEtcdPeerBindAddress(),
210
+		MasterAddress: etcdAddr.Host,
211
+		StorageDir:    args.EtcdDir,
212
+	}
213
+
214
+	return config, nil
215
+}
216
+
217
+// BuildSerializeableKubeMasterConfig creates a fully specified kubernetes master startup configuration based on MasterArgs
218
+func (args MasterArgs) BuildSerializeableKubeMasterConfig() (*configapi.KubernetesMasterConfig, error) {
219
+	servicesSubnet := net.IPNet(args.PortalNet)
220
+
221
+	config := &configapi.KubernetesMasterConfig{
222
+		ServicesSubnet:  servicesSubnet.String(),
223
+		StaticNodeNames: args.NodeList,
224
+	}
225
+
226
+	return config, nil
227
+}
228
+
229
+// GetServerCertHostnames returns the set of hostnames that any serving certificate for master needs to be valid for.
230
+func (args MasterArgs) GetServerCertHostnames() (util.StringSet, error) {
231
+	masterAddr, err := args.GetMasterAddress()
232
+	if err != nil {
233
+		return nil, err
234
+	}
235
+	masterPublicAddr, err := args.GetMasterPublicAddress()
236
+	if err != nil {
237
+		return nil, err
238
+	}
239
+	kubePublicAddr, err := args.GetKubernetesPublicAddress()
240
+	if err != nil {
241
+		return nil, err
242
+	}
243
+	assetPublicAddr, err := args.GetAssetPublicAddress()
244
+	if err != nil {
245
+		return nil, err
246
+	}
247
+
248
+	// 172.17.42.1 enables the router to call back out to the master
249
+	// TODO: Remove 172.17.42.1 once we can figure out how to validate the master's cert from inside a pod, or tell pods the real IP for the master
250
+	allHostnames := util.NewStringSet("localhost", "127.0.0.1", "172.17.42.1", masterAddr.Host, masterPublicAddr.Host, kubePublicAddr.Host, assetPublicAddr.Host)
251
+	certHostnames := util.StringSet{}
252
+	for hostname := range allHostnames {
253
+		if host, _, err := net.SplitHostPort(hostname); err == nil {
254
+			// add the hostname without the port
255
+			certHostnames.Insert(host)
256
+		} else {
257
+			// add the originally specified hostname
258
+			certHostnames.Insert(hostname)
259
+		}
260
+	}
261
+
262
+	return certHostnames, nil
263
+}
264
+
265
+// GetMasterAddress checks for an unset master address and then attempts to use the first
266
+// public IPv4 non-loopback address registered on this host.
267
+// TODO: make me IPv6 safe
268
+func (args MasterArgs) GetMasterAddress() (*url.URL, error) {
269
+	if args.MasterAddr.Provided {
270
+		return args.MasterAddr.URL, nil
271
+	}
272
+
273
+	// If the user specifies a bind address, and the master is not provided, use the bind port by default
274
+	port := args.MasterAddr.Port
275
+	if args.BindAddrArg.BindAddr.Provided {
276
+		port = args.BindAddrArg.BindAddr.Port
277
+	}
278
+
279
+	// If the user specifies a bind address, and the master is not provided, use the bind scheme by default
280
+	scheme := args.MasterAddr.URL.Scheme
281
+	if args.BindAddrArg.BindAddr.Provided {
282
+		scheme = args.BindAddrArg.BindAddr.URL.Scheme
283
+	}
284
+
285
+	addr := ""
286
+	if ip, err := cmdutil.DefaultLocalIP4(); err == nil {
287
+		addr = ip.String()
288
+	} else if err == cmdutil.ErrorNoDefaultIP {
289
+		addr = "127.0.0.1"
290
+	} else if err != nil {
291
+		return nil, fmt.Errorf("Unable to find a public IP address: %v", err)
292
+	}
293
+
294
+	masterAddr := scheme + "://" + net.JoinHostPort(addr, strconv.Itoa(port))
295
+	return url.Parse(masterAddr)
296
+}
297
+
298
+func (args MasterArgs) GetDNSBindAddress() (flagtypes.Addr, error) {
299
+	if args.DNSBindAddr.Provided {
300
+		return args.DNSBindAddr, nil
301
+	}
302
+	dnsAddr := flagtypes.Addr{Value: args.BindAddrArg.BindAddr.Host, DefaultPort: 53}.Default()
303
+	return dnsAddr, nil
304
+}
305
+
306
+func (args MasterArgs) GetMasterPublicAddress() (*url.URL, error) {
307
+	if args.MasterPublicAddr.Provided {
308
+		return args.MasterPublicAddr.URL, nil
309
+	}
310
+
311
+	return args.GetMasterAddress()
312
+}
313
+
314
+func (args MasterArgs) GetEtcdBindAddress() string {
315
+	// Derive the etcd bind address by using the bind address and the default etcd port
316
+	return net.JoinHostPort(args.BindAddrArg.BindAddr.Host, strconv.Itoa(args.EtcdAddr.DefaultPort))
317
+}
318
+
319
+func (args MasterArgs) GetEtcdPeerBindAddress() string {
320
+	// Derive the etcd peer address by using the bind address and the default etcd peering port
321
+	return net.JoinHostPort(args.BindAddrArg.BindAddr.Host, "7001")
322
+}
323
+
324
+func (args MasterArgs) GetEtcdAddress() (*url.URL, error) {
325
+	if args.EtcdAddr.Provided {
326
+		return args.EtcdAddr.URL, nil
327
+	}
328
+
329
+	// Etcd should be reachable on the same address that the master is (for simplicity)
330
+	masterAddr, err := args.GetMasterAddress()
331
+	if err != nil {
332
+		return nil, err
333
+	}
334
+
335
+	etcdAddr := net.JoinHostPort(getHost(*masterAddr), strconv.Itoa(args.EtcdAddr.DefaultPort))
336
+	return url.Parse(args.EtcdAddr.DefaultScheme + "://" + etcdAddr)
337
+}
338
+
339
+func (args MasterArgs) GetKubernetesPublicAddress() (*url.URL, error) {
340
+	if args.KubernetesPublicAddr.Provided {
341
+		return args.KubernetesPublicAddr.URL, nil
342
+	}
343
+	if args.KubeConnectionArgs.KubernetesAddr.Provided {
344
+		return args.KubeConnectionArgs.KubernetesAddr.URL, nil
345
+	}
346
+	config, ok, err := args.KubeConnectionArgs.GetExternalKubernetesClientConfig()
347
+	if err != nil {
348
+		return nil, err
349
+	}
350
+	if ok && len(config.Host) > 0 {
351
+		return url.Parse(config.Host)
352
+	}
353
+
354
+	return args.GetMasterPublicAddress()
355
+}
356
+
357
+func (args MasterArgs) GetAssetPublicAddress() (*url.URL, error) {
358
+	if args.AssetPublicAddr.Provided {
359
+		return args.AssetPublicAddr.URL, nil
360
+	}
361
+	// Derive the asset public address by incrementing the master public address port by 1
362
+	// TODO: derive the scheme/port from the asset bind scheme/port once that is settable via the command line
363
+	t, err := args.GetMasterPublicAddress()
364
+	if err != nil {
365
+		return nil, err
366
+	}
367
+	assetPublicAddr := *t
368
+	assetPublicAddr.Host = net.JoinHostPort(getHost(assetPublicAddr), strconv.Itoa(getPort(assetPublicAddr)+1))
369
+
370
+	return &assetPublicAddr, nil
371
+}
372
+
373
+func (args MasterArgs) GetAssetBindAddress() string {
374
+	if args.AssetBindAddr.Provided {
375
+		return args.AssetBindAddr.URL.Host
376
+	}
377
+	// Derive the asset bind address by incrementing the master bind address port by 1
378
+	return net.JoinHostPort(args.BindAddrArg.BindAddr.Host, strconv.Itoa(args.BindAddrArg.BindAddr.Port+1))
379
+}
380
+
381
+func getHost(theURL url.URL) string {
382
+	host, _, err := net.SplitHostPort(theURL.Host)
383
+	if err != nil {
384
+		return theURL.Host
385
+	}
386
+
387
+	return host
388
+}
389
+
390
+func getPort(theURL url.URL) int {
391
+	_, port, err := net.SplitHostPort(theURL.Host)
392
+	if err != nil {
393
+		return 0
394
+	}
395
+
396
+	intport, _ := strconv.Atoi(port)
397
+	return intport
398
+}
399
+
400
+// WriteMaster serializes the config to yaml.
401
+func WriteMaster(config *configapi.MasterConfig) ([]byte, error) {
402
+	json, err := latestconfigapi.Codec.Encode(config)
403
+	if err != nil {
404
+		return nil, err
405
+	}
406
+	content, err := yaml.JSONToYAML(json)
407
+	if err != nil {
408
+		return nil, err
409
+	}
410
+	return content, nil
411
+}
0 412
new file mode 100644
... ...
@@ -0,0 +1,127 @@
0
+package start
1
+
2
+import (
3
+	"fmt"
4
+	"net"
5
+	"net/url"
6
+	"os/exec"
7
+	"strconv"
8
+	"strings"
9
+
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/master/ports"
11
+	"github.com/ghodss/yaml"
12
+	"github.com/golang/glog"
13
+	"github.com/spf13/pflag"
14
+
15
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
16
+	latestconfigapi "github.com/openshift/origin/pkg/cmd/server/api/latest"
17
+	"github.com/openshift/origin/pkg/cmd/server/certs"
18
+	cmdutil "github.com/openshift/origin/pkg/cmd/util"
19
+)
20
+
21
+// NodeArgs is a struct that the command stores flag values into.  It holds a partially complete set of parameters for starting the master
22
+// This object should hold the common set values, but not attempt to handle all cases.  The expected path is to use this object to create
23
+// a fully specified config later on.  If you need something not set here, then create a fully specified config file and pass that as argument
24
+// to starting the master.
25
+type NodeArgs struct {
26
+	NodeName string
27
+
28
+	AllowDisabledDocker bool
29
+	VolumeDir           string
30
+
31
+	DefaultKubernetesURL url.URL
32
+	ClusterDomain        string
33
+	ClusterDNS           net.IP
34
+
35
+	BindAddrArg        *BindAddrArg
36
+	ImageFormatArgs    *ImageFormatArgs
37
+	KubeConnectionArgs *KubeConnectionArgs
38
+	CertArgs           *CertArgs
39
+}
40
+
41
+// BindNodeArgs binds the options to the flags with prefix + default flag names
42
+func BindNodeArgs(args *NodeArgs, flags *pflag.FlagSet, prefix string) {
43
+	flags.StringVar(&args.VolumeDir, prefix+"volume-dir", "openshift.local.volumes", "The volume storage directory.")
44
+	// TODO rename this node-name and recommend hostname -f
45
+	flags.StringVar(&args.NodeName, prefix+"hostname", args.NodeName, "The hostname to identify this node with the master.")
46
+}
47
+
48
+// NewDefaultNodeArgs creates NodeArgs with sub-objects created and default values set.
49
+func NewDefaultNodeArgs() *NodeArgs {
50
+	hostname, err := defaultHostname()
51
+	if err != nil {
52
+		hostname = "localhost"
53
+		glog.Warningf("Unable to lookup hostname, using %q: %v", hostname, err)
54
+	}
55
+
56
+	var dnsIP net.IP
57
+	if clusterDNS := cmdutil.Env("OPENSHIFT_DNS_ADDR", ""); len(clusterDNS) > 0 {
58
+		dnsIP = net.ParseIP(clusterDNS)
59
+	}
60
+
61
+	return &NodeArgs{
62
+		NodeName: hostname,
63
+
64
+		ClusterDomain: cmdutil.Env("OPENSHIFT_DNS_DOMAIN", "local"),
65
+		ClusterDNS:    dnsIP,
66
+
67
+		BindAddrArg:        NewDefaultBindAddrArg(),
68
+		ImageFormatArgs:    NewDefaultImageFormatArgs(),
69
+		KubeConnectionArgs: NewDefaultKubeConnectionArgs(),
70
+		CertArgs:           NewDefaultCertArgs(),
71
+	}
72
+}
73
+
74
+// BuildSerializeableNodeConfig takes the NodeArgs (partially complete config) and uses them along with defaulting behavior to create the fully specified
75
+// config object for starting the node
76
+func (args NodeArgs) BuildSerializeableNodeConfig() (*configapi.NodeConfig, error) {
77
+	var dnsIP string
78
+	if len(args.ClusterDNS) > 0 {
79
+		dnsIP = args.ClusterDNS.String()
80
+	}
81
+
82
+	config := &configapi.NodeConfig{
83
+		NodeName: args.NodeName,
84
+
85
+		ServingInfo: configapi.ServingInfo{
86
+			BindAddress: net.JoinHostPort(args.BindAddrArg.BindAddr.Host, strconv.Itoa(ports.KubeletPort)),
87
+			ServerCert:  certs.DefaultNodeServingCertInfo(args.CertArgs.CertDir, args.NodeName),
88
+		},
89
+
90
+		VolumeDirectory:       args.VolumeDir,
91
+		NetworkContainerImage: args.ImageFormatArgs.ImageTemplate.ExpandOrDie("pod"),
92
+		AllowDisabledDocker:   args.AllowDisabledDocker,
93
+
94
+		DNSDomain: args.ClusterDomain,
95
+		DNSIP:     dnsIP,
96
+
97
+		MasterKubeConfig: certs.DefaultKubeConfigFilename(args.CertArgs.CertDir, "node-"+args.NodeName),
98
+	}
99
+
100
+	return config, nil
101
+}
102
+
103
+// WriteNode serializes the config to yaml.
104
+func WriteNode(config *configapi.NodeConfig) ([]byte, error) {
105
+	json, err := latestconfigapi.Codec.Encode(config)
106
+	if err != nil {
107
+		return nil, err
108
+	}
109
+	content, err := yaml.JSONToYAML(json)
110
+	if err != nil {
111
+		return nil, err
112
+	}
113
+	return content, nil
114
+}
115
+
116
+// defaultHostname returns the default hostname for this system.
117
+func defaultHostname() (string, error) {
118
+
119
+	// Note: We use exec here instead of os.Hostname() because we
120
+	// want the FQDN, and this is the easiest way to get it.
121
+	fqdn, err := exec.Command("hostname", "-f").Output()
122
+	if err != nil {
123
+		return "", fmt.Errorf("Couldn't determine hostname: %v", err)
124
+	}
125
+	return strings.TrimSpace(string(fqdn)), nil
126
+}
0 127
new file mode 100644
... ...
@@ -0,0 +1,222 @@
0
+package start
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"net"
6
+	"strings"
7
+
8
+	"github.com/coreos/go-systemd/daemon"
9
+	"github.com/golang/glog"
10
+	"github.com/spf13/cobra"
11
+
12
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
13
+
14
+	"github.com/openshift/origin/pkg/cmd/server/certs"
15
+
16
+	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/admit"
17
+	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/limitranger"
18
+	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/namespace/exists"
19
+	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/resourcedefaults"
20
+	_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/resourcequota"
21
+)
22
+
23
+type AllInOneOptions struct {
24
+	MasterArgs *MasterArgs
25
+	NodeArgs   *NodeArgs
26
+
27
+	WriteConfigOnly  bool
28
+	MasterConfigFile string
29
+	NodeConfigFile   string
30
+}
31
+
32
+const longAllInOneCommandDesc = `
33
+Start an OpenShift all-in-one server
34
+
35
+This command helps you launch an OpenShift all-in-one server, which allows
36
+you to run all of the components of an OpenShift system on a server with Docker. Running
37
+
38
+    $ openshift start
39
+
40
+will start OpenShift listening on all interfaces, launch an etcd server to store persistent
41
+data, and launch the Kubernetes system components. The server will run in the foreground until
42
+you terminate the process.  This command delegates to "openshift start master" and 
43
+"openshift start node".
44
+
45
+
46
+Note: starting OpenShift without passing the --master address will attempt to find the IP
47
+address that will be visible inside running Docker containers. This is not always successful,
48
+so if you have problems tell OpenShift what public address it will be via --master=<ip>.
49
+
50
+You may also pass --etcd=<address> to connect to an external etcd server.
51
+
52
+You may also pass --kubeconfig=<path> to connect to an external Kubernetes cluster.
53
+`
54
+
55
+// NewCommandStartMaster provides a CLI handler for 'start' command
56
+func NewCommandStartAllInOne() (*cobra.Command, *AllInOneOptions) {
57
+	options := &AllInOneOptions{}
58
+
59
+	cmd := &cobra.Command{
60
+		Use:   "start",
61
+		Short: "Launch OpenShift All-In-One",
62
+		Long:  longAllInOneCommandDesc,
63
+		Run: func(c *cobra.Command, args []string) {
64
+			if err := options.Complete(); err != nil {
65
+				fmt.Println(err.Error())
66
+				c.Help()
67
+				return
68
+			}
69
+			if err := options.Validate(args); err != nil {
70
+				fmt.Println(err.Error())
71
+				c.Help()
72
+				return
73
+			}
74
+
75
+			if err := options.StartAllInOne(); err != nil {
76
+				glog.Fatal(err)
77
+			}
78
+		},
79
+	}
80
+
81
+	flags := cmd.Flags()
82
+
83
+	flags.BoolVar(&options.WriteConfigOnly, "write-config", false, "Indicates that the command should build the configuration from command-line arguments, write it to the locations specified by --master-config and --node-config, and exit.")
84
+	flags.StringVar(&options.MasterConfigFile, "master-config", "", "Location of the master configuration file to run from, or write to (when used with --write-config). When running from configuration files, all other command-line arguments are ignored.")
85
+	flags.StringVar(&options.NodeConfigFile, "node-config", "", "Location of the node configuration file to run from, or write to (when used with --write-config). When running from configuration files, all other command-line arguments are ignored.")
86
+
87
+	masterArgs, nodeArgs, bindAddrArg, imageFormatArgs, kubeConnectionArgs, certArgs := GetAllInOneArgs()
88
+	options.MasterArgs, options.NodeArgs = masterArgs, nodeArgs
89
+	// by default, all-in-ones all disabled docker.  Set it here so that if we allow it to be bound later, bindings take precendence
90
+	options.NodeArgs.AllowDisabledDocker = true
91
+
92
+	BindMasterArgs(masterArgs, flags, "")
93
+	BindNodeArgs(nodeArgs, flags, "")
94
+	BindBindAddrArg(bindAddrArg, flags, "")
95
+	BindImageFormatArgs(imageFormatArgs, flags, "")
96
+	BindKubeConnectionArgs(kubeConnectionArgs, flags, "")
97
+	BindCertArgs(certArgs, flags, "")
98
+
99
+	startMaster, _ := NewCommandStartMaster()
100
+	startNode, _ := NewCommandStartNode()
101
+	cmd.AddCommand(startMaster)
102
+	cmd.AddCommand(startNode)
103
+
104
+	return cmd, options
105
+}
106
+
107
+// GetAllInOneArgs makes sure that the node and master args that should be shared, are shared
108
+func GetAllInOneArgs() (*MasterArgs, *NodeArgs, *BindAddrArg, *ImageFormatArgs, *KubeConnectionArgs, *CertArgs) {
109
+	masterArgs := NewDefaultMasterArgs()
110
+	nodeArgs := NewDefaultNodeArgs()
111
+
112
+	bindAddrArg := NewDefaultBindAddrArg()
113
+	masterArgs.BindAddrArg = bindAddrArg
114
+	nodeArgs.BindAddrArg = bindAddrArg
115
+
116
+	imageFormatArgs := NewDefaultImageFormatArgs()
117
+	masterArgs.ImageFormatArgs = imageFormatArgs
118
+	nodeArgs.ImageFormatArgs = imageFormatArgs
119
+
120
+	kubeConnectionArgs := NewDefaultKubeConnectionArgs()
121
+	masterArgs.KubeConnectionArgs = kubeConnectionArgs
122
+	nodeArgs.KubeConnectionArgs = kubeConnectionArgs
123
+
124
+	certArgs := NewDefaultCertArgs()
125
+	masterArgs.CertArgs = certArgs
126
+	nodeArgs.CertArgs = certArgs
127
+	kubeConnectionArgs.CertArgs = certArgs
128
+
129
+	return masterArgs, nodeArgs, bindAddrArg, imageFormatArgs, kubeConnectionArgs, certArgs
130
+}
131
+
132
+func (o AllInOneOptions) Validate(args []string) error {
133
+	if len(args) != 0 {
134
+		return errors.New("no arguments are supported for start")
135
+	}
136
+	if o.WriteConfigOnly {
137
+		if len(o.MasterConfigFile) == 0 {
138
+			return errors.New("--master-config is required if --write-config is true")
139
+		}
140
+		if len(o.NodeConfigFile) == 0 {
141
+			return errors.New("--node-config is required if --write-config is true")
142
+		}
143
+	}
144
+
145
+	return nil
146
+}
147
+
148
+func (o AllInOneOptions) Complete() error {
149
+	nodeList := util.NewStringSet(strings.ToLower(o.NodeArgs.NodeName))
150
+	// take everything toLower
151
+	for _, s := range o.MasterArgs.NodeList {
152
+		nodeList.Insert(strings.ToLower(s))
153
+	}
154
+	o.MasterArgs.NodeList = nodeList.List()
155
+
156
+	o.NodeArgs.NodeName = strings.ToLower(o.NodeArgs.NodeName)
157
+
158
+	return nil
159
+}
160
+
161
+// StartAllInOne:
162
+// 1.  Creates the signer certificate if needed
163
+// 2.  Calls RunMaster
164
+// 3.  Calls RunNode
165
+// 4.  If only writing configs, it exits
166
+// 5.  Waits forever
167
+func (o AllInOneOptions) StartAllInOne() error {
168
+	if !o.WriteConfigOnly {
169
+		glog.Infof("Starting an OpenShift all-in-one")
170
+	}
171
+
172
+	// if either one of these wants to mint certs, make sure the signer is present BEFORE they start up to make sure they always share
173
+	if o.MasterArgs.CertArgs.CreateCerts || o.NodeArgs.CertArgs.CreateCerts {
174
+		signerOptions := &certs.CreateSignerCertOptions{
175
+			CertFile:   certs.DefaultCertFilename(o.NodeArgs.CertArgs.CertDir, "ca"),
176
+			KeyFile:    certs.DefaultKeyFilename(o.NodeArgs.CertArgs.CertDir, "ca"),
177
+			SerialFile: certs.DefaultSerialFilename(o.NodeArgs.CertArgs.CertDir, "ca"),
178
+			Name:       certs.DefaultSignerName(),
179
+		}
180
+
181
+		if _, err := signerOptions.CreateSignerCert(); err != nil {
182
+			return err
183
+		}
184
+	}
185
+
186
+	masterOptions := MasterOptions{o.MasterArgs, o.WriteConfigOnly, o.MasterConfigFile}
187
+
188
+	masterAddr, err := masterOptions.MasterArgs.GetMasterAddress()
189
+	if err != nil {
190
+		return nil
191
+	}
192
+
193
+	// in the all-in-one, default kubernetes URL to the master's address
194
+	o.NodeArgs.DefaultKubernetesURL = *masterAddr
195
+
196
+	// in the all-in-one, default ClusterDNS to the master's address
197
+	if host, _, err := net.SplitHostPort(masterAddr.Host); err == nil {
198
+		if ip := net.ParseIP(host); ip != nil {
199
+			o.NodeArgs.ClusterDNS = ip
200
+		}
201
+	}
202
+
203
+	nodeOptions := NodeOptions{o.NodeArgs, o.WriteConfigOnly, o.NodeConfigFile}
204
+
205
+	if err := masterOptions.RunMaster(); err != nil {
206
+		return err
207
+	}
208
+
209
+	if err := nodeOptions.RunNode(); err != nil {
210
+		return err
211
+	}
212
+
213
+	if o.WriteConfigOnly {
214
+		return nil
215
+	}
216
+
217
+	daemon.SdNotify("READY=1")
218
+	select {}
219
+
220
+	return nil
221
+}
0 222
new file mode 100644
... ...
@@ -0,0 +1,349 @@
0
+package start
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"io/ioutil"
6
+	"net/http"
7
+	_ "net/http/pprof"
8
+	"strings"
9
+
10
+	"github.com/coreos/go-systemd/daemon"
11
+	"github.com/golang/glog"
12
+	"github.com/spf13/cobra"
13
+
14
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
15
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
16
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
17
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
18
+
19
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
20
+	configapilatest "github.com/openshift/origin/pkg/cmd/server/api/latest"
21
+	"github.com/openshift/origin/pkg/cmd/server/certs"
22
+	"github.com/openshift/origin/pkg/cmd/server/etcd"
23
+	"github.com/openshift/origin/pkg/cmd/server/kubernetes"
24
+	"github.com/openshift/origin/pkg/cmd/server/origin"
25
+	cmdutil "github.com/openshift/origin/pkg/cmd/util"
26
+)
27
+
28
+type MasterOptions struct {
29
+	MasterArgs *MasterArgs
30
+
31
+	WriteConfigOnly bool
32
+	ConfigFile      string
33
+}
34
+
35
+const longMasterCommandDesc = `
36
+Start an OpenShift master
37
+
38
+This command helps you launch an OpenShift master.  Running
39
+
40
+    $ openshift start master
41
+
42
+will start an OpenShift master listening on all interfaces, launch an etcd server to store 
43
+persistent data, and launch the Kubernetes system components. The server will run in the 
44
+foreground until you terminate the process.
45
+
46
+Note: starting OpenShift without passing the --master address will attempt to find the IP
47
+address that will be visible inside running Docker containers. This is not always successful,
48
+so if you have problems tell OpenShift what public address it will be via --master=<ip>.
49
+
50
+You may also pass an optional argument to the start command to start OpenShift in one of the
51
+following roles:
52
+
53
+    $ openshift start master --nodes=<host1,host2,host3,...>
54
+
55
+      Launches the server and control plane for OpenShift. You may pass a list of the node
56
+      hostnames you want to use, or create nodes via the REST API or 'openshift kube'.
57
+
58
+You may also pass --etcd=<address> to connect to an external etcd server.
59
+
60
+You may also pass --kubeconfig=<path> to connect to an external Kubernetes cluster.
61
+`
62
+
63
+// NewCommandStartMaster provides a CLI handler for 'start' command
64
+func NewCommandStartMaster() (*cobra.Command, *MasterOptions) {
65
+	options := &MasterOptions{}
66
+
67
+	cmd := &cobra.Command{
68
+		Use:   "master",
69
+		Short: "Launch OpenShift master",
70
+		Long:  longMasterCommandDesc,
71
+		Run: func(c *cobra.Command, args []string) {
72
+			if err := options.Complete(); err != nil {
73
+				fmt.Println(err.Error())
74
+				c.Help()
75
+				return
76
+			}
77
+
78
+			if err := options.Validate(args); err != nil {
79
+				fmt.Println(err.Error())
80
+				c.Help()
81
+				return
82
+			}
83
+
84
+			if err := options.StartMaster(); err != nil {
85
+				glog.Fatal(err)
86
+			}
87
+		},
88
+	}
89
+
90
+	flags := cmd.Flags()
91
+
92
+	flags.BoolVar(&options.WriteConfigOnly, "write-config", false, "Indicates that the command should build the configuration from command-line arguments, write it to the location specified by --config, and exit.")
93
+	flags.StringVar(&options.ConfigFile, "config", "", "Location of the master configuration file to run from, or write to (when used with --write-config). When running from a configuration file, all other command-line arguments are ignored.")
94
+
95
+	options.MasterArgs = NewDefaultMasterArgs()
96
+	// make sure that KubeConnectionArgs and MasterArgs use the same CertArgs for this command
97
+	options.MasterArgs.KubeConnectionArgs.CertArgs = options.MasterArgs.CertArgs
98
+
99
+	BindMasterArgs(options.MasterArgs, flags, "")
100
+	BindBindAddrArg(options.MasterArgs.BindAddrArg, flags, "")
101
+	BindImageFormatArgs(options.MasterArgs.ImageFormatArgs, flags, "")
102
+	BindKubeConnectionArgs(options.MasterArgs.KubeConnectionArgs, flags, "")
103
+	BindCertArgs(options.MasterArgs.CertArgs, flags, "")
104
+
105
+	return cmd, options
106
+}
107
+
108
+func (o MasterOptions) Validate(args []string) error {
109
+	if len(args) != 0 {
110
+		return errors.New("no arguments are supported for start master")
111
+	}
112
+	if o.WriteConfigOnly {
113
+		if len(o.ConfigFile) == 0 {
114
+			return errors.New("--config is required if --write-config is true")
115
+		}
116
+	}
117
+
118
+	return nil
119
+}
120
+
121
+func (o MasterOptions) Complete() error {
122
+	nodeList := util.NewStringSet()
123
+	// take everything toLower
124
+	for _, s := range o.MasterArgs.NodeList {
125
+		nodeList.Insert(strings.ToLower(s))
126
+	}
127
+
128
+	o.MasterArgs.NodeList = nodeList.List()
129
+
130
+	return nil
131
+}
132
+
133
+// StartMaster calls RunMaster and then waits forever
134
+func (o MasterOptions) StartMaster() error {
135
+	if err := o.RunMaster(); err != nil {
136
+		return err
137
+	}
138
+
139
+	if o.WriteConfigOnly {
140
+		return nil
141
+	}
142
+
143
+	daemon.SdNotify("READY=1")
144
+	select {}
145
+
146
+	return nil
147
+}
148
+
149
+// RunMaster takes the options and:
150
+// 1.  Creates certs if needed
151
+// 2.  Reads fully specified master config OR builds a fully specified master config from the args
152
+// 3.  Writes the fully specified master config and exits if needed
153
+// 4.  Starts the master based on the fully specified config
154
+func (o MasterOptions) RunMaster() error {
155
+	startUsingConfigFile := !o.WriteConfigOnly && (len(o.ConfigFile) > 0)
156
+	mintCerts := o.MasterArgs.CertArgs.CreateCerts && !startUsingConfigFile
157
+
158
+	if mintCerts {
159
+		if err := o.CreateCerts(); err != nil {
160
+			return nil
161
+		}
162
+	}
163
+
164
+	var masterConfig *configapi.MasterConfig
165
+	var err error
166
+	if startUsingConfigFile {
167
+		masterConfig, err = ReadMasterConfig(o.ConfigFile)
168
+	} else {
169
+		masterConfig, err = o.MasterArgs.BuildSerializeableMasterConfig()
170
+	}
171
+	if err != nil {
172
+		return err
173
+	}
174
+
175
+	if o.WriteConfigOnly {
176
+		content, err := WriteMaster(masterConfig)
177
+		if err != nil {
178
+			return err
179
+		}
180
+		if err := ioutil.WriteFile(o.ConfigFile, content, 0644); err != nil {
181
+			return err
182
+		}
183
+		return nil
184
+	}
185
+
186
+	if err := StartMaster(masterConfig); err != nil {
187
+		return err
188
+	}
189
+
190
+	return nil
191
+}
192
+
193
+func (o MasterOptions) CreateCerts() error {
194
+	signerName := certs.DefaultSignerName()
195
+	hostnames, err := o.MasterArgs.GetServerCertHostnames()
196
+	if err != nil {
197
+		return err
198
+	}
199
+	mintAllCertsOptions := certs.CreateAllCertsOptions{
200
+		CertDir:    o.MasterArgs.CertArgs.CertDir,
201
+		SignerName: signerName,
202
+		Hostnames:  hostnames.List(),
203
+		NodeList:   o.MasterArgs.NodeList,
204
+	}
205
+	if err := mintAllCertsOptions.CreateAllCerts(); err != nil {
206
+		return err
207
+	}
208
+
209
+	rootCAFile := certs.DefaultRootCAFile(o.MasterArgs.CertArgs.CertDir)
210
+	masterAddr, err := o.MasterArgs.GetMasterAddress()
211
+	if err != nil {
212
+		return err
213
+	}
214
+	publicMasterAddr, err := o.MasterArgs.GetMasterPublicAddress()
215
+	if err != nil {
216
+		return err
217
+	}
218
+	for _, clientCertInfo := range certs.DefaultClientCerts(o.MasterArgs.CertArgs.CertDir) {
219
+		createKubeConfigOptions := certs.CreateKubeConfigOptions{
220
+			APIServerURL:       masterAddr.String(),
221
+			PublicAPIServerURL: publicMasterAddr.String(),
222
+			APIServerCAFile:    rootCAFile,
223
+			ServerNick:         "master",
224
+
225
+			CertFile: clientCertInfo.CertLocation.CertFile,
226
+			KeyFile:  clientCertInfo.CertLocation.KeyFile,
227
+			UserNick: clientCertInfo.SubDir,
228
+
229
+			KubeConfigFile: certs.DefaultKubeConfigFilename(o.MasterArgs.CertArgs.CertDir, clientCertInfo.SubDir),
230
+		}
231
+
232
+		if _, err := createKubeConfigOptions.CreateKubeConfig(); err != nil {
233
+			return err
234
+		}
235
+	}
236
+
237
+	return nil
238
+}
239
+
240
+func ReadMasterConfig(filename string) (*configapi.MasterConfig, error) {
241
+	data, err := ioutil.ReadFile(filename)
242
+	if err != nil {
243
+		return nil, err
244
+	}
245
+
246
+	config := &configapi.MasterConfig{}
247
+
248
+	if err := configapilatest.Codec.DecodeInto(data, config); err != nil {
249
+		return nil, err
250
+	}
251
+	return config, nil
252
+}
253
+
254
+func StartMaster(openshiftMasterConfig *configapi.MasterConfig) error {
255
+	glog.Infof("Starting an OpenShift master, reachable at %s (etcd: %s)", openshiftMasterConfig.ServingInfo.BindAddress, openshiftMasterConfig.EtcdClientInfo.URL)
256
+	glog.Infof("OpenShift master public address is %s", openshiftMasterConfig.AssetConfig.MasterPublicURL)
257
+
258
+	if openshiftMasterConfig.EtcdConfig != nil {
259
+		etcdConfig := &etcd.Config{
260
+			BindAddr:     openshiftMasterConfig.EtcdConfig.ServingInfo.BindAddress,
261
+			PeerBindAddr: openshiftMasterConfig.EtcdConfig.PeerAddress,
262
+			MasterAddr:   openshiftMasterConfig.EtcdConfig.MasterAddress,
263
+			EtcdDir:      openshiftMasterConfig.EtcdConfig.StorageDir,
264
+		}
265
+
266
+		etcdConfig.Run()
267
+	}
268
+
269
+	if cmdutil.Env("OPENSHIFT_PROFILE", "") == "web" {
270
+		go func() {
271
+			glog.Infof("Starting profiling endpoint at http://127.0.0.1:6060/debug/pprof/")
272
+			glog.Fatal(http.ListenAndServe("127.0.0.1:6060", nil))
273
+		}()
274
+	}
275
+
276
+	// Allow privileged containers
277
+	// TODO: make this configurable and not the default https://github.com/openshift/origin/issues/662
278
+	capabilities.Initialize(capabilities.Capabilities{
279
+		AllowPrivileged: true,
280
+	})
281
+
282
+	openshiftConfig, err := origin.BuildMasterConfig(*openshiftMasterConfig)
283
+	if err != nil {
284
+		return err
285
+	}
286
+	//	 must start policy caching immediately
287
+	openshiftConfig.RunPolicyCache()
288
+
289
+	authConfig, err := origin.BuildAuthConfig(*openshiftMasterConfig)
290
+	if err != nil {
291
+		return err
292
+	}
293
+
294
+	if openshiftMasterConfig.KubernetesMasterConfig != nil {
295
+		glog.Infof("Static Nodes: %v", openshiftMasterConfig.KubernetesMasterConfig.StaticNodeNames)
296
+
297
+		kubeConfig, err := kubernetes.BuildKubernetesMasterConfig(*openshiftMasterConfig, openshiftConfig.RequestContextMapper, openshiftConfig.KubeClient())
298
+		if err != nil {
299
+			return err
300
+		}
301
+		kubeConfig.EnsurePortalFlags()
302
+
303
+		openshiftConfig.Run([]origin.APIInstaller{kubeConfig}, []origin.APIInstaller{authConfig})
304
+
305
+		kubeConfig.RunScheduler()
306
+		kubeConfig.RunReplicationController()
307
+		kubeConfig.RunEndpointController()
308
+		kubeConfig.RunMinionController()
309
+		kubeConfig.RunResourceQuotaManager()
310
+
311
+	} else {
312
+		_, kubeConfig, err := configapi.GetKubeClient(openshiftMasterConfig.MasterClients.KubernetesKubeConfig)
313
+		if err != nil {
314
+			return err
315
+		}
316
+
317
+		proxy := &kubernetes.ProxyConfig{
318
+			ClientConfig: kubeConfig,
319
+		}
320
+
321
+		openshiftConfig.Run([]origin.APIInstaller{proxy}, []origin.APIInstaller{authConfig})
322
+	}
323
+
324
+	// TODO: recording should occur in individual components
325
+	record.StartRecording(openshiftConfig.KubeClient().Events(""), kapi.EventSource{Component: "master"})
326
+
327
+	glog.Infof("Using images from %q", openshiftConfig.ImageFor("<component>"))
328
+
329
+	if openshiftMasterConfig.DNSConfig != nil {
330
+		openshiftConfig.RunDNSServer()
331
+	}
332
+	if openshiftMasterConfig.AssetConfig != nil {
333
+		openshiftConfig.RunAssetServer()
334
+	}
335
+	openshiftConfig.RunBuildController()
336
+	openshiftConfig.RunBuildPodController()
337
+	openshiftConfig.RunBuildImageChangeTriggerController()
338
+	if err := openshiftConfig.RunDeploymentController(); err != nil {
339
+		return err
340
+	}
341
+	openshiftConfig.RunDeployerPodController()
342
+	openshiftConfig.RunDeploymentConfigController()
343
+	openshiftConfig.RunDeploymentConfigChangeController()
344
+	openshiftConfig.RunDeploymentImageChangeTriggerController()
345
+	openshiftConfig.RunProjectAuthorizationCache()
346
+
347
+	return nil
348
+}
0 349
new file mode 100644
... ...
@@ -0,0 +1,267 @@
0
+package start
1
+
2
+import (
3
+	"errors"
4
+	"fmt"
5
+	"io/ioutil"
6
+	"net/http"
7
+	_ "net/http/pprof"
8
+	"path"
9
+	"path/filepath"
10
+	"strings"
11
+
12
+	"github.com/coreos/go-systemd/daemon"
13
+	"github.com/golang/glog"
14
+	"github.com/spf13/cobra"
15
+
16
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
17
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
18
+	"github.com/openshift/origin/pkg/cmd/server/kubernetes"
19
+
20
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
21
+	configapilatest "github.com/openshift/origin/pkg/cmd/server/api/latest"
22
+	"github.com/openshift/origin/pkg/cmd/server/certs"
23
+	cmdutil "github.com/openshift/origin/pkg/cmd/util"
24
+	"github.com/openshift/origin/pkg/cmd/util/docker"
25
+)
26
+
27
+type NodeOptions struct {
28
+	NodeArgs *NodeArgs
29
+
30
+	WriteConfigOnly bool
31
+	ConfigFile      string
32
+}
33
+
34
+const longNodeCommandDesc = `
35
+Start an OpenShift node
36
+This command helps you launch an OpenShift node.  Running
37
+
38
+    $ openshift start node --master=<masterIP>
39
+
40
+will start an OpenShift node that attempts to connect to the master on the provided IP. The 
41
+node will run in the foreground until you terminate the process.
42
+`
43
+
44
+// NewCommandStartMaster provides a CLI handler for 'start' command
45
+func NewCommandStartNode() (*cobra.Command, *NodeOptions) {
46
+	options := &NodeOptions{}
47
+
48
+	cmd := &cobra.Command{
49
+		Use:   "node",
50
+		Short: "Launch OpenShift node",
51
+		Long:  longNodeCommandDesc,
52
+		Run: func(c *cobra.Command, args []string) {
53
+			if err := options.Complete(); err != nil {
54
+				fmt.Println(err.Error())
55
+				c.Help()
56
+				return
57
+			}
58
+			if err := options.Validate(args); err != nil {
59
+				fmt.Println(err.Error())
60
+				c.Help()
61
+				return
62
+			}
63
+
64
+			if err := options.StartNode(); err != nil {
65
+				glog.Fatal(err)
66
+			}
67
+		},
68
+	}
69
+
70
+	flags := cmd.Flags()
71
+
72
+	flags.BoolVar(&options.WriteConfigOnly, "write-config", false, "Indicates that the command should build the configuration from command-line arguments, write it to the location specified by --config, and exit.")
73
+	flags.StringVar(&options.ConfigFile, "config", "", "Location of the node configuration file to run from, or write to (when used with --write-config). When running from a configuration file, all other command-line arguments are ignored.")
74
+
75
+	options.NodeArgs = NewDefaultNodeArgs()
76
+	// make sure that KubeConnectionArgs and NodeArgs use the same CertArgs for this command
77
+	options.NodeArgs.KubeConnectionArgs.CertArgs = options.NodeArgs.CertArgs
78
+
79
+	BindNodeArgs(options.NodeArgs, flags, "")
80
+	BindBindAddrArg(options.NodeArgs.BindAddrArg, flags, "")
81
+	BindImageFormatArgs(options.NodeArgs.ImageFormatArgs, flags, "")
82
+	BindKubeConnectionArgs(options.NodeArgs.KubeConnectionArgs, flags, "")
83
+	BindCertArgs(options.NodeArgs.CertArgs, flags, "")
84
+
85
+	return cmd, options
86
+}
87
+
88
+func (o NodeOptions) Validate(args []string) error {
89
+	if len(args) != 0 {
90
+		return errors.New("no arguments are supported for start node")
91
+	}
92
+	if o.WriteConfigOnly {
93
+		if len(o.ConfigFile) == 0 {
94
+			return errors.New("--config is required if --write-config is true")
95
+		}
96
+	}
97
+
98
+	return nil
99
+}
100
+
101
+func (o NodeOptions) Complete() error {
102
+	o.NodeArgs.NodeName = strings.ToLower(o.NodeArgs.NodeName)
103
+
104
+	return nil
105
+}
106
+
107
+// StartNode calls RunNode and then waits forever
108
+func (o NodeOptions) StartNode() error {
109
+	if err := o.RunNode(); err != nil {
110
+		return err
111
+	}
112
+
113
+	if o.WriteConfigOnly {
114
+		return nil
115
+	}
116
+
117
+	daemon.SdNotify("READY=1")
118
+	select {}
119
+
120
+	return nil
121
+}
122
+
123
+// RunNode takes the options and:
124
+// 1.  Creates certs if needed
125
+// 2.  Reads fully specified node config OR builds a fully specified node config from the args
126
+// 3.  Writes the fully specified node config and exits if needed
127
+// 4.  Starts the node based on the fully specified config
128
+func (o NodeOptions) RunNode() error {
129
+	startUsingConfigFile := !o.WriteConfigOnly && (len(o.ConfigFile) > 0)
130
+	mintCerts := o.NodeArgs.CertArgs.CreateCerts && !startUsingConfigFile
131
+
132
+	if mintCerts {
133
+		if err := o.CreateCerts(); err != nil {
134
+			return nil
135
+		}
136
+	}
137
+
138
+	var nodeConfig *configapi.NodeConfig
139
+	var err error
140
+	if startUsingConfigFile {
141
+		nodeConfig, err = ReadNodeConfig(o.ConfigFile)
142
+	} else {
143
+		nodeConfig, err = o.NodeArgs.BuildSerializeableNodeConfig()
144
+	}
145
+	if err != nil {
146
+		return err
147
+	}
148
+
149
+	if o.WriteConfigOnly {
150
+		content, err := WriteNode(nodeConfig)
151
+		if err != nil {
152
+			return err
153
+		}
154
+		if err := ioutil.WriteFile(o.ConfigFile, content, 0644); err != nil {
155
+			return err
156
+		}
157
+		return nil
158
+	}
159
+
160
+	_, kubeClientConfig, err := configapi.GetKubeClient(nodeConfig.MasterKubeConfig)
161
+	if err != nil {
162
+		return err
163
+	}
164
+	glog.Infof("Starting an OpenShift node, connecting to %s", kubeClientConfig.Host)
165
+
166
+	if cmdutil.Env("OPENSHIFT_PROFILE", "") == "web" {
167
+		go func() {
168
+			glog.Infof("Starting profiling endpoint at http://127.0.0.1:6060/debug/pprof/")
169
+			glog.Fatal(http.ListenAndServe("127.0.0.1:6060", nil))
170
+		}()
171
+	}
172
+
173
+	if err := StartNode(*nodeConfig); err != nil {
174
+		return err
175
+	}
176
+
177
+	return nil
178
+}
179
+
180
+func (o NodeOptions) CreateCerts() error {
181
+	username := "node-" + o.NodeArgs.NodeName
182
+	signerOptions := &certs.CreateSignerCertOptions{
183
+		CertFile:   certs.DefaultCertFilename(o.NodeArgs.CertArgs.CertDir, "ca"),
184
+		KeyFile:    certs.DefaultKeyFilename(o.NodeArgs.CertArgs.CertDir, "ca"),
185
+		SerialFile: certs.DefaultSerialFilename(o.NodeArgs.CertArgs.CertDir, "ca"),
186
+		Name:       certs.DefaultSignerName(),
187
+	}
188
+	if _, err := signerOptions.CreateSignerCert(); err != nil {
189
+		return err
190
+	}
191
+	getSignerOptions := &certs.GetSignerCertOptions{
192
+		CertFile:   certs.DefaultCertFilename(o.NodeArgs.CertArgs.CertDir, "ca"),
193
+		KeyFile:    certs.DefaultKeyFilename(o.NodeArgs.CertArgs.CertDir, "ca"),
194
+		SerialFile: certs.DefaultSerialFilename(o.NodeArgs.CertArgs.CertDir, "ca"),
195
+	}
196
+
197
+	mintNodeClientCert := certs.CreateNodeClientCertOptions{
198
+		GetSignerCertOptions: getSignerOptions,
199
+		CertFile:             certs.DefaultCertFilename(o.NodeArgs.CertArgs.CertDir, username),
200
+		KeyFile:              certs.DefaultKeyFilename(o.NodeArgs.CertArgs.CertDir, username),
201
+		NodeName:             o.NodeArgs.NodeName,
202
+	}
203
+	if _, err := mintNodeClientCert.CreateNodeClientCert(); err != nil {
204
+		return err
205
+	}
206
+
207
+	masterAddr, err := o.NodeArgs.KubeConnectionArgs.GetKubernetesAddress(&o.NodeArgs.DefaultKubernetesURL)
208
+	if err != nil {
209
+		return err
210
+	}
211
+
212
+	createKubeConfigOptions := certs.CreateKubeConfigOptions{
213
+		APIServerURL:    masterAddr.String(),
214
+		APIServerCAFile: getSignerOptions.CertFile,
215
+		ServerNick:      "master",
216
+
217
+		CertFile: mintNodeClientCert.CertFile,
218
+		KeyFile:  mintNodeClientCert.KeyFile,
219
+		UserNick: username,
220
+
221
+		KubeConfigFile: path.Join(filepath.Dir(mintNodeClientCert.CertFile), ".kubeconfig"),
222
+	}
223
+	if _, err := createKubeConfigOptions.CreateKubeConfig(); err != nil {
224
+		return err
225
+	}
226
+
227
+	return nil
228
+}
229
+
230
+func ReadNodeConfig(filename string) (*configapi.NodeConfig, error) {
231
+	data, err := ioutil.ReadFile(filename)
232
+	if err != nil {
233
+		return nil, err
234
+	}
235
+
236
+	config := &configapi.NodeConfig{}
237
+
238
+	if err := configapilatest.Codec.DecodeInto(data, config); err != nil {
239
+		return nil, err
240
+	}
241
+	return config, nil
242
+}
243
+
244
+func StartNode(config configapi.NodeConfig) error {
245
+	if config.RecordEvents {
246
+		kubeClient, _, err := configapi.GetKubeClient(config.MasterKubeConfig)
247
+		if err != nil {
248
+			return err
249
+		}
250
+
251
+		// TODO: recording should occur in individual components
252
+		record.StartRecording(kubeClient.Events(""), kapi.EventSource{Component: "node"})
253
+	}
254
+
255
+	nodeConfig, err := kubernetes.BuildKubernetesNodeConfig(config)
256
+	if err != nil {
257
+		return err
258
+	}
259
+
260
+	nodeConfig.EnsureVolumeDir()
261
+	nodeConfig.EnsureDocker(docker.NewHelper())
262
+	nodeConfig.RunProxy()
263
+	nodeConfig.RunKubelet()
264
+
265
+	return nil
266
+}
... ...
@@ -4,8 +4,17 @@ import (
4 4
 	"fmt"
5 5
 	"os"
6 6
 	"regexp"
7
+	"strconv"
7 8
 )
8 9
 
10
+func EnvInt(key string, defaultValue int32, minValue int32) int32 {
11
+	value, err := strconv.ParseInt(Env(key, fmt.Sprintf("%d", defaultValue)), 10, 32)
12
+	if err != nil || int32(value) < minValue {
13
+		return defaultValue
14
+	}
15
+	return int32(value)
16
+}
17
+
9 18
 // Env returns an environment variable or a default value if not specified.
10 19
 func Env(key string, defaultValue string) string {
11 20
 	val := os.Getenv(key)
... ...
@@ -9,14 +9,14 @@ import (
9 9
 )
10 10
 
11 11
 // TryListen tries to open a connection on the given port and returns true if it succeeded.
12
-func TryListen(hostPort string) bool {
12
+func TryListen(hostPort string) (bool, error) {
13 13
 	l, err := net.Listen("tcp", hostPort)
14 14
 	if err != nil {
15 15
 		glog.V(5).Infof("Failure while checking listen on %s: %v", err)
16
-		return false
16
+		return false, err
17 17
 	}
18 18
 	defer l.Close()
19
-	return true
19
+	return true, nil
20 20
 }
21 21
 
22 22
 // WaitForDial attempts to connect to the given address, closing and returning nil on the first successful connection.
... ...
@@ -17,21 +17,21 @@ import (
17 17
 )
18 18
 
19 19
 func TestRestrictedAccessForProjectAdmins(t *testing.T) {
20
-	startConfig, err := StartTestMaster()
20
+	_, clusterAdminKubeConfig, err := StartTestMaster()
21 21
 	if err != nil {
22 22
 		t.Fatalf("unexpected error: %v", err)
23 23
 	}
24 24
 
25
-	openshiftClient, openshiftClientConfig, err := startConfig.GetOpenshiftClient()
25
+	clusterAdminClient, _, clusterAdminClientConfig, err := GetClusterAdminClient(clusterAdminKubeConfig)
26 26
 	if err != nil {
27 27
 		t.Errorf("unexpected error: %v", err)
28 28
 	}
29 29
 
30
-	haroldClient, err := CreateNewProject(openshiftClient, *openshiftClientConfig, "hammer-project", "harold")
30
+	haroldClient, err := CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold")
31 31
 	if err != nil {
32 32
 		t.Errorf("unexpected error: %v", err)
33 33
 	}
34
-	markClient, err := CreateNewProject(openshiftClient, *openshiftClientConfig, "mallet-project", "mark")
34
+	markClient, err := CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark")
35 35
 	if err != nil {
36 36
 		t.Errorf("unexpected error: %v", err)
37 37
 	}
... ...
@@ -81,12 +81,12 @@ func TestRestrictedAccessForProjectAdmins(t *testing.T) {
81 81
 }
82 82
 
83 83
 func TestOnlyResolveRolesForBindingsThatMatter(t *testing.T) {
84
-	startConfig, err := StartTestMaster()
84
+	_, clusterAdminKubeConfig, err := StartTestMaster()
85 85
 	if err != nil {
86 86
 		t.Fatalf("unexpected error: %v", err)
87 87
 	}
88 88
 
89
-	openshiftClient, _, err := startConfig.GetOpenshiftClient()
89
+	clusterAdminClient, _, _, err := GetClusterAdminClient(clusterAdminKubeConfig)
90 90
 	if err != nil {
91 91
 		t.Errorf("unexpected error: %v", err)
92 92
 	}
... ...
@@ -95,14 +95,14 @@ func TestOnlyResolveRolesForBindingsThatMatter(t *testing.T) {
95 95
 		RoleNamespace:    "master",
96 96
 		RoleName:         "view",
97 97
 		BindingNamespace: "master",
98
-		Client:           openshiftClient,
98
+		Client:           clusterAdminClient,
99 99
 		Users:            []string{"anypassword:valerie"},
100 100
 	}
101 101
 	if err := addValerie.Run(); err != nil {
102 102
 		t.Errorf("unexpected error: %v", err)
103 103
 	}
104 104
 
105
-	if err = openshiftClient.Roles("master").Delete("view"); err != nil {
105
+	if err = clusterAdminClient.Roles("master").Delete("view"); err != nil {
106 106
 		t.Errorf("unexpected error: %v", err)
107 107
 	}
108 108
 
... ...
@@ -110,7 +110,7 @@ func TestOnlyResolveRolesForBindingsThatMatter(t *testing.T) {
110 110
 		RoleNamespace:    "master",
111 111
 		RoleName:         "edit",
112 112
 		BindingNamespace: "master",
113
-		Client:           openshiftClient,
113
+		Client:           clusterAdminClient,
114 114
 		Users:            []string{"anypassword:edgar"},
115 115
 	}
116 116
 	if err := addEdgar.Run(); err != nil {
... ...
@@ -126,7 +126,7 @@ func TestOnlyResolveRolesForBindingsThatMatter(t *testing.T) {
126 126
 
127 127
 // TODO this list should start collapsing as we continue to tighten access on generated system ids
128 128
 var globalClusterAdminUsers = util.NewStringSet("system:kube-client", "system:openshift-client", "system:openshift-deployer")
129
-var globalClusterAdminGroups = util.NewStringSet("system:cluster-admins")
129
+var globalClusterAdminGroups = util.NewStringSet("system:cluster-admins", "system:nodes")
130 130
 
131 131
 type resourceAccessReviewTest struct {
132 132
 	clientInterface client.ResourceAccessReviewInterface
... ...
@@ -156,21 +156,21 @@ func (test resourceAccessReviewTest) run(t *testing.T) {
156 156
 }
157 157
 
158 158
 func TestResourceAccessReview(t *testing.T) {
159
-	startConfig, err := StartTestMaster()
159
+	_, clusterAdminKubeConfig, err := StartTestMaster()
160 160
 	if err != nil {
161 161
 		t.Fatalf("unexpected error: %v", err)
162 162
 	}
163 163
 
164
-	openshiftClient, openshiftClientConfig, err := startConfig.GetOpenshiftClient()
164
+	clusterAdminClient, _, clusterAdminClientConfig, err := GetClusterAdminClient(clusterAdminKubeConfig)
165 165
 	if err != nil {
166 166
 		t.Errorf("unexpected error: %v", err)
167 167
 	}
168 168
 
169
-	haroldClient, err := CreateNewProject(openshiftClient, *openshiftClientConfig, "hammer-project", "harold")
169
+	haroldClient, err := CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold")
170 170
 	if err != nil {
171 171
 		t.Errorf("unexpected error: %v", err)
172 172
 	}
173
-	markClient, err := CreateNewProject(openshiftClient, *openshiftClientConfig, "mallet-project", "mark")
173
+	markClient, err := CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark")
174 174
 	if err != nil {
175 175
 		t.Errorf("unexpected error: %v", err)
176 176
 	}
... ...
@@ -239,7 +239,7 @@ func TestResourceAccessReview(t *testing.T) {
239 239
 	// a cluster-admin should be able to make global access review requests
240 240
 	{
241 241
 		test := resourceAccessReviewTest{
242
-			clientInterface: openshiftClient.RootResourceAccessReviews(),
242
+			clientInterface: clusterAdminClient.RootResourceAccessReviews(),
243 243
 			review:          requestWhoCanViewDeployments,
244 244
 			response: authorizationapi.ResourceAccessReviewResponse{
245 245
 				Users:  globalClusterAdminUsers,
... ...
@@ -278,21 +278,21 @@ func (test subjectAccessReviewTest) run(t *testing.T) {
278 278
 }
279 279
 
280 280
 func TestSubjectAccessReview(t *testing.T) {
281
-	startConfig, err := StartTestMaster()
281
+	_, clusterAdminKubeConfig, err := StartTestMaster()
282 282
 	if err != nil {
283 283
 		t.Fatalf("unexpected error: %v", err)
284 284
 	}
285 285
 
286
-	openshiftClient, openshiftClientConfig, err := startConfig.GetOpenshiftClient()
286
+	clusterAdminClient, _, clusterAdminClientConfig, err := GetClusterAdminClient(clusterAdminKubeConfig)
287 287
 	if err != nil {
288 288
 		t.Errorf("unexpected error: %v", err)
289 289
 	}
290 290
 
291
-	haroldClient, err := CreateNewProject(openshiftClient, *openshiftClientConfig, "hammer-project", "harold")
291
+	haroldClient, err := CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "hammer-project", "harold")
292 292
 	if err != nil {
293 293
 		t.Errorf("unexpected error: %v", err)
294 294
 	}
295
-	markClient, err := CreateNewProject(openshiftClient, *openshiftClientConfig, "mallet-project", "mark")
295
+	markClient, err := CreateNewProject(clusterAdminClient, *clusterAdminClientConfig, "mallet-project", "mark")
296 296
 	if err != nil {
297 297
 		t.Errorf("unexpected error: %v", err)
298 298
 	}
... ...
@@ -368,7 +368,7 @@ func TestSubjectAccessReview(t *testing.T) {
368 368
 
369 369
 	askCanClusterAdminsCreateProject := &authorizationapi.SubjectAccessReview{Groups: util.NewStringSet("system:cluster-admins"), Verb: "create", Resource: "projects"}
370 370
 	subjectAccessReviewTest{
371
-		clientInterface: openshiftClient.RootSubjectAccessReviews(),
371
+		clientInterface: clusterAdminClient.RootSubjectAccessReviews(),
372 372
 		review:          askCanClusterAdminsCreateProject,
373 373
 		response: authorizationapi.SubjectAccessReviewResponse{
374 374
 			Allowed:   true,
... ...
@@ -15,17 +15,17 @@ import (
15 15
 )
16 16
 
17 17
 func TestAuthenticatedUsersAgainstOpenshiftNamespace(t *testing.T) {
18
-	startConfig, err := StartTestMaster()
18
+	_, clusterAdminKubeConfig, err := StartTestMaster()
19 19
 	if err != nil {
20 20
 		t.Fatalf("unexpected error: %v", err)
21 21
 	}
22 22
 
23
-	_, openshiftClientConfig, err := startConfig.GetOpenshiftClient()
23
+	_, _, clusterAdminClientConfig, err := GetClusterAdminClient(clusterAdminKubeConfig)
24 24
 	if err != nil {
25 25
 		t.Errorf("unexpected error: %v", err)
26 26
 	}
27 27
 
28
-	valerieClientConfig := *openshiftClientConfig
28
+	valerieClientConfig := *clusterAdminClientConfig
29 29
 	valerieClientConfig.Username = ""
30 30
 	valerieClientConfig.Password = ""
31 31
 	valerieClientConfig.BearerToken = ""
... ...
@@ -12,14 +12,11 @@ import (
12 12
 )
13 13
 
14 14
 func TestDNS(t *testing.T) {
15
-	_, err := StartTestAllInOne()
15
+	masterConfig, _, err := StartTestAllInOne()
16 16
 	if err != nil {
17 17
 		t.Fatalf("unexpected error: %v", err)
18 18
 	}
19 19
 
20
-	// ugly...
21
-	server := "127.0.0.1:8053"
22
-
23 20
 	// verify service DNS entry is visible
24 21
 	stop := make(chan struct{})
25 22
 	util.Until(func() {
... ...
@@ -27,7 +24,7 @@ func TestDNS(t *testing.T) {
27 27
 			MsgHdr:   dns.MsgHdr{Id: dns.Id(), RecursionDesired: true},
28 28
 			Question: []dns.Question{{"kubernetes.default.local.", dns.TypeA, dns.ClassINET}},
29 29
 		}
30
-		in, err := dns.Exchange(m1, server)
30
+		in, err := dns.Exchange(m1, masterConfig.DNSConfig.BindAddress)
31 31
 		if err != nil {
32 32
 			t.Logf("unexpected error: %v", err)
33 33
 			return
... ...
@@ -52,7 +49,7 @@ func TestDNS(t *testing.T) {
52 52
 		MsgHdr:   dns.MsgHdr{Id: dns.Id(), RecursionDesired: true},
53 53
 		Question: []dns.Question{{"foo.kubernetes.default.local.", dns.TypeA, dns.ClassINET}},
54 54
 	}
55
-	in, err := dns.Exchange(m1, server)
55
+	in, err := dns.Exchange(m1, masterConfig.DNSConfig.BindAddress)
56 56
 	if err != nil {
57 57
 		t.Fatalf("unexpected error: %v", err)
58 58
 	}
... ...
@@ -73,7 +70,7 @@ func TestDNS(t *testing.T) {
73 73
 		MsgHdr:   dns.MsgHdr{Id: dns.Id(), RecursionDesired: true},
74 74
 		Question: []dns.Question{{"www.google.com.", dns.TypeA, dns.ClassINET}},
75 75
 	}
76
-	in, err = dns.Exchange(m1, server)
76
+	in, err = dns.Exchange(m1, masterConfig.DNSConfig.BindAddress)
77 77
 	if err != nil {
78 78
 		t.Fatalf("unexpected error: %v", err)
79 79
 	}
... ...
@@ -20,12 +20,12 @@ func init() {
20 20
 }
21 21
 
22 22
 func TestLogin(t *testing.T) {
23
-	startConfig, err := StartTestMaster()
23
+	_, clusterAdminKubeConfig, err := StartTestMaster()
24 24
 	if err != nil {
25 25
 		t.Fatalf("unexpected error: %v", err)
26 26
 	}
27 27
 
28
-	openshiftClient, openshiftClientConfig, err := startConfig.GetOpenshiftClient()
28
+	clusterAdminClient, _, clusterAdminClientConfig, err := GetClusterAdminClient(clusterAdminKubeConfig)
29 29
 	if err != nil {
30 30
 		t.Fatalf("unexpected error: %v", err)
31 31
 	}
... ...
@@ -40,7 +40,7 @@ func TestLogin(t *testing.T) {
40 40
 	username := "joe"
41 41
 	password := "pass"
42 42
 	project := "the-singularity-is-near"
43
-	server := openshiftClientConfig.Host
43
+	server := clusterAdminClientConfig.Host
44 44
 
45 45
 	loginOptions = newLoginOptions(server, username, password, "", true)
46 46
 
... ...
@@ -61,7 +61,7 @@ func TestLogin(t *testing.T) {
61 61
 	}
62 62
 
63 63
 	newProjectOptions := &newproject.NewProjectOptions{
64
-		Client:                openshiftClient,
64
+		Client:                clusterAdminClient,
65 65
 		ProjectName:           project,
66 66
 		AdminRole:             "admin",
67 67
 		MasterPolicyNamespace: "master",
... ...
@@ -25,13 +25,14 @@ func TestHTPasswd(t *testing.T) {
25 25
 	os.Setenv("OPENSHIFT_OAUTH_PASSWORD_AUTH", "htpasswd")
26 26
 	os.Setenv("OPENSHIFT_OAUTH_HTPASSWD_FILE", htpasswdFile.Name())
27 27
 
28
-	startConfig, err := StartTestMaster()
28
+	_, clusterAdminKubeConfig, err := StartTestMaster()
29 29
 	if err != nil {
30 30
 		t.Fatalf("unexpected error: %v", err)
31 31
 	}
32
-	_, clientConfig, err := startConfig.GetOpenshiftClient()
32
+
33
+	_, _, clientConfig, err := GetClusterAdminClient(clusterAdminKubeConfig)
33 34
 	if err != nil {
34
-		t.Fatalf("unexpected error: %v", err)
35
+		t.Errorf("unexpected error: %v", err)
35 36
 	}
36 37
 
37 38
 	// Use the server and CA info
... ...
@@ -104,11 +104,12 @@ func TestOAuthRequestHeader(t *testing.T) {
104 104
 	os.Setenv("OPENSHIFT_OAUTH_REQUEST_HEADER_CA_FILE", caFile.Name())
105 105
 
106 106
 	// Start server
107
-	startConfig, err := StartTestMaster()
107
+	_, clusterAdminKubeConfig, err := StartTestAllInOne()
108 108
 	if err != nil {
109 109
 		t.Fatalf("unexpected error: %v", err)
110 110
 	}
111
-	_, clientConfig, err := startConfig.GetOpenshiftClient()
111
+
112
+	_, _, clientConfig, err := GetClusterAdminClient(clusterAdminKubeConfig)
112 113
 	if err != nil {
113 114
 		t.Fatalf("unexpected error: %v", err)
114 115
 	}
... ...
@@ -11,10 +11,12 @@ import (
11 11
 
12 12
 	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
13 13
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
14
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
14 15
 
15 16
 	"github.com/openshift/origin/pkg/client"
16 17
 	newproject "github.com/openshift/origin/pkg/cmd/experimental/project"
17
-	start "github.com/openshift/origin/pkg/cmd/server"
18
+	configapi "github.com/openshift/origin/pkg/cmd/server/api"
19
+	"github.com/openshift/origin/pkg/cmd/server/start"
18 20
 	cmdutil "github.com/openshift/origin/pkg/cmd/util"
19 21
 	"github.com/openshift/origin/pkg/cmd/util/tokencmd"
20 22
 )
... ...
@@ -23,53 +25,71 @@ func init() {
23 23
 	requireEtcd()
24 24
 }
25 25
 
26
-func StartTestServer(args ...string) (start.Config, error) {
27
-	deleteAllEtcdKeys()
28
-
29
-	startConfig := start.NewDefaultConfig()
30
-	startConfig.DNSBindAddr.DefaultPort = 8053
31
-	startConfig.DNSBindAddr = startConfig.DNSBindAddr.Default()
26
+func setupStartOptions() (*start.MasterArgs, *start.NodeArgs, *start.BindAddrArg, *start.ImageFormatArgs, *start.KubeConnectionArgs, *start.CertArgs) {
27
+	masterArgs, nodeArgs, bindAddrArg, imageFormatArgs, kubeConnectionArgs, certArgs := start.GetAllInOneArgs()
32 28
 
33 29
 	basedir := path.Join(os.TempDir(), "openshift-integration-tests")
34
-
35
-	startConfig.VolumeDir = path.Join(basedir, "volume")
36
-	startConfig.EtcdDir = path.Join(basedir, "etcd")
37
-	startConfig.CertDir = path.Join(basedir, "cert")
30
+	nodeArgs.VolumeDir = path.Join(basedir, "volume")
31
+	masterArgs.EtcdDir = path.Join(basedir, "etcd")
32
+	certArgs.CertDir = path.Join(basedir, "cert")
38 33
 
39 34
 	// don't wait for nodes to come up
40
-	if len(args) > 0 && args[0] == "master" {
41
-		startConfig.NodeList = nil
42
-	}
43 35
 
44 36
 	masterAddr := httptest.NewUnstartedServer(nil).Listener.Addr().String()
45 37
 	fmt.Printf("masterAddr: %#v\n", masterAddr)
46
-	startConfig.MasterAddr.Set(masterAddr)
47
-	startConfig.BindAddr.Set(masterAddr)
48
-	startConfig.EtcdAddr.Set(getEtcdURL())
38
+	masterArgs.MasterAddr.Set(masterAddr)
39
+	bindAddrArg.BindAddr.Set(masterAddr)
40
+	masterArgs.EtcdAddr.Set(getEtcdURL())
49 41
 
50 42
 	assetAddr := httptest.NewUnstartedServer(nil).Listener.Addr().String()
51 43
 	fmt.Printf("assetAddr: %#v\n", assetAddr)
52
-	startConfig.AssetBindAddr.Set(assetAddr)
53
-	startConfig.AssetPublicAddr.Set(assetAddr)
44
+	masterArgs.AssetBindAddr.Set(assetAddr)
45
+	masterArgs.AssetPublicAddr.Set(assetAddr)
46
+
47
+	dnsAddr := httptest.NewUnstartedServer(nil).Listener.Addr().String()
48
+	fmt.Printf("dnsAddr: %#v\n", dnsAddr)
49
+	masterArgs.DNSBindAddr.Set(dnsAddr)
50
+
51
+	return masterArgs, nodeArgs, bindAddrArg, imageFormatArgs, kubeConnectionArgs, certArgs
52
+}
53
+
54
+func getAdminKubeConfigFile(certArgs start.CertArgs) string {
55
+	return path.Clean(path.Join(certArgs.CertDir, "admin/.kubeconfig"))
56
+}
57
+
58
+func StartTestAllInOne() (*configapi.MasterConfig, string, error) {
59
+	deleteAllEtcdKeys()
60
+
61
+	masterArgs, nodeArgs, _, _, _, _ := setupStartOptions()
62
+	masterArgs.NodeList = nil
54 63
 
55
-	startConfig.Complete(args)
64
+	startOptions := start.AllInOneOptions{}
65
+	startOptions.MasterArgs, startOptions.NodeArgs = masterArgs, nodeArgs
66
+	startOptions.Complete()
56 67
 
57 68
 	errCh := make(chan error)
58 69
 	go func() {
59
-		errCh <- startConfig.Start(args)
70
+		errCh <- startOptions.StartAllInOne()
60 71
 		close(errCh)
61 72
 	}()
62 73
 
74
+	adminKubeConfigFile := getAdminKubeConfigFile(*masterArgs.CertArgs)
75
+
76
+	openshiftMasterConfig, err := startOptions.MasterArgs.BuildSerializeableMasterConfig()
77
+	if err != nil {
78
+		return nil, "", err
79
+	}
80
+
63 81
 	// wait for the server to come up: 35 seconds
64
-	if err := cmdutil.WaitForSuccessfulDial(true, "tcp", masterAddr, 100*time.Millisecond, 1*time.Second, 35); err != nil {
82
+	if err := cmdutil.WaitForSuccessfulDial(true, "tcp", masterArgs.MasterAddr.URL.Host, 100*time.Millisecond, 1*time.Second, 35); err != nil {
65 83
 		select {
66 84
 		case err := <-errCh:
67 85
 			if err != nil {
68
-				return *startConfig, err
86
+				return nil, "", err
69 87
 			}
70 88
 		default:
71 89
 		}
72
-		return *startConfig, err
90
+		return nil, "", err
73 91
 	}
74 92
 
75 93
 	// try to get a client
... ...
@@ -77,34 +97,74 @@ func StartTestServer(args ...string) (start.Config, error) {
77 77
 		select {
78 78
 		case err := <-errCh:
79 79
 			if err != nil {
80
-				return *startConfig, err
80
+				return nil, "", err
81 81
 			}
82 82
 		default:
83 83
 		}
84 84
 		// confirm that we can actually query from the api server
85
-		if client, _, err := startConfig.GetOpenshiftClient(); err == nil {
85
+
86
+		if client, _, _, err := GetClusterAdminClient(adminKubeConfigFile); err == nil {
86 87
 			if _, err := client.Policies("master").List(labels.Everything(), labels.Everything()); err == nil {
87 88
 				break
88 89
 			}
89 90
 		}
90 91
 		time.Sleep(100 * time.Millisecond)
91 92
 	}
92
-	return *startConfig, nil
93
+	return openshiftMasterConfig, adminKubeConfigFile, nil
93 94
 }
94 95
 
95
-// StartTestMaster starts up a test master and returns back the startConfig so you can get clients and certs
96
-func StartTestMaster() (start.Config, error) {
97
-	return StartTestServer("master")
98
-}
96
+// TODO Unify with StartAllInOne.
99 97
 
100
-// StartTestNode starts up a test node and returns back the startConfig so you can get clients and certs
101
-func StartTestNode() (start.Config, error) {
102
-	return StartTestServer("node")
103
-}
98
+// StartTestMaster starts up a test master and returns back the startOptions so you can get clients and certs
99
+func StartTestMaster() (*configapi.MasterConfig, string, error) {
100
+	deleteAllEtcdKeys()
101
+
102
+	masterArgs, _, _, _, _, _ := setupStartOptions()
103
+
104
+	startOptions := start.MasterOptions{}
105
+	startOptions.MasterArgs = masterArgs
106
+	startOptions.Complete()
107
+
108
+	var startError error
109
+	go func() {
110
+		err := startOptions.StartMaster()
111
+		if err != nil {
112
+			startError = err
113
+			fmt.Printf("ERROR STARTING SERVER! %v", err)
114
+		}
115
+	}()
116
+
117
+	adminKubeConfigFile := getAdminKubeConfigFile(*masterArgs.CertArgs)
118
+
119
+	openshiftMasterConfig, err := startOptions.MasterArgs.BuildSerializeableMasterConfig()
120
+	if err != nil {
121
+		return nil, "", err
122
+	}
123
+
124
+	// wait for the server to come up: 35 seconds
125
+	if err := cmdutil.WaitForSuccessfulDial(true, "tcp", masterArgs.MasterAddr.URL.Host, 100*time.Millisecond, 1*time.Second, 35); err != nil {
126
+		return nil, "", err
127
+	}
128
+
129
+	stopChannel := make(chan struct{})
130
+	util.Until(
131
+		func() {
132
+			if startError != nil {
133
+				close(stopChannel)
134
+				return
135
+			}
136
+
137
+			// confirm that we can actually query from the api server
138
+			client, _, _, err := GetClusterAdminClient(adminKubeConfigFile)
139
+			if err != nil {
140
+				return
141
+			}
142
+			if _, err := client.Policies("master").List(labels.Everything(), labels.Everything()); err == nil {
143
+				close(stopChannel)
144
+			}
145
+		}, 100*time.Millisecond, stopChannel)
104 146
 
105
-// StartTestAllInOne starts up a test all-in-one and returns back the startConfig so you can get clients and certs
106
-func StartTestAllInOne() (start.Config, error) {
107
-	return StartTestServer()
147
+	return openshiftMasterConfig, adminKubeConfigFile, startError
108 148
 }
109 149
 
110 150
 // CreateNewProject creates a new project using the clusterAdminClient, then gets a token for the adminUser and returns
... ...
@@ -144,3 +204,17 @@ func CreateNewProject(clusterAdminClient *client.Client, clientConfig kclient.Co
144 144
 
145 145
 	return adminClient, nil
146 146
 }
147
+
148
+func GetClusterAdminClient(adminKubeConfigFile string) (*client.Client, *kclient.Client, *kclient.Config, error) {
149
+	kclient, clientConfig, err := configapi.GetKubeClient(adminKubeConfigFile)
150
+	if err != nil {
151
+		return nil, nil, nil, err
152
+	}
153
+
154
+	osclient, err := client.New(clientConfig)
155
+	if err != nil {
156
+		return nil, nil, nil, err
157
+	}
158
+
159
+	return osclient, kclient, clientConfig, nil
160
+}