github.com/redhat-appstudio/e2e-tests@v0.0.0-20230619105049-9a422b2094d7/pkg/apis/vcluster/vcluster.go (about)

     1  package vcluster
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"time"
     9  
    10  	routev1 "github.com/openshift/api/route/v1"
    11  	client "github.com/redhat-appstudio/e2e-tests/pkg/apis/kubernetes"
    12  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    13  	"gopkg.in/yaml.v2"
    14  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  	"k8s.io/apimachinery/pkg/util/intstr"
    16  	"k8s.io/apimachinery/pkg/util/wait"
    17  	"k8s.io/client-go/tools/clientcmd"
    18  )
    19  
    20  const (
    21  	VCLUSTER_BIN = "vcluster"
    22  )
    23  
    24  type vclusterFactory struct {
    25  	TargetDir  string
    26  	KubeClient *client.CustomClient
    27  }
    28  
    29  type Vcluster interface {
    30  	InitializeVCluster(clusterName string, targetNamespace string, host string) (kubeconfigPath string, err error)
    31  }
    32  
    33  func NewVclusterController(dir string, kube *client.CustomClient) Vcluster {
    34  	return &vclusterFactory{
    35  		TargetDir:  dir,
    36  		KubeClient: kube,
    37  	}
    38  }
    39  
    40  func (c *vclusterFactory) InitializeVCluster(clusterName string, targetNamespace string, host string) (kubeconfigPath string, err error) {
    41  	var valuesFilename = fmt.Sprintf("%s/%s-values.yaml", c.TargetDir, clusterName)
    42  	var createVclusterArgs = []string{"create", clusterName, "--namespace", targetNamespace, "--connect=false", "--expose", "-f", valuesFilename}
    43  	kubeconfigPath = fmt.Sprintf("%s/%s-kubeconfig", c.TargetDir, clusterName)
    44  
    45  	if err := c.WriteToFile(c.GenerateHelmValues(host), valuesFilename); err != nil {
    46  		return "", err
    47  	}
    48  
    49  	if err := utils.ExecuteCommandInASpecificDirectory(VCLUSTER_BIN, createVclusterArgs, ""); err != nil {
    50  		return "", err
    51  	}
    52  
    53  	if err := c.CreateKubeconfig(clusterName, targetNamespace, kubeconfigPath); err != nil {
    54  		return "", err
    55  	}
    56  
    57  	config, err := clientcmd.LoadFromFile(kubeconfigPath)
    58  	if err != nil {
    59  		return "", fmt.Errorf("failed to load kubeconfig: %s", err)
    60  	}
    61  
    62  	route, err := c.CreateRoute(clusterName, targetNamespace)
    63  	if err != nil {
    64  		return "", fmt.Errorf("failed to create route for vcluster: %s", err)
    65  	}
    66  
    67  	config.Clusters[config.CurrentContext].Server = fmt.Sprintf("https://%s", string(route.Spec.Host))
    68  
    69  	err = clientcmd.WriteToFile(*config, kubeconfigPath)
    70  	if err != nil {
    71  		return "", fmt.Errorf("failed to generate kubeconfig with openshift route: %s", err)
    72  	}
    73  
    74  	return kubeconfigPath, nil
    75  }
    76  
    77  func (vc *vclusterFactory) CreateRoute(serviceName string, namespace string) (route *routev1.Route, err error) {
    78  	routeSpec := routev1.Route{
    79  		ObjectMeta: v1.ObjectMeta{
    80  			Name:      serviceName,
    81  			Namespace: namespace,
    82  		},
    83  		Spec: routev1.RouteSpec{
    84  			To: routev1.RouteTargetReference{
    85  				Kind: "Service",
    86  				Name: serviceName,
    87  			},
    88  			Port: &routev1.RoutePort{
    89  				TargetPort: intstr.FromString("https"),
    90  			},
    91  			TLS: &routev1.TLSConfig{
    92  				Termination:                   routev1.TLSTerminationPassthrough,
    93  				InsecureEdgeTerminationPolicy: routev1.InsecureEdgeTerminationPolicyNone,
    94  			},
    95  			WildcardPolicy: routev1.WildcardPolicyNone,
    96  		},
    97  	}
    98  	if err := vc.KubeClient.KubeRest().Create(context.Background(), &routeSpec); err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	return route, wait.Poll(5*time.Minute, 10*time.Second, func() (done bool, err error) {
   103  		route, err = vc.KubeClient.RouteClient().RouteV1().Routes(namespace).Get(context.TODO(), routeSpec.Name, v1.GetOptions{})
   104  		if err != nil {
   105  			return false, nil
   106  		}
   107  
   108  		for _, condition := range route.Status.Ingress[0].Conditions {
   109  			if condition.Type == routev1.RouteAdmitted && condition.Status == "True" {
   110  				return true, nil
   111  			}
   112  		}
   113  
   114  		return false, nil
   115  	})
   116  }
   117  
   118  func (c *vclusterFactory) CreateKubeconfig(clusterName string, targetNamespace string, kubeconfigPath string) error {
   119  	return utils.ExecuteCommandInASpecificDirectory(VCLUSTER_BIN, []string{"connect", clusterName, "--namespace", targetNamespace, "--update-current=false", "--service-account=kube-system/admin",
   120  		"--token-expiration=10800", "--cluster-role=cluster-admin", "--insecure", "--kube-config", kubeconfigPath}, "")
   121  }
   122  
   123  func (c *vclusterFactory) GenerateHelmValues(host string) ValuesTemplate {
   124  	return ValuesTemplate{
   125  		Openshift: Openshift{
   126  			Enable: true,
   127  		},
   128  		Sync: Sync{
   129  			NetworkPolicies: NetworkPolicies{
   130  				Enabled: true,
   131  			},
   132  			Services: Services{
   133  				SyncServiceSelector: true,
   134  			},
   135  			Ingresses: Ingresses{
   136  				Enabled:    true,
   137  				PathType:   "Prefix",
   138  				ApiVersion: "networking.k8s.io/v1",
   139  			},
   140  			Secrets: Secrets{
   141  				Enabled: true,
   142  				All:     true,
   143  			},
   144  		},
   145  	}
   146  }
   147  
   148  // WriteToFile serializes the config to yaml and writes it out to a file.  If not present, it creates the file with the mode 0600.  If it is present
   149  // it stomps the contents
   150  func (c *vclusterFactory) WriteToFile(config ValuesTemplate, filename string) error {
   151  	content, err := yaml.Marshal(config)
   152  	if err != nil {
   153  		return err
   154  	}
   155  	dir := filepath.Dir(filename)
   156  	if _, err := os.Stat(dir); os.IsNotExist(err) {
   157  		if err = os.MkdirAll(dir, 0755); err != nil {
   158  			return err
   159  		}
   160  	}
   161  
   162  	if err := os.WriteFile(filename, content, 0600); err != nil {
   163  		return err
   164  	}
   165  	return nil
   166  }