| ... | ... |
@@ -289,11 +289,9 @@ This section covers how to perform all the steps of building, deploying, and upd |
| 289 | 289 |
|
| 290 | 290 |
If you want to see the build logs of a complete build, use the |
| 291 | 291 |
command below (substituting your build name from the "osc get builds" |
| 292 |
- output). Notice that for now only cluster admins can run the `build-logs` |
|
| 293 |
- command, so we have to explicitly tell the command to use the `master` |
|
| 294 |
- context from the $OPENSHIFTCONFIG config file: |
|
| 292 |
+ output). |
|
| 295 | 293 |
|
| 296 |
- $ osc build-logs ruby-sample-build-1 --context=master -n test |
|
| 294 |
+ $ osc build-logs ruby-sample-build-1 -n test |
|
| 297 | 295 |
|
| 298 | 296 |
The creation of the new image in the Docker registry will |
| 299 | 297 |
automatically trigger a deployment of the application, creating a |
| ... | ... |
@@ -238,17 +238,20 @@ OS_PID=$! |
| 238 | 238 |
|
| 239 | 239 |
export HOME="${FAKE_HOME_DIR}"
|
| 240 | 240 |
|
| 241 |
+export OPENSHIFTCONFIG="${MASTER_CONFIG_DIR}/admin.kubeconfig"
|
|
| 242 |
+CLUSTER_ADMIN_CONTEXT=$(osc config view --flatten -o template -t '{{index . "current-context"}}')
|
|
| 243 |
+ |
|
| 241 | 244 |
if [[ "${API_SCHEME}" == "https" ]]; then
|
| 242 | 245 |
export CURL_CA_BUNDLE="${MASTER_CONFIG_DIR}/ca.crt"
|
| 243 | 246 |
export CURL_CERT="${MASTER_CONFIG_DIR}/admin.crt"
|
| 244 | 247 |
export CURL_KEY="${MASTER_CONFIG_DIR}/admin.key"
|
| 245 | 248 |
|
| 246 | 249 |
# Make osc use ${MASTER_CONFIG_DIR}/admin.kubeconfig, and ignore anything in the running user's $HOME dir
|
| 247 |
- export OPENSHIFTCONFIG="${MASTER_CONFIG_DIR}/admin.kubeconfig"
|
|
| 248 | 250 |
sudo chmod -R a+rwX "${OPENSHIFTCONFIG}"
|
| 249 | 251 |
echo "[INFO] To debug: export OPENSHIFTCONFIG=$OPENSHIFTCONFIG" |
| 250 | 252 |
fi |
| 251 | 253 |
|
| 254 |
+ |
|
| 252 | 255 |
wait_for_url "${KUBELET_SCHEME}://${KUBELET_HOST}:${KUBELET_PORT}/healthz" "[INFO] kubelet: " 0.5 60
|
| 253 | 256 |
wait_for_url "${API_SCHEME}://${API_HOST}:${API_PORT}/healthz" "apiserver: " 0.25 80
|
| 254 | 257 |
wait_for_url "${API_SCHEME}://${API_HOST}:${API_PORT}/api/v1beta3/nodes/${KUBELET_HOST}" "apiserver(nodes): " 0.25 80
|
| ... | ... |
@@ -307,7 +310,7 @@ docker push ${DOCKER_REGISTRY}/cache/ruby-20-centos7:latest
|
| 307 | 307 |
echo "[INFO] Pushed ruby-20-centos7" |
| 308 | 308 |
|
| 309 | 309 |
echo "[INFO] Back to 'master' context with 'admin' user..." |
| 310 |
-osc project default |
|
| 310 |
+osc project ${CLUSTER_ADMIN_CONTEXT}
|
|
| 311 | 311 |
|
| 312 | 312 |
# Process template and create |
| 313 | 313 |
echo "[INFO] Submitting application template json for processing..." |
| ... | ... |
@@ -1,6 +1,7 @@ |
| 1 | 1 |
package config |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "crypto/x509" |
|
| 4 | 5 |
"net/url" |
| 5 | 6 |
"reflect" |
| 6 | 7 |
"strings" |
| ... | ... |
@@ -9,12 +10,18 @@ import ( |
| 9 | 9 |
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" |
| 10 | 10 |
"github.com/GoogleCloudPlatform/kubernetes/third_party/golang/netutil" |
| 11 | 11 |
|
| 12 |
+ "github.com/openshift/origin/pkg/auth/authenticator/request/x509request" |
|
| 12 | 13 |
osclient "github.com/openshift/origin/pkg/client" |
| 13 | 14 |
) |
| 14 | 15 |
|
| 15 |
-// getClusterNickname returns host:port of the clientConfig.Host, with .'s replaced by -'s |
|
| 16 |
-func getClusterNickname(clientCfg *client.Config) (string, error) {
|
|
| 17 |
- u, err := url.Parse(clientCfg.Host) |
|
| 16 |
+// GetClusterNicknameFromConfig returns host:port of the clientConfig.Host, with .'s replaced by -'s |
|
| 17 |
+func GetClusterNicknameFromConfig(clientCfg *client.Config) (string, error) {
|
|
| 18 |
+ return GetClusterNicknameFromURL(clientCfg.Host) |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+// GetClusterNicknameFromURL returns host:port of the apiServerLocation, with .'s replaced by -'s |
|
| 22 |
+func GetClusterNicknameFromURL(apiServerLocation string) (string, error) {
|
|
| 23 |
+ u, err := url.Parse(apiServerLocation) |
|
| 18 | 24 |
if err != nil {
|
| 19 | 25 |
return "", err |
| 20 | 26 |
} |
| ... | ... |
@@ -24,9 +31,9 @@ func getClusterNickname(clientCfg *client.Config) (string, error) {
|
| 24 | 24 |
return strings.Replace(hostPort, ".", "-", -1), nil |
| 25 | 25 |
} |
| 26 | 26 |
|
| 27 |
-// getUserNickname returns "username(as known by the server)/getClusterNickname". This allows tab completion for switching users to |
|
| 27 |
+// GetUserNicknameFromConfig returns "username(as known by the server)/GetClusterNicknameFromConfig". This allows tab completion for switching users to |
|
| 28 | 28 |
// work easily and obviously. |
| 29 |
-func getUserNickname(clientCfg *client.Config) (string, error) {
|
|
| 29 |
+func GetUserNicknameFromConfig(clientCfg *client.Config) (string, error) {
|
|
| 30 | 30 |
client, err := osclient.New(clientCfg) |
| 31 | 31 |
if err != nil {
|
| 32 | 32 |
return "", err |
| ... | ... |
@@ -36,7 +43,7 @@ func getUserNickname(clientCfg *client.Config) (string, error) {
|
| 36 | 36 |
return "", err |
| 37 | 37 |
} |
| 38 | 38 |
|
| 39 |
- clusterNick, err := getClusterNickname(clientCfg) |
|
| 39 |
+ clusterNick, err := GetClusterNicknameFromConfig(clientCfg) |
|
| 40 | 40 |
if err != nil {
|
| 41 | 41 |
return "", err |
| 42 | 42 |
} |
| ... | ... |
@@ -44,10 +51,19 @@ func getUserNickname(clientCfg *client.Config) (string, error) {
|
| 44 | 44 |
return userInfo.Name + "/" + clusterNick, nil |
| 45 | 45 |
} |
| 46 | 46 |
|
| 47 |
-// getContextNickname returns "namespace/getClusterNickname/username(as known by the server)". This allows tab completion for switching projects/context |
|
| 47 |
+func GetUserNicknameFromCert(clusterNick string, chain ...*x509.Certificate) (string, error) {
|
|
| 48 |
+ userInfo, _, err := x509request.SubjectToUserConversion(chain) |
|
| 49 |
+ if err != nil {
|
|
| 50 |
+ return "", err |
|
| 51 |
+ } |
|
| 52 |
+ |
|
| 53 |
+ return userInfo.GetName() + "/" + clusterNick, nil |
|
| 54 |
+} |
|
| 55 |
+ |
|
| 56 |
+// GetContextNicknameFromConfig returns "namespace/GetClusterNicknameFromConfig/username(as known by the server)". This allows tab completion for switching projects/context |
|
| 48 | 57 |
// to work easily. First tab is the most selective on project. Second stanza in the next most selective on cluster name. The chances of a user trying having |
| 49 | 58 |
// one projects on a single server that they want to operate against with two identities is low, so username is last. |
| 50 |
-func getContextNickname(namespace string, clientCfg *client.Config) (string, error) {
|
|
| 59 |
+func GetContextNicknameFromConfig(namespace string, clientCfg *client.Config) (string, error) {
|
|
| 51 | 60 |
client, err := osclient.New(clientCfg) |
| 52 | 61 |
if err != nil {
|
| 53 | 62 |
return "", err |
| ... | ... |
@@ -57,7 +73,7 @@ func getContextNickname(namespace string, clientCfg *client.Config) (string, err |
| 57 | 57 |
return "", err |
| 58 | 58 |
} |
| 59 | 59 |
|
| 60 |
- clusterNick, err := getClusterNickname(clientCfg) |
|
| 60 |
+ clusterNick, err := GetClusterNicknameFromConfig(clientCfg) |
|
| 61 | 61 |
if err != nil {
|
| 62 | 62 |
return "", err |
| 63 | 63 |
} |
| ... | ... |
@@ -65,19 +81,24 @@ func getContextNickname(namespace string, clientCfg *client.Config) (string, err |
| 65 | 65 |
return namespace + "/" + clusterNick + "/" + userInfo.Name, nil |
| 66 | 66 |
} |
| 67 | 67 |
|
| 68 |
+func GetContextNickname(namespace, clusterNick, userNick string) (string, error) {
|
|
| 69 |
+ tokens := strings.SplitN(userNick, "/", 2) |
|
| 70 |
+ return namespace + "/" + clusterNick + "/" + tokens[0], nil |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 68 | 73 |
// CreatePartialConfig takes a clientCfg and builds a config (kubeconfig style) from it. |
| 69 | 74 |
func CreateConfig(namespace string, clientCfg *client.Config) (*clientcmdapi.Config, error) {
|
| 70 |
- clusterNick, err := getClusterNickname(clientCfg) |
|
| 75 |
+ clusterNick, err := GetClusterNicknameFromConfig(clientCfg) |
|
| 71 | 76 |
if err != nil {
|
| 72 | 77 |
return nil, err |
| 73 | 78 |
} |
| 74 | 79 |
|
| 75 |
- userNick, err := getUserNickname(clientCfg) |
|
| 80 |
+ userNick, err := GetUserNicknameFromConfig(clientCfg) |
|
| 76 | 81 |
if err != nil {
|
| 77 | 82 |
return nil, err |
| 78 | 83 |
} |
| 79 | 84 |
|
| 80 |
- contextNick, err := getContextNickname(namespace, clientCfg) |
|
| 85 |
+ contextNick, err := GetContextNicknameFromConfig(namespace, clientCfg) |
|
| 81 | 86 |
if err != nil {
|
| 82 | 87 |
return nil, err |
| 83 | 88 |
} |
| ... | ... |
@@ -134,11 +134,9 @@ func (o CreateClientOptions) CreateClientFolder() error {
|
| 134 | 134 |
APIServerURL: o.APIServerURL, |
| 135 | 135 |
PublicAPIServerURL: o.PublicAPIServerURL, |
| 136 | 136 |
APIServerCAFile: clientCopyOfCAFile, |
| 137 |
- ServerNick: "master", |
|
| 138 | 137 |
|
| 139 | 138 |
CertFile: clientCertFile, |
| 140 | 139 |
KeyFile: clientKeyFile, |
| 141 |
- UserNick: o.User, |
|
| 142 | 140 |
|
| 143 | 141 |
ContextNamespace: kapi.NamespaceDefault, |
| 144 | 142 |
|
| ... | ... |
@@ -16,6 +16,8 @@ import ( |
| 16 | 16 |
|
| 17 | 17 |
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" |
| 18 | 18 |
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" |
| 19 |
+ cliconfig "github.com/openshift/origin/pkg/cmd/cli/config" |
|
| 20 |
+ "github.com/openshift/origin/pkg/cmd/server/crypto" |
|
| 19 | 21 |
cmdutil "github.com/openshift/origin/pkg/cmd/util" |
| 20 | 22 |
) |
| 21 | 23 |
|
| ... | ... |
@@ -25,13 +27,10 @@ type CreateKubeConfigOptions struct {
|
| 25 | 25 |
APIServerURL string |
| 26 | 26 |
PublicAPIServerURL string |
| 27 | 27 |
APIServerCAFile string |
| 28 |
- ServerNick string |
|
| 29 | 28 |
|
| 30 | 29 |
CertFile string |
| 31 | 30 |
KeyFile string |
| 32 |
- UserNick string |
|
| 33 | 31 |
|
| 34 |
- ContextNick string |
|
| 35 | 32 |
ContextNamespace string |
| 36 | 33 |
|
| 37 | 34 |
KubeConfigFile string |
| ... | ... |
@@ -92,11 +91,8 @@ users: |
| 92 | 92 |
flags.StringVar(&options.APIServerURL, "master", "https://localhost:8443", "The API server's URL.") |
| 93 | 93 |
flags.StringVar(&options.PublicAPIServerURL, "public-master", "", "The API public facing server's URL (if applicable).") |
| 94 | 94 |
flags.StringVar(&options.APIServerCAFile, "certificate-authority", "openshift.local.config/master/ca.crt", "Path to the API server's CA file.") |
| 95 |
- flags.StringVar(&options.ServerNick, "cluster", "master", "Nick name for this server in .kubeconfig.") |
|
| 96 | 95 |
flags.StringVar(&options.CertFile, "client-certificate", "", "The client cert file.") |
| 97 | 96 |
flags.StringVar(&options.KeyFile, "client-key", "", "The client key file.") |
| 98 |
- flags.StringVar(&options.UserNick, "user", "user", "Nick name for this user in .kubeconfig.") |
|
| 99 |
- flags.StringVar(&options.ContextNick, "context", "", "Nick name for this context in .kubeconfig.") |
|
| 100 | 97 |
flags.StringVar(&options.ContextNamespace, "namespace", kapi.NamespaceDefault, "Namespace for this context in .kubeconfig.") |
| 101 | 98 |
flags.StringVar(&options.KubeConfigFile, "kubeconfig", ".kubeconfig", "Path for the resulting .kubeconfig file.") |
| 102 | 99 |
|
| ... | ... |
@@ -119,15 +115,12 @@ func (o CreateKubeConfigOptions) Validate(args []string) error {
|
| 119 | 119 |
if len(o.APIServerCAFile) == 0 {
|
| 120 | 120 |
return errors.New("certificate-authority must be provided")
|
| 121 | 121 |
} |
| 122 |
- if len(o.ServerNick) == 0 {
|
|
| 123 |
- return errors.New("cluster must be provided")
|
|
| 124 |
- } |
|
| 125 |
- if len(o.UserNick) == 0 {
|
|
| 126 |
- return errors.New("user-nick must be provided")
|
|
| 127 |
- } |
|
| 128 | 122 |
if len(o.ContextNamespace) == 0 {
|
| 129 | 123 |
return errors.New("namespace must be provided")
|
| 130 | 124 |
} |
| 125 |
+ if len(o.APIServerURL) == 0 {
|
|
| 126 |
+ return errors.New("master must be provided")
|
|
| 127 |
+ } |
|
| 131 | 128 |
|
| 132 | 129 |
return nil |
| 133 | 130 |
} |
| ... | ... |
@@ -135,11 +128,7 @@ func (o CreateKubeConfigOptions) Validate(args []string) error {
|
| 135 | 135 |
func (o CreateKubeConfigOptions) CreateKubeConfig() (*clientcmdapi.Config, error) {
|
| 136 | 136 |
glog.V(2).Infof("creating a .kubeconfig with: %#v", o)
|
| 137 | 137 |
|
| 138 |
- // if you don't specify a context nick, assign it to the context namespace |
|
| 139 |
- if len(o.ContextNick) == 0 {
|
|
| 140 |
- o.ContextNick = o.ContextNamespace |
|
| 141 |
- } |
|
| 142 |
- |
|
| 138 |
+ // read all the refenced filenames |
|
| 143 | 139 |
caData, err := ioutil.ReadFile(o.APIServerCAFile) |
| 144 | 140 |
if err != nil {
|
| 145 | 141 |
return nil, err |
| ... | ... |
@@ -152,41 +141,66 @@ func (o CreateKubeConfigOptions) CreateKubeConfig() (*clientcmdapi.Config, error |
| 152 | 152 |
if err != nil {
|
| 153 | 153 |
return nil, err |
| 154 | 154 |
} |
| 155 |
+ certConfig, err := crypto.GetTLSCertificateConfig(o.CertFile, o.KeyFile) |
|
| 156 |
+ if err != nil {
|
|
| 157 |
+ return nil, err |
|
| 158 |
+ } |
|
| 159 |
+ |
|
| 160 |
+ // determine all the nicknames |
|
| 161 |
+ clusterNick, err := cliconfig.GetClusterNicknameFromURL(o.APIServerURL) |
|
| 162 |
+ if err != nil {
|
|
| 163 |
+ return nil, err |
|
| 164 |
+ } |
|
| 165 |
+ userNick, err := cliconfig.GetUserNicknameFromCert(clusterNick, certConfig.Certs...) |
|
| 166 |
+ if err != nil {
|
|
| 167 |
+ return nil, err |
|
| 168 |
+ } |
|
| 169 |
+ contextNick, err := cliconfig.GetContextNickname(o.ContextNamespace, clusterNick, userNick) |
|
| 170 |
+ if err != nil {
|
|
| 171 |
+ return nil, err |
|
| 172 |
+ } |
|
| 155 | 173 |
|
| 156 | 174 |
credentials := make(map[string]clientcmdapi.AuthInfo) |
| 157 |
- credentials[o.UserNick] = clientcmdapi.AuthInfo{
|
|
| 175 |
+ credentials[userNick] = clientcmdapi.AuthInfo{
|
|
| 158 | 176 |
ClientCertificateData: certData, |
| 159 | 177 |
ClientKeyData: keyData, |
| 160 | 178 |
} |
| 161 | 179 |
|
| 162 | 180 |
clusters := make(map[string]clientcmdapi.Cluster) |
| 163 |
- clusters[o.ServerNick] = clientcmdapi.Cluster{
|
|
| 181 |
+ clusters[clusterNick] = clientcmdapi.Cluster{
|
|
| 164 | 182 |
Server: o.APIServerURL, |
| 165 | 183 |
CertificateAuthorityData: caData, |
| 166 | 184 |
} |
| 167 | 185 |
|
| 168 | 186 |
contexts := make(map[string]clientcmdapi.Context) |
| 169 |
- contexts[o.ContextNick] = clientcmdapi.Context{Cluster: o.ServerNick, AuthInfo: o.UserNick, Namespace: o.ContextNamespace}
|
|
| 187 |
+ contexts[contextNick] = clientcmdapi.Context{Cluster: clusterNick, AuthInfo: userNick, Namespace: o.ContextNamespace}
|
|
| 170 | 188 |
|
| 171 |
- createPublic := len(o.PublicAPIServerURL) > 0 |
|
| 189 |
+ createPublic := (len(o.PublicAPIServerURL) > 0) && o.APIServerURL != o.PublicAPIServerURL |
|
| 172 | 190 |
if createPublic {
|
| 173 |
- publicClusterNick := "public-" + o.ServerNick |
|
| 174 |
- publicContextNick := "public-" + o.ContextNick |
|
| 191 |
+ publicClusterNick, err := cliconfig.GetClusterNicknameFromURL(o.PublicAPIServerURL) |
|
| 192 |
+ if err != nil {
|
|
| 193 |
+ return nil, err |
|
| 194 |
+ } |
|
| 195 |
+ publicContextNick, err := cliconfig.GetContextNickname(o.ContextNamespace, publicClusterNick, userNick) |
|
| 196 |
+ if err != nil {
|
|
| 197 |
+ return nil, err |
|
| 198 |
+ } |
|
| 199 |
+ |
|
| 175 | 200 |
clusters[publicClusterNick] = clientcmdapi.Cluster{
|
| 176 | 201 |
Server: o.PublicAPIServerURL, |
| 177 | 202 |
CertificateAuthorityData: caData, |
| 178 | 203 |
} |
| 179 |
- contexts[publicContextNick] = clientcmdapi.Context{Cluster: publicClusterNick, AuthInfo: o.UserNick, Namespace: o.ContextNamespace}
|
|
| 204 |
+ contexts[publicContextNick] = clientcmdapi.Context{Cluster: publicClusterNick, AuthInfo: userNick, Namespace: o.ContextNamespace}
|
|
| 180 | 205 |
} |
| 181 | 206 |
|
| 182 | 207 |
kubeConfig := &clientcmdapi.Config{
|
| 183 | 208 |
Clusters: clusters, |
| 184 | 209 |
AuthInfos: credentials, |
| 185 | 210 |
Contexts: contexts, |
| 186 |
- CurrentContext: o.ContextNick, |
|
| 211 |
+ CurrentContext: contextNick, |
|
| 187 | 212 |
} |
| 188 | 213 |
|
| 189 |
- fmt.Fprintf(o.Output.Get(), "Generating '%s' API client config as %s\n", o.UserNick, o.KubeConfigFile) |
|
| 214 |
+ fmt.Fprintf(o.Output.Get(), "Generating '%s' API client config as %s\n", userNick, o.KubeConfigFile) |
|
| 190 | 215 |
// Ensure the parent dir exists |
| 191 | 216 |
if err := os.MkdirAll(filepath.Dir(o.KubeConfigFile), os.FileMode(0755)); err != nil {
|
| 192 | 217 |
return nil, err |
| ... | ... |
@@ -184,11 +184,9 @@ func (o CreateMasterCertsOptions) createAPIClients(getSignerCertOptions *GetSign |
| 184 | 184 |
APIServerURL: o.APIServerURL, |
| 185 | 185 |
PublicAPIServerURL: o.PublicAPIServerURL, |
| 186 | 186 |
APIServerCAFile: getSignerCertOptions.CertFile, |
| 187 |
- ServerNick: "master", |
|
| 188 | 187 |
|
| 189 | 188 |
CertFile: clientCertInfo.CertLocation.CertFile, |
| 190 | 189 |
KeyFile: clientCertInfo.CertLocation.KeyFile, |
| 191 |
- UserNick: clientCertInfo.User, |
|
| 192 | 190 |
|
| 193 | 191 |
ContextNamespace: kapi.NamespaceDefault, |
| 194 | 192 |
|
| ... | ... |
@@ -323,11 +323,9 @@ func (o CreateNodeConfigOptions) MakeKubeConfig(clientCertFile, clientKeyFile, c |
| 323 | 323 |
createKubeConfigOptions := CreateKubeConfigOptions{
|
| 324 | 324 |
APIServerURL: o.APIServerURL, |
| 325 | 325 |
APIServerCAFile: clientCopyOfCAFile, |
| 326 |
- ServerNick: "master", |
|
| 327 | 326 |
|
| 328 | 327 |
CertFile: clientCertFile, |
| 329 | 328 |
KeyFile: clientKeyFile, |
| 330 |
- UserNick: "node", |
|
| 331 | 329 |
|
| 332 | 330 |
ContextNamespace: kapi.NamespaceDefault, |
| 333 | 331 |
|