github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/pkg/clients/kubernetes/client.go (about)

     1  package client
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"net"
     7  	"net/http"
     8  	"time"
     9  
    10  	toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
    11  	ecp "github.com/enterprise-contract/enterprise-contract-controller/api/v1alpha1"
    12  	imagecontroller "github.com/konflux-ci/image-controller/api/v1alpha1"
    13  	integrationservicev1beta1 "github.com/konflux-ci/integration-service/api/v1beta1"
    14  	pacv1alpha1 "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
    15  	ocpOauth "github.com/openshift/api/config/v1"
    16  	routev1 "github.com/openshift/api/route/v1"
    17  	userv1 "github.com/openshift/api/user/v1"
    18  	routeclientset "github.com/openshift/client-go/route/clientset/versioned"
    19  	appstudioApi "github.com/redhat-appstudio/application-api/api/v1alpha1"
    20  	buildservice "github.com/redhat-appstudio/build-service/api/v1alpha1"
    21  	"github.com/redhat-appstudio/e2e-tests/pkg/sandbox"
    22  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    23  	jvmbuildservice "github.com/redhat-appstudio/jvm-build-service/pkg/apis/jvmbuildservice/v1alpha1"
    24  	jvmbuildserviceclientset "github.com/redhat-appstudio/jvm-build-service/pkg/client/clientset/versioned"
    25  
    26  	release "github.com/redhat-appstudio/release-service/api/v1alpha1"
    27  	rs "github.com/redhat-appstudio/remote-secret/api/v1beta1"
    28  	spi "github.com/redhat-appstudio/service-provider-integration-operator/api/v1beta1"
    29  	tekton "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
    30  	pipelineclientset "github.com/tektoncd/pipeline/pkg/client/clientset/versioned"
    31  	"k8s.io/apimachinery/pkg/runtime"
    32  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    33  	"k8s.io/apimachinery/pkg/util/wait"
    34  	"k8s.io/client-go/dynamic"
    35  	"k8s.io/client-go/kubernetes"
    36  	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
    37  	"k8s.io/client-go/rest"
    38  	crclient "sigs.k8s.io/controller-runtime/pkg/client"
    39  	"sigs.k8s.io/controller-runtime/pkg/client/config"
    40  )
    41  
    42  const (
    43  	DefaultRetryInterval = time.Millisecond * 100 // make it short because a "retry interval" is waited before the first test
    44  	DefaultTimeout       = time.Second * 240
    45  )
    46  
    47  type CustomClient struct {
    48  	kubeClient            *kubernetes.Clientset
    49  	crClient              crclient.Client
    50  	pipelineClient        pipelineclientset.Interface
    51  	dynamicClient         dynamic.Interface
    52  	jvmbuildserviceClient jvmbuildserviceclientset.Interface
    53  	routeClient           routeclientset.Interface
    54  }
    55  
    56  type K8SClient struct {
    57  	AsKubeAdmin       *CustomClient
    58  	AsKubeDeveloper   *CustomClient
    59  	ProxyUrl          string
    60  	SandboxController *sandbox.SandboxController
    61  	UserName          string
    62  	UserNamespace     string
    63  	UserToken         string
    64  }
    65  
    66  var (
    67  	scheme = runtime.NewScheme()
    68  )
    69  
    70  func init() {
    71  	utilruntime.Must(clientgoscheme.AddToScheme(scheme))
    72  	utilruntime.Must(appstudioApi.AddToScheme(scheme))
    73  	utilruntime.Must(ocpOauth.AddToScheme(scheme))
    74  	utilruntime.Must(tekton.AddToScheme(scheme))
    75  	utilruntime.Must(routev1.AddToScheme(scheme))
    76  	utilruntime.Must(spi.AddToScheme(scheme))
    77  	utilruntime.Must(toolchainv1alpha1.AddToScheme(scheme))
    78  	utilruntime.Must(release.AddToScheme(scheme))
    79  	utilruntime.Must(integrationservicev1beta1.AddToScheme(scheme))
    80  	utilruntime.Must(jvmbuildservice.AddToScheme(scheme))
    81  	utilruntime.Must(ecp.AddToScheme(scheme))
    82  	utilruntime.Must(buildservice.AddToScheme(scheme))
    83  	utilruntime.Must(userv1.AddToScheme(scheme))
    84  	utilruntime.Must(rs.AddToScheme(scheme))
    85  	utilruntime.Must(imagecontroller.AddToScheme(scheme))
    86  	utilruntime.Must(pacv1alpha1.AddToScheme(scheme))
    87  }
    88  
    89  // Kube returns the clientset for Kubernetes upstream.
    90  func (c *CustomClient) KubeInterface() kubernetes.Interface {
    91  	return c.kubeClient
    92  }
    93  
    94  // Return a rest client to perform CRUD operations on Kubernetes objects
    95  func (c *CustomClient) KubeRest() crclient.Client {
    96  	return c.crClient
    97  }
    98  
    99  func (c *CustomClient) PipelineClient() pipelineclientset.Interface {
   100  	return c.pipelineClient
   101  }
   102  
   103  func (c *CustomClient) JvmbuildserviceClient() jvmbuildserviceclientset.Interface {
   104  	return c.jvmbuildserviceClient
   105  }
   106  
   107  func (c *CustomClient) RouteClient() routeclientset.Interface {
   108  	return c.routeClient
   109  }
   110  
   111  // Returns a DynamicClient interface.
   112  // Note: other client interfaces are likely preferred, except in rare cases.
   113  func (c *CustomClient) DynamicClient() dynamic.Interface {
   114  	return c.dynamicClient
   115  }
   116  
   117  // Creates Kubernetes clients:
   118  // 1. Will create a kubernetes client from default kubeconfig as kubeadmin
   119  // 2. Will create a sandbox user and will generate a client using user token a new client to create resources in RHTAP like a normal user
   120  func NewDevSandboxProxyClient(userName string, isStage bool, options utils.Options) (*K8SClient, error) {
   121  	var err error
   122  	var asAdminClient *CustomClient = nil
   123  	var sandboxController *sandbox.SandboxController
   124  	var proxyAuthInfo *sandbox.SandboxUserAuthInfo
   125  	var sandboxProxyClient *CustomClient
   126  
   127  	if isStage {
   128  		sandboxController, err := sandbox.NewDevSandboxStageController()
   129  		if err != nil {
   130  			return nil, err
   131  		}
   132  		proxyAuthInfo, err = sandboxController.ReconcileUserCreationStage(userName, options.ToolchainApiUrl, options.KeycloakUrl, options.OfflineToken)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  
   137  	} else {
   138  		asAdminClient, err = NewAdminKubernetesClient()
   139  		if err != nil {
   140  			return nil, err
   141  		}
   142  
   143  		sandboxController, err = sandbox.NewDevSandboxController(asAdminClient.KubeInterface(), asAdminClient.KubeRest())
   144  		if err != nil {
   145  			return nil, err
   146  		}
   147  
   148  		proxyAuthInfo, err = sandboxController.ReconcileUserCreation(userName)
   149  		if err != nil {
   150  			return nil, err
   151  		}
   152  	}
   153  
   154  	sandboxProxyClient, err = CreateAPIProxyClient(proxyAuthInfo.UserToken, proxyAuthInfo.ProxyUrl)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  
   159  	return &K8SClient{
   160  		AsKubeAdmin:       asAdminClient,
   161  		AsKubeDeveloper:   sandboxProxyClient,
   162  		ProxyUrl:          proxyAuthInfo.ProxyUrl,
   163  		SandboxController: sandboxController,
   164  		UserName:          proxyAuthInfo.UserName,
   165  		UserNamespace:     proxyAuthInfo.UserNamespace,
   166  		UserToken:         proxyAuthInfo.UserToken,
   167  	}, nil
   168  }
   169  
   170  // Creates a kubernetes client from default kubeconfig. Will take it from KUBECONFIG env if it is defined and if in case is not defined
   171  // will create the client from $HOME/.kube/config
   172  func NewAdminKubernetesClient() (*CustomClient, error) {
   173  	adminKubeconfig, err := config.GetConfig()
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	clientSets, err := createClientSetsFromConfig(adminKubeconfig)
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  
   182  	crClient, err := crclient.New(adminKubeconfig, crclient.Options{
   183  		Scheme: scheme,
   184  	})
   185  
   186  	if err != nil {
   187  		return nil, err
   188  	}
   189  
   190  	return &CustomClient{
   191  		kubeClient:            clientSets.kubeClient,
   192  		pipelineClient:        clientSets.pipelineClient,
   193  		dynamicClient:         clientSets.dynamicClient,
   194  		jvmbuildserviceClient: clientSets.jvmbuildserviceClient,
   195  		routeClient:           clientSets.routeClient,
   196  		crClient:              crClient,
   197  	}, nil
   198  }
   199  
   200  // CreateAPIProxyClient creates a client to the RHTAP api proxy using the given user token
   201  func CreateAPIProxyClient(usertoken, proxyURL string) (*CustomClient, error) {
   202  	var proxyCl crclient.Client
   203  	var initProxyClError error
   204  
   205  	proxyKubeConfig := &rest.Config{
   206  		Host:        proxyURL,
   207  		BearerToken: usertoken,
   208  		Transport:   noTimeoutDefaultTransport(),
   209  	}
   210  
   211  	// Getting the proxy client can fail from time to time if the proxy's informer cache has not been
   212  	// updated yet and we try to create the client to quickly so retry to reduce flakiness.
   213  	waitErr := wait.PollUntilContextTimeout(context.Background(), DefaultRetryInterval, DefaultTimeout, false, func(ctx context.Context) (done bool, err error) {
   214  		proxyCl, initProxyClError = crclient.New(proxyKubeConfig, crclient.Options{Scheme: scheme})
   215  		return initProxyClError == nil, nil
   216  	})
   217  	if waitErr != nil {
   218  		return nil, initProxyClError
   219  	}
   220  
   221  	clientSets, err := createClientSetsFromConfig(proxyKubeConfig)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	return &CustomClient{
   227  		kubeClient:            clientSets.kubeClient,
   228  		pipelineClient:        clientSets.pipelineClient,
   229  		dynamicClient:         clientSets.dynamicClient,
   230  		jvmbuildserviceClient: clientSets.jvmbuildserviceClient,
   231  		routeClient:           clientSets.routeClient,
   232  		crClient:              proxyCl,
   233  	}, nil
   234  }
   235  
   236  func noTimeoutDefaultTransport() *http.Transport {
   237  	transport := http.DefaultTransport.(interface {
   238  		Clone() *http.Transport
   239  	}).Clone()
   240  	transport.DialContext = noTimeoutDialerProxy
   241  	transport.TLSClientConfig = &tls.Config{
   242  		InsecureSkipVerify: true, // nolint:gosec
   243  	}
   244  	return transport
   245  }
   246  
   247  var noTimeoutDialerProxy = func(ctx context.Context, network, addr string) (net.Conn, error) {
   248  	dialer := &net.Dialer{
   249  		Timeout:   0,
   250  		KeepAlive: 0,
   251  	}
   252  	return dialer.DialContext(ctx, network, addr)
   253  }
   254  
   255  func createClientSetsFromConfig(cfg *rest.Config) (*CustomClient, error) {
   256  	client, err := kubernetes.NewForConfig(cfg)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  
   261  	dynamicClient, err := dynamic.NewForConfig(cfg)
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  
   266  	pipelineClient, err := pipelineclientset.NewForConfig(cfg)
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  
   271  	jvmbuildserviceClient, err := jvmbuildserviceclientset.NewForConfig(cfg)
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  
   276  	routeClient, err := routeclientset.NewForConfig(cfg)
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  
   281  	return &CustomClient{
   282  		kubeClient:            client,
   283  		pipelineClient:        pipelineClient,
   284  		dynamicClient:         dynamicClient,
   285  		jvmbuildserviceClient: jvmbuildserviceClient,
   286  		routeClient:           routeClient,
   287  	}, nil
   288  }