pkg/cmd/server/api/helpers.go
30380339
 package api
 
 import (
 	"crypto/x509"
 	"fmt"
2d02cf55
 	"net"
 	"net/http"
970f2c0f
 	"strings"
2d02cf55
 	"time"
30380339
 
83c702b4
 	kclient "k8s.io/kubernetes/pkg/client"
 	"k8s.io/kubernetes/pkg/client/clientcmd"
 	"k8s.io/kubernetes/pkg/runtime"
 	"k8s.io/kubernetes/pkg/util"
30380339
 
 	"github.com/openshift/origin/pkg/client"
86e9e5c0
 	cmdutil "github.com/openshift/origin/pkg/cmd/util"
30380339
 )
 
642797f7
 var (
 	knownOpenShiftFeatureSet map[string]string
 )
 
 func init() {
 	knownOpenShiftFeatureSet = make(map[string]string, len(KnownOpenShiftFeatures))
 	for _, feature := range KnownOpenShiftFeatures {
 		knownOpenShiftFeatureSet[strings.ToLower(feature)] = feature
 	}
 }
 
 // Add extends feature list with given valid items. They are appended
 // unless already present.
 func (fl *FeatureList) Add(items ...string) error {
 	unknown := []string{}
 	toAppend := make([]string, 0, len(items))
 	for _, item := range items {
 		feature, exists := knownOpenShiftFeatureSet[strings.ToLower(item)]
 		if !exists {
 			unknown = append(unknown, item)
 			continue
 		}
 		if fl.Has(feature) {
 			continue
 		}
 		toAppend = append(toAppend, feature)
 	}
 	if len(unknown) > 0 {
 		return fmt.Errorf("unknown features: %s", strings.Join(unknown, ", "))
 	}
 	*fl = append(*fl, toAppend...)
 	return nil
 }
 
 // Delete removes given items from feature list while keeping its original
 // order.
 func (fl *FeatureList) Delete(items ...string) {
 	if len(*fl) == 0 || len(items) == 0 {
 		return
 	}
 	toDelete := util.NewStringSet()
 	for _, item := range items {
 		toDelete.Insert(strings.ToLower(item))
 	}
 	newList := []string{}
 	for _, item := range *fl {
 		if !toDelete.Has(strings.ToLower(item)) {
 			newList = append(newList, item)
 		}
 	}
 	*fl = newList
 }
 
 // Has returns true if given feature exists in feature list. The check is
 // case-insensitive.
 func (fl FeatureList) Has(feature string) bool {
 	lowerCased := strings.ToLower(feature)
 	for _, item := range fl {
 		if strings.ToLower(item) == lowerCased {
 			return true
 		}
 	}
 	return false
 }
 
970f2c0f
 // ParseNamespaceAndName returns back the namespace and name (empty if something goes wrong), for a given string.
 // This is useful when pointing to a particular resource inside of our config.
 func ParseNamespaceAndName(in string) (string, string, error) {
 	if len(in) == 0 {
 		return "", "", nil
 	}
 
 	tokens := strings.Split(in, "/")
 	if len(tokens) != 2 {
 		return "", "", fmt.Errorf("expected input in the form <namespace>/<resource-name>, not: %v", in)
 	}
 
 	return tokens[0], tokens[1], nil
 }
 
