istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/framework/components/istio/installer.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 istio 16 17 import ( 18 "bytes" 19 "fmt" 20 "io" 21 "os" 22 "path" 23 "path/filepath" 24 "strconv" 25 "sync" 26 27 "k8s.io/client-go/rest" 28 29 "istio.io/istio/operator/cmd/mesh" 30 "istio.io/istio/operator/pkg/util/clog" 31 "istio.io/istio/pkg/kube" 32 testenv "istio.io/istio/pkg/test/env" 33 "istio.io/istio/pkg/test/framework/components/cluster" 34 "istio.io/istio/pkg/test/framework/resource" 35 "istio.io/istio/pkg/test/scopes" 36 ) 37 38 var _ resource.Dumper = &installer{} 39 40 type installArgs struct { 41 ComponentName string 42 Revision string 43 Files []string 44 Set []string 45 } 46 47 func (args *installArgs) AppendSet(key, value string) { 48 args.Set = append(args.Set, fmt.Sprintf("%s=%s", key, value)) 49 } 50 51 type installer struct { 52 ctx resource.Context 53 workDir string 54 manifests map[string][]string 55 mu sync.Mutex 56 } 57 58 func newInstaller(ctx resource.Context, workDir string) *installer { 59 return &installer{ 60 ctx: ctx, 61 workDir: workDir, 62 manifests: make(map[string][]string), 63 } 64 } 65 66 func (i *installer) Install(c cluster.Cluster, args installArgs) error { 67 kubeConfigFile, err := kubeConfigFileForCluster(c) 68 if err != nil { 69 return err 70 } 71 72 iArgs := &mesh.InstallArgs{ 73 InFilenames: args.Files, 74 Set: args.Set, 75 ManifestsPath: filepath.Join(testenv.IstioSrc, "manifests"), 76 Revision: args.Revision, 77 } 78 if i.ctx.Settings().Ambient { 79 iArgs.InFilenames = append(iArgs.InFilenames, filepath.Join(testenv.IstioSrc, IntegrationTestAmbientDefaultsIOP)) 80 } 81 if i.ctx.Settings().PeerMetadataDiscovery && len(args.ComponentName) == 0 { 82 iArgs.InFilenames = append(iArgs.InFilenames, filepath.Join(testenv.IstioSrc, IntegrationTestPeerMetadataDiscoveryDefaultsIOP)) 83 } 84 85 rc, err := kube.DefaultRestConfig(kubeConfigFile, "", func(config *rest.Config) { 86 config.QPS = 50 87 config.Burst = 100 88 }) 89 if err != nil { 90 return err 91 } 92 kubeClient, err := kube.NewCLIClient(kube.NewClientConfigForRestConfig(rc)) 93 if err != nil { 94 return fmt.Errorf("create Kubernetes client: %v", err) 95 } 96 97 // Generate the manifest YAML, so that we can uninstall it in Close. 98 var stdOut, stdErr bytes.Buffer 99 if err := mesh.ManifestGenerate(kubeClient, &mesh.RootArgs{}, &mesh.ManifestGenerateArgs{ 100 InFilenames: iArgs.InFilenames, 101 Set: iArgs.Set, 102 Force: iArgs.Force, 103 ManifestsPath: iArgs.ManifestsPath, 104 Revision: iArgs.Revision, 105 }, cmdLogger(&stdOut, &stdErr)); err != nil { 106 return err 107 } 108 yaml := stdOut.String() 109 110 // Store the generated manifest. 111 i.mu.Lock() 112 i.manifests[c.Name()] = append(i.manifests[c.Name()], yaml) 113 i.mu.Unlock() 114 115 // Actually run the install command 116 iArgs.SkipConfirmation = true 117 118 componentName := args.ComponentName 119 if len(componentName) == 0 { 120 componentName = "Istio components" 121 } 122 123 scopes.Framework.Infof("Installing %s on cluster %s: %s", componentName, c.Name(), iArgs) 124 stdOut.Reset() 125 stdErr.Reset() 126 if err := mesh.Install(kubeClient, &mesh.RootArgs{}, iArgs, &stdOut, 127 cmdLogger(&stdOut, &stdErr), 128 mesh.NewPrinterForWriter(&stdOut)); err != nil { 129 return fmt.Errorf("failed installing %s on cluster %s: %v. Details: %s", componentName, c.Name(), err, &stdErr) 130 } 131 return nil 132 } 133 134 func (i *installer) Close(c cluster.Cluster) error { 135 i.mu.Lock() 136 manifests := i.manifests[c.Name()] 137 delete(i.manifests, c.Name()) 138 i.mu.Unlock() 139 140 if len(manifests) > 0 { 141 return i.ctx.ConfigKube(c).YAML("", removeCRDsSlice(manifests)).Delete() 142 } 143 return nil 144 } 145 146 func (i *installer) Dump(resource.Context) { 147 manifestsDir := path.Join(i.workDir, "manifests") 148 if err := os.Mkdir(manifestsDir, 0o700); err != nil { 149 scopes.Framework.Errorf("Unable to create directory for dumping install manifests: %v", err) 150 } 151 for clusterName, manifests := range i.manifests { 152 clusterDir := path.Join(manifestsDir, clusterName) 153 if err := os.Mkdir(manifestsDir, 0o700); err != nil { 154 scopes.Framework.Errorf("Unable to create directory for dumping %s install manifests: %v", clusterName, err) 155 } 156 for i, manifest := range manifests { 157 err := os.WriteFile(path.Join(clusterDir, "manifest-"+strconv.Itoa(i)+".yaml"), []byte(manifest), 0o644) 158 if err != nil { 159 scopes.Framework.Errorf("Failed writing manifest %d/%d in %s: %v", i, len(manifests)-1, clusterName, err) 160 } 161 } 162 } 163 } 164 165 func cmdLogger(stdOut, stdErr io.Writer) clog.Logger { 166 return clog.NewConsoleLogger(stdOut, stdErr, scopes.Framework) 167 }