github.com/solo-io/service-mesh-hub@v0.9.2/test/e2e/env.go (about) 1 package e2e 2 3 import ( 4 "context" 5 "os" 6 "os/exec" 7 "path/filepath" 8 "strconv" 9 "strings" 10 "sync" 11 "time" 12 13 "github.com/solo-io/skv2/codegen/util" 14 15 "k8s.io/client-go/rest" 16 17 "github.com/solo-io/service-mesh-hub/test/kubectl" 18 19 . "github.com/onsi/ginkgo" 20 . "github.com/onsi/gomega" 21 istionetworkingv1alpha3 "github.com/solo-io/external-apis/pkg/api/istio/networking.istio.io/v1alpha3" 22 kubernetes_core "github.com/solo-io/external-apis/pkg/api/k8s/core/v1" 23 discoveryv1alpha2 "github.com/solo-io/service-mesh-hub/pkg/api/discovery.smh.solo.io/v1alpha2" 24 networkingv1alpha2 "github.com/solo-io/service-mesh-hub/pkg/api/networking.smh.solo.io/v1alpha2" 25 corev1 "k8s.io/api/core/v1" 26 v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/client-go/kubernetes" 28 "k8s.io/client-go/tools/clientcmd" 29 ) 30 31 const ( 32 mgmtContext = "kind-mgmt-cluster" 33 remoteContext = "kind-remote-cluster" 34 ) 35 36 type Env struct { 37 Management KubeContext 38 Remote KubeContext 39 } 40 41 func (e Env) DumpState() { 42 dumpState() 43 } 44 45 func newEnv(mgmt, remote string) Env { 46 return Env{ 47 Management: NewKubeContext(mgmt), 48 Remote: NewKubeContext(remote), 49 } 50 } 51 52 type SingleClusterEnv struct { 53 Management KubeContext 54 } 55 56 func (s SingleClusterEnv) DumpState() { 57 dumpState() 58 } 59 60 func newSingleClusterEnv(mgmt string) SingleClusterEnv { 61 return SingleClusterEnv{ 62 Management: NewKubeContext(mgmt), 63 } 64 } 65 66 type KubeContext struct { 67 Context string 68 Config *rest.Config 69 Clientset *kubernetes.Clientset 70 TrafficPolicyClient networkingv1alpha2.TrafficPolicyClient 71 MeshClient discoveryv1alpha2.MeshClient 72 SecretClient kubernetes_core.SecretClient 73 VirtualMeshClient networkingv1alpha2.VirtualMeshClient 74 DestinationRuleClient istionetworkingv1alpha3.DestinationRuleClient 75 VirtualServiceClient istionetworkingv1alpha3.VirtualServiceClient 76 } 77 78 // If kubecontext is empty string, use current context. 79 func NewKubeContext(kubecontext string) KubeContext { 80 cfg, err := clientcmd.NewDefaultClientConfigLoadingRules().Load() 81 Expect(err).NotTo(HaveOccurred()) 82 83 config := clientcmd.NewNonInteractiveClientConfig(*cfg, kubecontext, &clientcmd.ConfigOverrides{}, nil) 84 restcfg, err := config.ClientConfig() 85 Expect(err).NotTo(HaveOccurred()) 86 87 clientset, err := kubernetes.NewForConfig(restcfg) 88 Expect(err).NotTo(HaveOccurred()) 89 90 kubeCoreClientset, err := kubernetes_core.NewClientsetFromConfig(restcfg) 91 Expect(err).NotTo(HaveOccurred()) 92 93 networkingClientset, err := networkingv1alpha2.NewClientsetFromConfig(restcfg) 94 Expect(err).NotTo(HaveOccurred()) 95 96 discoveryClientset, err := discoveryv1alpha2.NewClientsetFromConfig(restcfg) 97 Expect(err).NotTo(HaveOccurred()) 98 99 istioNetworkingClientset, err := istionetworkingv1alpha3.NewClientsetFromConfig(restcfg) 100 Expect(err).NotTo(HaveOccurred()) 101 102 return KubeContext{ 103 Context: kubecontext, 104 Config: restcfg, 105 Clientset: clientset, 106 TrafficPolicyClient: networkingClientset.TrafficPolicies(), 107 VirtualMeshClient: networkingClientset.VirtualMeshes(), 108 MeshClient: discoveryClientset.Meshes(), 109 SecretClient: kubeCoreClientset.Secrets(), 110 DestinationRuleClient: istioNetworkingClientset.DestinationRules(), 111 VirtualServiceClient: istioNetworkingClientset.VirtualServices(), 112 } 113 } 114 115 func (k *KubeContext) Curl(ctx context.Context, ns, fromDeployment, fromContainer, url string) string { 116 return kubectl.Curl(ctx, k.Context, ns, fromDeployment, fromContainer, url) 117 } 118 119 func (k *KubeContext) WaitForRollout(ctx context.Context, ns, deployment string) { 120 kubectl.WaitForRollout(ctx, k.Context, ns, deployment) 121 } 122 123 func (k *KubeContext) DeployBookInfo(ctx context.Context, ns string) { 124 kubectl.DeployBookInfo(ctx, k.Context, ns) 125 } 126 127 func (k *KubeContext) CreateNamespace(ctx context.Context, ns string) { 128 kubectl.CreateNamespace(ctx, k.Context, ns) 129 } 130 131 func (k *KubeContext) DeleteNamespace(ctx context.Context, ns string) { 132 kubectl.DeleteNamespace(ctx, k.Context, ns) 133 } 134 135 func (k *KubeContext) LabelNamespace(ctx context.Context, ns, label string) { 136 kubectl.LabelNamespace(ctx, k.Context, ns, label) 137 } 138 139 func (k *KubeContext) SetDeploymentEnvVars( 140 ctx context.Context, 141 ns string, 142 deploymentName string, 143 containerName string, 144 envVars map[string]string) { 145 kubectl.SetDeploymentEnvVars(ctx, k.Context, ns, deploymentName, containerName, envVars) 146 } 147 148 // Modify the deployment's container entrypoint command to "sleep 20h" to disable the application. 149 func (k *KubeContext) DisableContainer( 150 ctx context.Context, 151 ns string, 152 deploymentName string, 153 containerName string, 154 ) { 155 kubectl.DisableContainer(ctx, k.Context, ns, deploymentName, containerName) 156 } 157 158 // Remove the sleep command to re-enable the application container. 159 func (k *KubeContext) EnableContainer( 160 ctx context.Context, 161 ns string, 162 deploymentName string, 163 ) { 164 kubectl.EnableContainer(ctx, k.Context, ns, deploymentName) 165 } 166 167 type Pod struct { 168 corev1.Pod 169 Cluster *KubeContext 170 } 171 172 func (p *Pod) Curl(ctx context.Context, args ...string) string { 173 return kubectl.CurlWithEphemeralPod(ctx, p.Cluster.Context, p.Namespace, p.Name, args...) 174 } 175 176 func (k *KubeContext) GetPod(ctx context.Context, ns, app string) *Pod { 177 pl, err := k.Clientset.CoreV1().Pods(ns).List(ctx, v1.ListOptions{LabelSelector: "app=" + app}) 178 Expect(err).NotTo(HaveOccurred()) 179 Expect(pl.Items).NotTo(BeEmpty()) 180 181 return &Pod{ 182 Pod: pl.Items[0], 183 Cluster: k, 184 } 185 } 186 187 var env Env 188 var envOnce sync.Once 189 190 func StartEnvOnce(ctx context.Context) Env { 191 envOnce.Do(func() { 192 env = StartEnv(ctx) 193 }) 194 195 return env 196 } 197 198 func GetEnv() Env { 199 return env 200 } 201 202 func ClearEnv(ctx context.Context) error { 203 if useExisting := os.Getenv("USE_EXISTING"); useExisting == "1" { 204 // dont clear existing env 205 return nil 206 } 207 cmd := exec.CommandContext(ctx, "./ci/setup-kind.sh", "cleanup", strconv.Itoa(GinkgoParallelNode())) 208 cmd.Stdout = GinkgoWriter 209 cmd.Stderr = GinkgoWriter 210 return cmd.Run() 211 } 212 213 func StartEnv(ctx context.Context) Env { 214 215 if useExisting := os.Getenv("USE_EXISTING"); useExisting == "1" { 216 mgmt := "kind-mgmt-cluster" 217 remote := "kind-remote-cluster" 218 if fields := strings.Split(useExisting, ","); len(fields) == 2 { 219 mgmt = fields[0] 220 remote = fields[1] 221 } 222 return newEnv(mgmt, remote) 223 } 224 225 // change to repo root dir 226 err := os.Chdir(filepath.Join(util.MustGetThisDir(), "..", "..")) 227 Expect(err).NotTo(HaveOccurred()) 228 229 // get absolute path to setup script so this function can be called from any working directory) 230 cmd := exec.CommandContext(ctx, "./ci/setup-kind.sh", strconv.Itoa(GinkgoParallelNode())) 231 cmd.Stdout = GinkgoWriter 232 cmd.Stderr = GinkgoWriter 233 234 err = cmd.Run() 235 if err != nil { 236 dumpState() 237 } 238 Expect(err).NotTo(HaveOccurred()) 239 240 return newEnv(mgmtContext, remoteContext) 241 } 242 243 var ( 244 singleClusterEnv SingleClusterEnv 245 singleClusterEnvOnce sync.Once 246 ) 247 248 func StartSingleClusterEnvOnce(ctx context.Context) SingleClusterEnv { 249 singleClusterEnvOnce.Do(func() { 250 singleClusterEnv = StartSingleClusterEnv(ctx) 251 }) 252 253 return singleClusterEnv 254 } 255 256 func GetSingleClusterEnv() SingleClusterEnv { 257 return singleClusterEnv 258 } 259 260 func ClearSingleClusterEnv(ctx context.Context) error { 261 if useExisting := os.Getenv("USE_EXISTING"); useExisting == "1" { 262 // dont clear existing env 263 return nil 264 } 265 cmd := exec.CommandContext(ctx, "./ci/setup-kind.sh", "cleanup", strconv.Itoa(GinkgoParallelNode())) 266 cmd.Stdout = GinkgoWriter 267 cmd.Stderr = GinkgoWriter 268 return cmd.Run() 269 } 270 271 func StartSingleClusterEnv(ctx context.Context) SingleClusterEnv { 272 273 if useExisting := os.Getenv("USE_EXISTING"); useExisting == "1" { 274 return newSingleClusterEnv(mgmtContext) 275 } 276 277 // TODO: don't hardcode osm installation here 278 cmd := exec.CommandContext(ctx, "./ci/setup-kind.sh", "osm") 279 cmd.Stdout = GinkgoWriter 280 cmd.Stderr = GinkgoWriter 281 282 err := cmd.Run() 283 if err != nil { 284 dumpState() 285 } 286 Expect(err).NotTo(HaveOccurred()) 287 288 return newSingleClusterEnv(mgmtContext) 289 } 290 291 func dumpState() { 292 timeoutCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 293 defer cancel() 294 dbgCmd := exec.CommandContext(timeoutCtx, "./ci/print-kind-info.sh", strconv.Itoa(GinkgoParallelNode())) 295 dbgCmd.Dir = "../.." 296 dbgCmd.Stdout = GinkgoWriter 297 dbgCmd.Stderr = GinkgoWriter 298 _ = dbgCmd.Run() 299 }