86e9e5c0
 func RelativizeMasterConfigPaths(config *MasterConfig, base string) error {
8564fdc7
 	return cmdutil.RelativizePathWithNoBacksteps(GetMasterFileReferences(config), base)
86e9e5c0
 }
 
 func ResolveMasterConfigPaths(config *MasterConfig, base string) error {
 	return cmdutil.ResolvePaths(GetMasterFileReferences(config), base)
 }
 
 func GetMasterFileReferences(config *MasterConfig) []*string {
 	refs := []*string{}
 
 	refs = append(refs, &config.ServingInfo.ServerCert.CertFile)
 	refs = append(refs, &config.ServingInfo.ServerCert.KeyFile)
 	refs = append(refs, &config.ServingInfo.ClientCA)
 
 	refs = append(refs, &config.EtcdClientInfo.ClientCert.CertFile)
 	refs = append(refs, &config.EtcdClientInfo.ClientCert.KeyFile)
 	refs = append(refs, &config.EtcdClientInfo.CA)
 
53c7aa34
 	refs = append(refs, &config.KubeletClientInfo.ClientCert.CertFile)
 	refs = append(refs, &config.KubeletClientInfo.ClientCert.KeyFile)
 	refs = append(refs, &config.KubeletClientInfo.CA)
 
86e9e5c0
 	if config.EtcdConfig != nil {
 		refs = append(refs, &config.EtcdConfig.ServingInfo.ServerCert.CertFile)
 		refs = append(refs, &config.EtcdConfig.ServingInfo.ServerCert.KeyFile)
 		refs = append(refs, &config.EtcdConfig.ServingInfo.ClientCA)
53c7aa34
 
 		refs = append(refs, &config.EtcdConfig.PeerServingInfo.ServerCert.CertFile)
 		refs = append(refs, &config.EtcdConfig.PeerServingInfo.ServerCert.KeyFile)
 		refs = append(refs, &config.EtcdConfig.PeerServingInfo.ClientCA)
 
86e9e5c0
 		refs = append(refs, &config.EtcdConfig.StorageDir)
 	}
 
 	if config.OAuthConfig != nil {
4457e6fd
 
 		if config.OAuthConfig.SessionConfig != nil {
 			refs = append(refs, &config.OAuthConfig.SessionConfig.SessionSecretsFile)
 		}
 
130758bb
 		for _, identityProvider := range config.OAuthConfig.IdentityProviders {
 			switch provider := identityProvider.Provider.Object.(type) {
 			case (*RequestHeaderIdentityProvider):
 				refs = append(refs, &provider.ClientCA)
 
 			case (*HTPasswdPasswordIdentityProvider):
 				refs = append(refs, &provider.File)
 
f3d621f6
 			case (*LDAPPasswordIdentityProvider):
 				refs = append(refs, &provider.CA)
 
130758bb
 			case (*BasicAuthPasswordIdentityProvider):
 				refs = append(refs, &provider.RemoteConnectionInfo.CA)
 				refs = append(refs, &provider.RemoteConnectionInfo.ClientCert.CertFile)
 				refs = append(refs, &provider.RemoteConnectionInfo.ClientCert.KeyFile)
 
a8407292
 			case (*OpenIDIdentityProvider):
 				refs = append(refs, &provider.CA)
 
130758bb
 			}
 		}
86e9e5c0
 	}
 
 	if config.AssetConfig != nil {
 		refs = append(refs, &config.AssetConfig.ServingInfo.ServerCert.CertFile)
 		refs = append(refs, &config.AssetConfig.ServingInfo.ServerCert.KeyFile)
 		refs = append(refs, &config.AssetConfig.ServingInfo.ClientCA)
 	}
 
287c3a51
 	if config.KubernetesMasterConfig != nil {
 		refs = append(refs, &config.KubernetesMasterConfig.SchedulerConfigFile)
 	}
 
5b4ee6a4
 	refs = append(refs, &config.ServiceAccountConfig.MasterCA)
878d37a8
 	refs = append(refs, &config.ServiceAccountConfig.PrivateKeyFile)
 	for i := range config.ServiceAccountConfig.PublicKeyFiles {
 		refs = append(refs, &config.ServiceAccountConfig.PublicKeyFiles[i])
 	}
 
86e9e5c0
 	refs = append(refs, &config.MasterClients.OpenShiftLoopbackKubeConfig)
da1980d3
 	refs = append(refs, &config.MasterClients.ExternalKubernetesKubeConfig)
86e9e5c0
 
287c3a51
 	refs = append(refs, &config.PolicyConfig.BootstrapPolicyFile)
 
86e9e5c0
 	return refs
 }
 
 func RelativizeNodeConfigPaths(config *NodeConfig, base string) error {
8564fdc7
 	return cmdutil.RelativizePathWithNoBacksteps(GetNodeFileReferences(config), base)
86e9e5c0
 }
 
 func ResolveNodeConfigPaths(config *NodeConfig, base string) error {
 	return cmdutil.ResolvePaths(GetNodeFileReferences(config), base)
 }
 
 func GetNodeFileReferences(config *NodeConfig) []*string {
 	refs := []*string{}
 
 	refs = append(refs, &config.ServingInfo.ServerCert.CertFile)
 	refs = append(refs, &config.ServingInfo.ServerCert.KeyFile)
 	refs = append(refs, &config.ServingInfo.ClientCA)
 
 	refs = append(refs, &config.MasterKubeConfig)
 
 	refs = append(refs, &config.VolumeDirectory)
 
d25e310c
 	if config.PodManifestConfig != nil {
 		refs = append(refs, &config.PodManifestConfig.Path)
 	}
 
86e9e5c0
 	return refs
 }
 
