package util
import (
"fmt"
"math/rand"
"os"
"path"
"path/filepath"
"time"
kapi "k8s.io/kubernetes/pkg/api"
kerrs "k8s.io/kubernetes/pkg/api/errors"
kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
kcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned"
"k8s.io/kubernetes/pkg/client/restclient"
kclient "k8s.io/kubernetes/pkg/client/unversioned"
adapter "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/quota"
"k8s.io/kubernetes/pkg/util/wait"
"github.com/openshift/origin/pkg/client"
configapi "github.com/openshift/origin/pkg/cmd/server/api"
"github.com/openshift/origin/pkg/cmd/server/origin"
cmdutil "github.com/openshift/origin/pkg/cmd/util"
"github.com/openshift/origin/pkg/cmd/util/clientcmd"
"github.com/openshift/origin/pkg/cmd/util/tokencmd"
oauthapi "github.com/openshift/origin/pkg/oauth/api"
"github.com/openshift/origin/pkg/serviceaccounts"
)
// GetBaseDir returns the base directory used for test.
func GetBaseDir() string {
return cmdutil.Env("BASETMPDIR", path.Join(os.TempDir(), "openshift-"+Namespace()))
}
func KubeConfigPath() string {
return filepath.Join(GetBaseDir(), "openshift.local.config", "master", "admin.kubeconfig")
}
func GetClusterAdminKubeClient(adminKubeConfigFile string) (*kclientset.Clientset, error) {
_, c, _, err := configapi.GetKubeClient(adminKubeConfigFile, nil)
if err != nil {
return nil, err
}
return c, nil
}
func GetClusterAdminClient(adminKubeConfigFile string) (*client.Client, error) {
clientConfig, err := GetClusterAdminClientConfig(adminKubeConfigFile)
if err != nil {
return nil, err
}
osClient, err := client.New(clientConfig)
if err != nil {
return nil, err
}
return osClient, nil
}
func GetClusterAdminClientConfig(adminKubeConfigFile string) (*restclient.Config, error) {
_, _, conf, err := configapi.GetKubeClient(adminKubeConfigFile, nil)
if err != nil {
return nil, err
}
return conf, nil
}
// TODO internalclientset: get rid of oldClient after next rebase
func GetClientForUser(clientConfig restclient.Config, username string) (*client.Client, *kclientset.Clientset, *restclient.Config, error) {
token, err := tokencmd.RequestToken(&clientConfig, nil, username, "password")
if err != nil {
return nil, nil, nil, err
}
userClientConfig := clientcmd.AnonymousClientConfig(&clientConfig)
userClientConfig.BearerToken = token
kubeClient, err := kclient.New(&userClientConfig)
if err != nil {
return nil, nil, nil, err
}
kubeClientset := adapter.FromUnversionedClient(kubeClient)
osClient, err := client.New(&userClientConfig)
if err != nil {
return nil, nil, nil, err
}
return osClient, kubeClientset, &userClientConfig, nil
}
func GetScopedClientForUser(adminClient *client.Client, clientConfig restclient.Config, username string, scopes []string) (*client.Client, *kclient.Client, *restclient.Config, error) {
// make sure the user exists
if _, _, _, err := GetClientForUser(clientConfig, username); err != nil {
return nil, nil, nil, err
}
user, err := adminClient.Users().Get(username)
if err != nil {
return nil, nil, nil, err
}
token := &oauthapi.OAuthAccessToken{
ObjectMeta: kapi.ObjectMeta{Name: fmt.Sprintf("%s-token-plus-some-padding-here-to-make-the-limit-%d", username, rand.Int())},
ClientName: origin.OpenShiftCLIClientID,
ExpiresIn: 86400,
Scopes: scopes,
RedirectURI: "https://127.0.0.1:12000/oauth/token/implicit",
UserName: user.Name,
UserUID: string(user.UID),
}
if _, err := adminClient.OAuthAccessTokens().Create(token); err != nil {
return nil, nil, nil, err
}
scopedConfig := clientcmd.AnonymousClientConfig(&clientConfig)
scopedConfig.BearerToken = token.Name
kubeClient, err := kclient.New(&scopedConfig)
if err != nil {
return nil, nil, nil, err
}
osClient, err := client.New(&scopedConfig)
if err != nil {
return nil, nil, nil, err
}
return osClient, kubeClient, &scopedConfig, nil
}
func GetClientForServiceAccount(adminClient *kclientset.Clientset, clientConfig restclient.Config, namespace, name string) (*client.Client, *kclientset.Clientset, *restclient.Config, error) {
_, err := adminClient.Core().Namespaces().Create(&kapi.Namespace{ObjectMeta: kapi.ObjectMeta{Name: namespace}})
if err != nil && !kerrs.IsAlreadyExists(err) {
return nil, nil, nil, err
}
sa, err := adminClient.Core().ServiceAccounts(namespace).Create(&kapi.ServiceAccount{ObjectMeta: kapi.ObjectMeta{Name: name}})
if kerrs.IsAlreadyExists(err) {
sa, err = adminClient.Core().ServiceAccounts(namespace).Get(name)
}
if err != nil {
return nil, nil, nil, err
}
token := ""
err = wait.Poll(time.Second, 30*time.Second, func() (bool, error) {
selector := fields.OneTermEqualSelector(kapi.SecretTypeField, string(kapi.SecretTypeServiceAccountToken))
secrets, err := adminClient.Core().Secrets(namespace).List(kapi.ListOptions{FieldSelector: selector})
if err != nil {
return false, err
}
for _, secret := range secrets.Items {
if serviceaccounts.IsValidServiceAccountToken(sa, &secret) {
token = string(secret.Data[kapi.ServiceAccountTokenKey])
return true, nil
}
}
return false, nil
})
if err != nil {
return nil, nil, nil, err
}
saClientConfig := clientcmd.AnonymousClientConfig(&clientConfig)
saClientConfig.BearerToken = token
kubeClient, err := kclient.New(&saClientConfig)
if err != nil {
return nil, nil, nil, err
}
kubeClientset := adapter.FromUnversionedClient(kubeClient)
osClient, err := client.New(&saClientConfig)
if err != nil {
return nil, nil, nil, err
}
return osClient, kubeClientset, &saClientConfig, nil
}
// WaitForResourceQuotaSync watches given resource quota until its hard limit is updated to match the desired
// spec or timeout occurs.
func WaitForResourceQuotaLimitSync(
client kcoreclient.ResourceQuotaInterface,
name string,
hardLimit kapi.ResourceList,
timeout time.Duration,
) error {
startTime := time.Now()
endTime := startTime.Add(timeout)
expectedResourceNames := quota.ResourceNames(hardLimit)
list, err := client.List(kapi.ListOptions{FieldSelector: fields.Set{"metadata.name": name}.AsSelector()})
if err != nil {
return err
}
for i := range list.Items {
used := quota.Mask(list.Items[i].Status.Hard, expectedResourceNames)
if isLimitSynced(used, hardLimit) {
return nil
}
}
rv := list.ResourceVersion
w, err := client.Watch(kapi.ListOptions{FieldSelector: fields.Set{"metadata.name": name}.AsSelector(), ResourceVersion: rv})
if err != nil {
return err
}
defer w.Stop()
for time.Now().Before(endTime) {
select {
case val, ok := <-w.ResultChan():
if !ok {
// reget and re-watch
continue
}
if rq, ok := val.Object.(*kapi.ResourceQuota); ok {
used := quota.Mask(rq.Status.Hard, expectedResourceNames)
if isLimitSynced(used, hardLimit) {
return nil
}
}
case <-time.After(endTime.Sub(time.Now())):
return wait.ErrWaitTimeout
}
}
return wait.ErrWaitTimeout
}
func isLimitSynced(received, expected kapi.ResourceList) bool {
resourceNames := quota.ResourceNames(expected)
masked := quota.Mask(received, resourceNames)
if len(masked) != len(expected) {
return false
}
if le, _ := quota.LessThanOrEqual(masked, expected); !le {
return false
}
if le, _ := quota.LessThanOrEqual(expected, masked); !le {
return false
}
return true
}