istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/framework/components/istioctl/kube.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package istioctl 16 17 import ( 18 "bytes" 19 "fmt" 20 "strings" 21 "sync" 22 "time" 23 24 "istio.io/istio/istioctl/cmd" 25 "istio.io/istio/pilot/pkg/config/kube/crd" 26 "istio.io/istio/pkg/log" 27 "istio.io/istio/pkg/test" 28 "istio.io/istio/pkg/test/framework/resource" 29 "istio.io/istio/pkg/test/scopes" 30 ) 31 32 // We cannot invoke the istioctl library concurrently due to the number of global variables 33 // https://github.com/istio/istio/issues/37324 34 var invokeMutex sync.Mutex 35 36 type kubeComponent struct { 37 config Config 38 kubeconfig string 39 } 40 41 // Filenamer is an interface to avoid importing kubecluster package, instead build our own interface 42 // to extract kube context 43 type Filenamer interface { 44 Filename() string 45 } 46 47 func newKube(ctx resource.Context, config Config) (Instance, error) { 48 fn, ok := ctx.Clusters().GetOrDefault(config.Cluster).(Filenamer) 49 if !ok { 50 return nil, fmt.Errorf("cluster does not support fetching kube config") 51 } 52 n := &kubeComponent{ 53 config: config, 54 kubeconfig: fn.Filename(), 55 } 56 57 return n, nil 58 } 59 60 // Invoke implements WaitForConfigs 61 func (c *kubeComponent) WaitForConfig(defaultNamespace string, configs string) error { 62 cfgs, _, err := crd.ParseInputs(configs) 63 if err != nil { 64 return fmt.Errorf("failed to parse input: %v", err) 65 } 66 for _, cfg := range cfgs { 67 ns := cfg.Namespace 68 if ns == "" { 69 ns = defaultNamespace 70 } 71 // TODO(https://github.com/istio/istio/issues/37148) increase timeout. Right now it fails often, so 72 // set it to low timeout to reduce impact 73 if out, stderr, err := c.Invoke([]string{"x", "wait", "-v", "--timeout=5s", cfg.GroupVersionKind.Kind, cfg.Name + "." + ns}); err != nil { 74 return fmt.Errorf("wait: %v\nout: %v\nerr: %v", err, out, stderr) 75 } 76 } 77 return nil 78 } 79 80 // Invoke implements Instance 81 func (c *kubeComponent) Invoke(args []string) (string, string, error) { 82 cmdArgs := append([]string{ 83 "--kubeconfig", 84 c.kubeconfig, 85 }, args...) 86 87 var out bytes.Buffer 88 var err bytes.Buffer 89 90 start := time.Now() 91 92 invokeMutex.Lock() 93 rootCmd := cmd.GetRootCmd(cmdArgs) 94 rootCmd.SetOut(&out) 95 rootCmd.SetErr(&err) 96 // istioctl will overwrite logs which we don't want. 97 // It happens to do this via PersistentPreRunE, which we can disable. 98 // We add an additional check in case someone refactors this away, to ensure we don't wipe out non-logging code. 99 if fmt.Sprintf("%p", rootCmd.PersistentPreRunE) != fmt.Sprintf("%p", cmd.ConfigureLogging) { 100 log.Fatalf("istioctl PersistentPreRunE is not configuring logging") 101 } 102 rootCmd.PersistentPreRunE = nil 103 fErr := rootCmd.Execute() 104 invokeMutex.Unlock() 105 106 scopes.Framework.Infof("istioctl (%v): completed after %.4fs", args, time.Since(start).Seconds()) 107 108 if err.String() != "" { 109 scopes.Framework.Infof("istioctl error: %v", strings.TrimSpace(err.String())) 110 } 111 return out.String(), err.String(), fErr 112 } 113 114 // InvokeOrFail implements Instance 115 func (c *kubeComponent) InvokeOrFail(t test.Failer, args []string) (string, string) { 116 output, stderr, err := c.Invoke(args) 117 if err != nil { 118 t.Logf("Unwanted exception for 'istioctl %s': %v", strings.Join(args, " "), err) 119 t.Logf("Output:\n%v", output) 120 t.Logf("Error:\n%v", stderr) 121 t.FailNow() 122 } 123 return output, stderr 124 }