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 }