istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/bootstrap/instance.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 bootstrap 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io" 21 "os" 22 "path" 23 "strings" 24 "text/template" 25 26 "github.com/Masterminds/sprig/v3" 27 28 "istio.io/istio/pkg/env" 29 "istio.io/istio/pkg/log" 30 "istio.io/istio/pkg/model" 31 ) 32 33 const ( 34 // EnvoyFileTemplate is a template for the root config JSON 35 EnvoyFileTemplate = "envoy-rev.%s" 36 DefaultCfgDir = "./var/lib/istio/envoy/envoy_bootstrap_tmpl.json" 37 ) 38 39 // TODO(nmittler): Move this to application code. This shouldn't be declared in a library. 40 var overrideVar = env.Register("ISTIO_BOOTSTRAP", "", "") 41 42 // Instance of a configured Envoy bootstrap writer. 43 type Instance interface { 44 // WriteTo writes the content of the Envoy bootstrap to the given writer. 45 WriteTo(templateFile string, w io.Writer) error 46 47 // CreateFile generates an Envoy bootstrap file. 48 CreateFile() (string, error) 49 } 50 51 // New creates a new Instance of an Envoy bootstrap writer. 52 func New(cfg Config) Instance { 53 return &instance{ 54 Config: cfg, 55 } 56 } 57 58 type instance struct { 59 Config 60 } 61 62 func (i *instance) WriteTo(templateFile string, w io.Writer) error { 63 // Get the input bootstrap template. 64 t, err := newTemplate(templateFile) 65 if err != nil { 66 return err 67 } 68 69 // Create the parameters for the template. 70 templateParams, err := i.toTemplateParams() 71 if err != nil { 72 return err 73 } 74 75 // Execute the template. 76 return t.Execute(w, templateParams) 77 } 78 79 func toJSON(i any) string { 80 if i == nil { 81 return "{}" 82 } 83 84 ba, err := json.Marshal(i) 85 if err != nil { 86 log.Warnf("Unable to marshal %v: %v", i, err) 87 return "{}" 88 } 89 90 return string(ba) 91 } 92 93 // GetEffectiveTemplatePath gets the template file that should be used for bootstrap 94 func GetEffectiveTemplatePath(pc *model.NodeMetaProxyConfig) string { 95 var templateFilePath string 96 switch { 97 case pc.CustomConfigFile != "": 98 templateFilePath = pc.CustomConfigFile 99 case pc.ProxyBootstrapTemplatePath != "": 100 templateFilePath = pc.ProxyBootstrapTemplatePath 101 default: 102 templateFilePath = DefaultCfgDir 103 } 104 override := overrideVar.Get() 105 if len(override) > 0 { 106 templateFilePath = override 107 } 108 return templateFilePath 109 } 110 111 func (i *instance) CreateFile() (string, error) { 112 // Create the output file. 113 if err := os.MkdirAll(i.Metadata.ProxyConfig.ConfigPath, 0o700); err != nil { 114 return "", err 115 } 116 117 templateFile := GetEffectiveTemplatePath(i.Metadata.ProxyConfig) 118 119 outputFilePath := configFile(i.Metadata.ProxyConfig.ConfigPath, templateFile) 120 outputFile, err := os.Create(outputFilePath) 121 if err != nil { 122 return "", err 123 } 124 defer func() { _ = outputFile.Close() }() 125 126 // Write the content of the file. 127 if err := i.WriteTo(templateFile, outputFile); err != nil { 128 return "", err 129 } 130 131 return outputFilePath, err 132 } 133 134 func configFile(config string, templateFile string) string { 135 suffix := "json" 136 // Envoy will interpret the file extension to determine the type. We should detect yaml inputs 137 if strings.HasSuffix(templateFile, ".yaml.tmpl") || strings.HasSuffix(templateFile, ".yaml") { 138 suffix = "yaml" 139 } 140 return path.Join(config, fmt.Sprintf(EnvoyFileTemplate, suffix)) 141 } 142 143 func newTemplate(templateFilePath string) (*template.Template, error) { 144 cfgTmpl, err := os.ReadFile(templateFilePath) 145 if err != nil { 146 return nil, err 147 } 148 149 funcMap := template.FuncMap{ 150 "toJSON": toJSON, 151 } 152 return template.New("bootstrap").Funcs(funcMap).Funcs(sprig.GenericFuncMap()).Parse(string(cfgTmpl)) 153 }