istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/framework/components/echo/cmd/echogen/echogen.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 main
    16  
    17  import (
    18  	"flag"
    19  	"fmt"
    20  	"io"
    21  	"log"
    22  	"os"
    23  
    24  	"github.com/hashicorp/go-multierror"
    25  
    26  	"istio.io/istio/pkg/test/framework/components/cluster"
    27  	"istio.io/istio/pkg/test/framework/components/echo"
    28  	"istio.io/istio/pkg/test/framework/components/echo/common/ports"
    29  	"istio.io/istio/pkg/test/framework/components/echo/kube"
    30  	"istio.io/istio/pkg/test/framework/config"
    31  	"istio.io/istio/pkg/test/framework/resource"
    32  	"istio.io/istio/pkg/test/util/yml"
    33  )
    34  
    35  var (
    36  	outputPath string
    37  	dirOutput  bool
    38  )
    39  
    40  func init() {
    41  	flag.StringVar(&outputPath, "out", "", "If specified, all generated output will be written to this file.")
    42  	flag.BoolVar(&dirOutput, "dir", false, "If true, all generated output will be written to separate files per-config, in a directory named by -out.")
    43  }
    44  
    45  func main() {
    46  	if !config.Parsed() {
    47  		config.Parse()
    48  	}
    49  	if flag.NArg() < 1 {
    50  		flag.Usage()
    51  		os.Exit(1)
    52  	}
    53  	generate(flag.Args()[0], outputPath, dirOutput, os.Stdout)
    54  }
    55  
    56  func generate(input, output string, outputDir bool, outstream io.StringWriter) {
    57  	if !config.Parsed() {
    58  		// for tests
    59  		config.Parse()
    60  	}
    61  
    62  	gen := newGenerator()
    63  	if err := gen.load(input); err != nil {
    64  		log.Fatalf("failed loading %s: %v", input, err)
    65  	}
    66  	if err := gen.generate(); err != nil {
    67  		log.Fatalf("failed generating manifests: %v", err)
    68  	}
    69  
    70  	var err error
    71  	if output != "" {
    72  		err = gen.writeOutputFile(output, outputDir)
    73  	} else {
    74  		_, err = outstream.WriteString(gen.joinManifests())
    75  	}
    76  	if err != nil {
    77  		log.Fatalf("failed writing output: %v", err)
    78  	}
    79  }
    80  
    81  type generator struct {
    82  	// settings
    83  	settings *resource.Settings
    84  
    85  	// internal
    86  	configs   []echo.Config
    87  	manifests map[string]string
    88  }
    89  
    90  func newGenerator() generator {
    91  	// we read resource package settings to respsect --istio.test.versions
    92  	settings, err := resource.SettingsFromCommandLine("echogen")
    93  	if err != nil {
    94  		log.Fatalf("failed reading test framework settings: %v", err)
    95  	}
    96  
    97  	return generator{
    98  		settings: settings,
    99  	}
   100  }
   101  
   102  func (g *generator) load(input string) error {
   103  	// deserialize
   104  	bytes, err := os.ReadFile(input)
   105  	if err != nil {
   106  		return fmt.Errorf("failed reading file: %v", err)
   107  	}
   108  	g.configs, err = echo.ParseConfigs(bytes)
   109  	if err != nil {
   110  		return fmt.Errorf("failed parsing file: %v", err)
   111  	}
   112  	// fill in defaults
   113  	c := cluster.NewFake("fake", "1", "20")
   114  	for i, cfg := range g.configs {
   115  		if len(cfg.Ports) == 0 {
   116  			cfg.Ports = ports.All()
   117  		}
   118  		cfg.Cluster = c
   119  		if err := cfg.FillDefaults(nil); err != nil {
   120  			return fmt.Errorf("failed filling defaults for %s: %v", cfg.ClusterLocalFQDN(), err)
   121  		}
   122  		g.configs[i] = cfg
   123  	}
   124  	return nil
   125  }
   126  
   127  func (g *generator) generate() error {
   128  	outputByFQDN := map[string]string{}
   129  	var errs error
   130  	for _, cfg := range g.configs {
   131  		id := cfg.ClusterLocalFQDN()
   132  		// generate
   133  		svc, err := kube.GenerateService(cfg)
   134  		if err != nil {
   135  			errs = multierror.Append(errs, fmt.Errorf("failed generating service for %s: %v", id, err))
   136  			continue
   137  		}
   138  		deployment, err := kube.GenerateDeployment(nil, cfg, g.settings)
   139  		if err != nil {
   140  			errs = multierror.Append(errs, fmt.Errorf("failed generating deployment for %s: %v", id, err))
   141  			continue
   142  		}
   143  		outputByFQDN[id] = yml.JoinString(svc, deployment)
   144  		// add namespace if specified
   145  		if cfg.Namespace.Name() != "" {
   146  			var err error
   147  			outputByFQDN[id], err = yml.ApplyNamespace(outputByFQDN[id], cfg.Namespace.Name())
   148  			if err != nil {
   149  				return fmt.Errorf("error applying namespace to %s: %v", id, err)
   150  			}
   151  		}
   152  
   153  	}
   154  	g.manifests = outputByFQDN
   155  	return errs
   156  }
   157  
   158  func (g *generator) joinManifests() string {
   159  	var m []string
   160  	for _, yaml := range g.manifests {
   161  		m = append(m, yaml)
   162  	}
   163  	return yml.JoinString(m...)
   164  }
   165  
   166  func (g *generator) writeOutputFile(path string, dir bool) error {
   167  	if dir {
   168  		// multi file
   169  		if err := os.Mkdir(path, 0o644); err != nil {
   170  			return fmt.Errorf("failed creating directory %s: %v", path, err)
   171  		}
   172  		for id, yaml := range g.manifests {
   173  			fname := id + ".yaml"
   174  			if err := os.WriteFile(fname, []byte(yaml), 0o644); err != nil {
   175  				return fmt.Errorf("failed writing %s: %v", fname, err)
   176  			}
   177  		}
   178  	} else if err := os.WriteFile(path, []byte(g.joinManifests()), 0o644); err != nil {
   179  		return fmt.Errorf("failed writing %s: %v", path, err)
   180  	}
   181  	return nil
   182  }