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 }