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  }