Skip to content

Commit

Permalink
Allow in-cluster config for oc
Browse files Browse the repository at this point in the history
Because we set the default env value to empty, we can't use the default
in cluster config for 'oc' (when you run inside a container, oc works).
It's really important for container integration scenarios that oc uses
the service account token by default, just like kubeconfig.
  • Loading branch information
smarterclayton committed Nov 6, 2015
1 parent f9d8378 commit ab12593
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 2 deletions.
7 changes: 6 additions & 1 deletion hack/test-cmd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,12 @@ atomic-enterprise start \
"${NODE_CONFIG_DIR}/node-config.yaml"

# test client not configured
[ "$(oc get services 2>&1 | grep 'Error in configuration')" ]
[ "$(oc get services 2>&1 | grep 'No configuration file found, please login')" ]
unused_port=33333
# setting env bypasses the not configured message
[ "$(KUBERNETES_MASTER='http://${API_HOST}:${unused_port}' oc get services 2>&1 | grep 'did you specify the right host or port')" ]
# setting --server bypasses the not configured message
[ "$(oc get services --server=http://${API_HOST}:${unused_port} 2>&1 | grep 'did you specify the right host or port')" ]

# Set KUBERNETES_MASTER for oc from now on
export KUBERNETES_MASTER="${API_SCHEME}://${API_HOST}:${API_PORT}"
Expand Down
61 changes: 60 additions & 1 deletion pkg/cmd/util/clientcmd/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@ package clientcmd
import (
"errors"
"fmt"
"os"
"sort"
"strconv"
"time"

"github.com/golang/glog"

"github.com/spf13/pflag"
"k8s.io/kubernetes/pkg/api"
kerrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
kclient "k8s.io/kubernetes/pkg/client/unversioned"
kclientcmd "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
kclientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
Expand All @@ -36,7 +40,6 @@ import (
// New creates a default Factory for commands that should share identical server
// connection behavior. Most commands should use this method to get a factory.
func New(flags *pflag.FlagSet) *Factory {
// Override global default to "" so we force the client to ask for user input
// TODO refactor this upstream:
// DefaultCluster should not be a global
// A call to ClientConfig() should always return the best clientCfg possible
Expand All @@ -45,12 +48,68 @@ func New(flags *pflag.FlagSet) *Factory {

// TODO: there should be two client configs, one for OpenShift, and one for Kubernetes
clientConfig := DefaultClientConfig(flags)
clientConfig = defaultingClientConfig{clientConfig}
f := NewFactory(clientConfig)
f.BindFlags(flags)

return f
}

// defaultingClientConfig detects whether the provided config is the default, and if
// so returns an error that indicates the user should set up their config.
type defaultingClientConfig struct {
nested kclientcmd.ClientConfig
}

// RawConfig calls the nested method
func (c defaultingClientConfig) RawConfig() (kclientcmdapi.Config, error) {
return c.nested.RawConfig()
}

// Namespace calls the nested method, and if an empty config error is returned
// it checks for the same default as kubectl - the value of POD_NAMESPACE or
// "default".
func (c defaultingClientConfig) Namespace() (string, bool, error) {
namespace, ok, err := c.nested.Namespace()
if err == nil {
return namespace, ok, nil
}
if !kclientcmd.IsEmptyConfig(err) {
return "", false, err
}
// TODO: can we inject the namespace as a file in the secret?
namespace = os.Getenv("POD_NAMESPACE")
if len(namespace) == 0 {
return api.NamespaceDefault, false, nil
}
return namespace, true, nil
}

// ClientConfig returns a complete client config
func (c defaultingClientConfig) ClientConfig() (*kclient.Config, error) {
cfg, err := c.nested.ClientConfig()
if err == nil {
return cfg, nil
}

if !kclientcmd.IsEmptyConfig(err) {
return nil, err
}

if icc, err := kclient.InClusterConfig(); err == nil {
glog.V(4).Infof("Using in-cluster configuration")
return icc, nil
}

return nil, fmt.Errorf(`No configuration file found, please login or point to an existing file:
1. Via the command-line flag --config
2. Via the KUBECONFIG environment variable
3. In your home directory as ~/.kube/config
To view or setup config directly use the 'config' command.`)
}

// Factory provides common options for OpenShift commands
type Factory struct {
*cmdutil.Factory
Expand Down
16 changes: 16 additions & 0 deletions test/end-to-end/core.sh
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,22 @@ oc login -u e2e-user
oc project test
oc whoami

echo "[INFO] Running a CLI command in a container using the service account"
oc policy add-role-to-user view -z default
out=$(oc run cli-with-token --attach --env=POD_NAMESPACE=test --image=openshift/origin:${TAG} --restart=Never -- cli status --loglevel=4 2>&1)
echo $out
[ "$(echo $out | grep 'Using in-cluster configuration')" ]
[ "$(echo $out | grep 'Project test')" ]
oc delete pod cli-with-token
out=$(oc run cli-with-token-2 --attach --env=POD_NAMESPACE=test --image=openshift/origin:${TAG} --restart=Never -- cli whoami --loglevel=4 2>&1)
echo $out
[ "$(echo $out | grep 'system:serviceaccount:test:default')" ]
oc delete pod cli-with-token-2
out=$(oc run kubectl-with-token --attach --env=POD_NAMESPACE=test --image=openshift/origin:${TAG} --restart=Never --cmd -- kubectl get pods --loglevel=4 2>&1)
echo $out
[ "$(echo $out | grep 'Using in-cluster configuration')" ]
[ "$(echo $out | grep 'kubectl-with-token')" ]

echo "[INFO] Streaming the logs from a deployment twice..."
oc create -f test/fixtures/failing-dc.yaml
tryuntil oc get rc/failing-dc-1
Expand Down

0 comments on commit ab12593

Please sign in to comment.