bb8a2276
 // TODO: clients should be copied and instantiated from a common client config, tweaked, then
 // given to individual controllers and other infrastructure components.
30380339
 func GetKubeClient(kubeConfigFile string) (*kclient.Client, *kclient.Config, error) {
c087f950
 	loadingRules := &clientcmd.ClientConfigLoadingRules{}
 	loadingRules.ExplicitPath = kubeConfigFile
30380339
 	loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
 
 	kubeConfig, err := loader.ClientConfig()
 	if err != nil {
 		return nil, nil, err
 	}
ee1f59b0
 
bb8a2276
 	// This is an internal client which is shared by most controllers, so boost default QPS
 	// TODO: this should be configured by the caller, not in this method.
 	kubeConfig.QPS = 100.0
 	kubeConfig.Burst = 200
ee1f59b0
 
2d02cf55
 	kubeConfig.WrapTransport = DefaultClientTransport
30380339
 	kubeClient, err := kclient.New(kubeConfig)
 	if err != nil {
 		return nil, nil, err
 	}
 
 	return kubeClient, kubeConfig, nil
 }
 
bb8a2276
 // TODO: clients should be copied and instantiated from a common client config, tweaked, then
 // given to individual controllers and other infrastructure components.
30380339
 func GetOpenShiftClient(kubeConfigFile string) (*client.Client, *kclient.Config, error) {
c087f950
 	loadingRules := &clientcmd.ClientConfigLoadingRules{}
 	loadingRules.ExplicitPath = kubeConfigFile
30380339
 	loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
 
 	kubeConfig, err := loader.ClientConfig()
 	if err != nil {
 		return nil, nil, err
 	}
bb8a2276
 
 	// This is an internal client which is shared by most controllers, so boost default QPS
 	// TODO: this should be configured by the caller, not in this method.
 	kubeConfig.QPS = 150.0
 	kubeConfig.Burst = 300
 
2d02cf55
 	kubeConfig.WrapTransport = DefaultClientTransport
30380339
 	openshiftClient, err := client.New(kubeConfig)
 	if err != nil {
 		return nil, nil, err
 	}
 
 	return openshiftClient, kubeConfig, nil
 }
 
2d02cf55
 // DefaultClientTransport sets defaults for a client Transport that are suitable
 // for use by infrastructure components.
 func DefaultClientTransport(rt http.RoundTripper) http.RoundTripper {
 	transport := rt.(*http.Transport)
bb8a2276
 	// TODO: this should be configured by the caller, not in this method.
2d02cf55
 	dialer := &net.Dialer{
 		Timeout:   30 * time.Second,
 		KeepAlive: 30 * time.Second,
 	}
 	transport.Dial = dialer.Dial
 	// Hold open more internal idle connections
bb8a2276
 	// TODO: this should be configured by the caller, not in this method.
 	transport.MaxIdleConnsPerHost = 100
2d02cf55
 	return transport
 }
 
30380339
 func UseTLS(servingInfo ServingInfo) bool {
 	return len(servingInfo.ServerCert.CertFile) > 0
 }
 
 // GetAPIClientCertCAPool returns the cert pool used to validate client certificates to the API server
 func GetAPIClientCertCAPool(options MasterConfig) (*x509.CertPool, error) {
3aa6d379
 	return cmdutil.CertPoolFromFile(options.ServingInfo.ClientCA)
30380339
 }
 
 // GetClientCertCAPool returns a cert pool containing all client CAs that could be presented (union of API and OAuth)
 func GetClientCertCAPool(options MasterConfig) (*x509.CertPool, error) {
 	roots := x509.NewCertPool()
 
 	// Add CAs for OAuth
 	certs, err := getOAuthClientCertCAs(options)
 	if err != nil {
 		return nil, err
 	}
 	for _, root := range certs {
 		roots.AddCert(root)
 	}
 
 	// Add CAs for API
 	certs, err = getAPIClientCertCAs(options)
 	if err != nil {
 		return nil, err
 	}
 	for _, root := range certs {
 		roots.AddCert(root)
 	}
 
 	return roots, nil
 }
 
 // GetAPIServerCertCAPool returns the cert pool containing the roots for the API server cert
 func GetAPIServerCertCAPool(options MasterConfig) (*x509.CertPool, error) {
e00edaae
 	if !UseTLS(options.ServingInfo.ServingInfo) {
41dae4d1
 		return x509.NewCertPool(), nil
 	}
 
3aa6d379
 	return cmdutil.CertPoolFromFile(options.ServingInfo.ClientCA)
30380339
 }
 
 func getOAuthClientCertCAs(options MasterConfig) ([]*x509.Certificate, error) {
e00edaae
 	if !UseTLS(options.ServingInfo.ServingInfo) {
41dae4d1
 		return nil, nil
 	}
 
130758bb
 	allCerts := []*x509.Certificate{}
 
 	if options.OAuthConfig != nil {
 		for _, identityProvider := range options.OAuthConfig.IdentityProviders {
 
 			switch provider := identityProvider.Provider.Object.(type) {
 			case (*RequestHeaderIdentityProvider):
 				caFile := provider.ClientCA
 				if len(caFile) == 0 {
53c7aa34
 					continue
130758bb
 				}
3aa6d379
 				certs, err := cmdutil.CertificatesFromFile(caFile)
130758bb
 				if err != nil {
 					return nil, fmt.Errorf("Error reading %s: %s", caFile, err)
 				}
 				allCerts = append(allCerts, certs...)
 			}
 		}
30380339
 	}
130758bb
 
 	return allCerts, nil
30380339
 }
 
 func getAPIClientCertCAs(options MasterConfig) ([]*x509.Certificate, error) {
e00edaae
 	if !UseTLS(options.ServingInfo.ServingInfo) {
41dae4d1
 		return nil, nil
 	}
 
3aa6d379
 	return cmdutil.CertificatesFromFile(options.ServingInfo.ClientCA)
53c7aa34
 }
 
 func GetKubeletClientConfig(options MasterConfig) *kclient.KubeletConfig {
 	config := &kclient.KubeletConfig{
 		Port: options.KubeletClientInfo.Port,
 	}
 
 	if len(options.KubeletClientInfo.CA) > 0 {
 		config.EnableHttps = true
 		config.CAFile = options.KubeletClientInfo.CA
 	}
 
 	if len(options.KubeletClientInfo.ClientCert.CertFile) > 0 {
 		config.EnableHttps = true
 		config.CertFile = options.KubeletClientInfo.ClientCert.CertFile
 		config.KeyFile = options.KubeletClientInfo.ClientCert.KeyFile
30380339
 	}
 
53c7aa34
 	return config
30380339
 }
130758bb
 
 func IsPasswordAuthenticator(provider IdentityProvider) bool {
e14c2574
 	switch provider.Provider.Object.(type) {
130758bb
 	case
 		(*BasicAuthPasswordIdentityProvider),
 		(*AllowAllPasswordIdentityProvider),
 		(*DenyAllPasswordIdentityProvider),
f3d621f6
 		(*HTPasswdPasswordIdentityProvider),
 		(*LDAPPasswordIdentityProvider):
130758bb
 
 		return true
 	}
 
 	return false
 }
 
 func IsIdentityProviderType(provider runtime.EmbeddedObject) bool {
 	switch provider.Object.(type) {
 	case
 		(*RequestHeaderIdentityProvider),
 		(*BasicAuthPasswordIdentityProvider),
 		(*AllowAllPasswordIdentityProvider),
 		(*DenyAllPasswordIdentityProvider),
e14c2574
 		(*HTPasswdPasswordIdentityProvider),
f3d621f6
 		(*LDAPPasswordIdentityProvider),
a8407292
 		(*OpenIDIdentityProvider),
e14c2574
 		(*GitHubIdentityProvider),
 		(*GoogleIdentityProvider):
130758bb
 
 		return true
 	}
 
 	return false
 }
 
e14c2574
 func IsOAuthIdentityProvider(provider IdentityProvider) bool {
 	switch provider.Provider.Object.(type) {
130758bb
 	case
a8407292
 		(*OpenIDIdentityProvider),
e14c2574
 		(*GitHubIdentityProvider),
 		(*GoogleIdentityProvider):
130758bb
 
 		return true
 	}
 
 	return false
 }
26c990fe
 
 func HasOpenShiftAPILevel(config MasterConfig, apiLevel string) bool {
 	apiLevelSet := util.NewStringSet(config.APILevels...)
 	return apiLevelSet.Has(apiLevel)
 }
 
 func HasKubernetesAPILevel(config KubernetesMasterConfig, apiLevel string) bool {
 	apiLevelSet := util.NewStringSet(config.APILevels...)
 	return apiLevelSet.Has(apiLevel)
 }