| // Copyright Istio Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package mesh |
| |
| import ( |
| "fmt" |
| "os" |
| "path/filepath" |
| "strings" |
| ) |
| |
| import ( |
| "github.com/spf13/cobra" |
| "istio.io/pkg/log" |
| ) |
| |
| import ( |
| "github.com/apache/dubbo-go-pixiu/operator/pkg/helm" |
| "github.com/apache/dubbo-go-pixiu/operator/pkg/helmreconciler" |
| "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" |
| "github.com/apache/dubbo-go-pixiu/operator/pkg/name" |
| "github.com/apache/dubbo-go-pixiu/operator/pkg/object" |
| "github.com/apache/dubbo-go-pixiu/operator/pkg/util/clog" |
| ) |
| |
| type ManifestGenerateArgs struct { |
| // InFilenames is an array of paths to the input IstioOperator CR files. |
| InFilenames []string |
| // OutFilename is the path to the generated output directory. |
| OutFilename string |
| // Set is a string with element format "path=value" where path is an IstioOperator path and the value is a |
| // value to set the node at that path to. |
| Set []string |
| // Force proceeds even if there are validation errors |
| Force bool |
| // ManifestsPath is a path to a charts and profiles directory in the local filesystem, or URL with a release tgz. |
| ManifestsPath string |
| // Revision is the Istio control plane revision the command targets. |
| Revision string |
| // Components is a list of strings specifying which component's manifests to be generated. |
| Components []string |
| // Filter is the list of components to render |
| Filter []string |
| } |
| |
| func (a *ManifestGenerateArgs) String() string { |
| var b strings.Builder |
| b.WriteString("InFilenames: " + fmt.Sprint(a.InFilenames) + "\n") |
| b.WriteString("OutFilename: " + a.OutFilename + "\n") |
| b.WriteString("Set: " + fmt.Sprint(a.Set) + "\n") |
| b.WriteString("Force: " + fmt.Sprint(a.Force) + "\n") |
| b.WriteString("ManifestsPath: " + a.ManifestsPath + "\n") |
| b.WriteString("Revision: " + a.Revision + "\n") |
| b.WriteString("Components: " + fmt.Sprint(a.Components) + "\n") |
| return b.String() |
| } |
| |
| func addManifestGenerateFlags(cmd *cobra.Command, args *ManifestGenerateArgs) { |
| cmd.PersistentFlags().StringSliceVarP(&args.InFilenames, "filename", "f", nil, filenameFlagHelpStr) |
| cmd.PersistentFlags().StringVarP(&args.OutFilename, "output", "o", "", "Manifest output directory path.") |
| cmd.PersistentFlags().StringArrayVarP(&args.Set, "set", "s", nil, setFlagHelpStr) |
| cmd.PersistentFlags().BoolVar(&args.Force, "force", false, ForceFlagHelpStr) |
| cmd.PersistentFlags().StringVarP(&args.ManifestsPath, "charts", "", "", ChartsDeprecatedStr) |
| cmd.PersistentFlags().StringVarP(&args.ManifestsPath, "manifests", "d", "", ManifestsFlagHelpStr) |
| cmd.PersistentFlags().StringVarP(&args.Revision, "revision", "r", "", revisionFlagHelpStr) |
| cmd.PersistentFlags().StringSliceVar(&args.Components, "component", nil, ComponentFlagHelpStr) |
| cmd.PersistentFlags().StringSliceVar(&args.Filter, "filter", nil, "") |
| _ = cmd.PersistentFlags().MarkHidden("filter") |
| } |
| |
| func ManifestGenerateCmd(rootArgs *RootArgs, mgArgs *ManifestGenerateArgs, logOpts *log.Options) *cobra.Command { |
| return &cobra.Command{ |
| Use: "generate", |
| Short: "Generates an Istio install manifest", |
| Long: "The generate subcommand generates an Istio install manifest and outputs to the console by default.", |
| // nolint: lll |
| Example: ` # Generate a default Istio installation |
| istioctl manifest generate |
| |
| # Enable Tracing |
| istioctl manifest generate --set meshConfig.enableTracing=true |
| |
| # Generate the demo profile |
| istioctl manifest generate --set profile=demo |
| |
| # To override a setting that includes dots, escape them with a backslash (\). Your shell may require enclosing quotes. |
| istioctl manifest generate --set "values.sidecarInjectorWebhook.injectedAnnotations.container\.apparmor\.security\.beta\.kubernetes\.io/istio-proxy=runtime/default" |
| |
| # For setting boolean-string option, it should be enclosed quotes and escaped with a backslash (\). |
| istioctl manifest generate --set meshConfig.defaultConfig.proxyMetadata.PROXY_XDS_VIA_AGENT=\"false\" |
| `, |
| Args: func(cmd *cobra.Command, args []string) error { |
| if len(args) != 0 { |
| return fmt.Errorf("generate accepts no positional arguments, got %#v", args) |
| } |
| return nil |
| }, |
| RunE: func(cmd *cobra.Command, args []string) error { |
| l := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.ErrOrStderr(), installerScope) |
| return ManifestGenerate(rootArgs, mgArgs, logOpts, l) |
| }, |
| } |
| } |
| |
| func ManifestGenerate(args *RootArgs, mgArgs *ManifestGenerateArgs, logopts *log.Options, l clog.Logger) error { |
| if err := configLogs(logopts); err != nil { |
| return fmt.Errorf("could not configure logs: %s", err) |
| } |
| |
| manifests, _, err := manifest.GenManifests(mgArgs.InFilenames, applyFlagAliases(mgArgs.Set, mgArgs.ManifestsPath, mgArgs.Revision), |
| mgArgs.Force, mgArgs.Filter, nil, l) |
| if err != nil { |
| return err |
| } |
| |
| if len(mgArgs.Components) != 0 { |
| filteredManifests := name.ManifestMap{} |
| for _, cArg := range mgArgs.Components { |
| componentName := name.ComponentName(cArg) |
| if cManifests, ok := manifests[componentName]; ok { |
| filteredManifests[componentName] = cManifests |
| } else { |
| return fmt.Errorf("incorrect component name: %s. Valid options: %v", cArg, name.AllComponentNames) |
| } |
| } |
| manifests = filteredManifests |
| } |
| |
| if mgArgs.OutFilename == "" { |
| ordered, err := orderedManifests(manifests) |
| if err != nil { |
| return fmt.Errorf("failed to order manifests: %v", err) |
| } |
| for _, m := range ordered { |
| l.Print(m + object.YAMLSeparator) |
| } |
| } else { |
| if err := os.MkdirAll(mgArgs.OutFilename, os.ModePerm); err != nil { |
| return err |
| } |
| if err := RenderToDir(manifests, mgArgs.OutFilename, args.DryRun, l); err != nil { |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| // orderedManifests generates a list of manifests from the given map sorted by the default object order |
| // This allows |
| func orderedManifests(mm name.ManifestMap) ([]string, error) { |
| var rawOutput []string |
| var output []string |
| for _, mfs := range mm { |
| rawOutput = append(rawOutput, mfs...) |
| } |
| objects, err := object.ParseK8sObjectsFromYAMLManifest(strings.Join(rawOutput, helm.YAMLSeparator)) |
| if err != nil { |
| return nil, err |
| } |
| // For a given group of objects, sort in order to avoid missing dependencies, such as creating CRDs first |
| objects.Sort(object.DefaultObjectOrder()) |
| for _, obj := range objects { |
| yml, err := obj.YAML() |
| if err != nil { |
| return nil, err |
| } |
| output = append(output, string(yml)) |
| } |
| |
| return output, nil |
| } |
| |
| // RenderToDir writes manifests to a local filesystem directory tree. |
| func RenderToDir(manifests name.ManifestMap, outputDir string, dryRun bool, l clog.Logger) error { |
| l.LogAndPrintf("Component dependencies tree: \n%s", helmreconciler.InstallTreeString()) |
| l.LogAndPrintf("Rendering manifests to output dir %s", outputDir) |
| return renderRecursive(manifests, helmreconciler.InstallTree, outputDir, dryRun, l) |
| } |
| |
| func renderRecursive(manifests name.ManifestMap, installTree helmreconciler.ComponentTree, outputDir string, dryRun bool, l clog.Logger) error { |
| for k, v := range installTree { |
| componentName := string(k) |
| // In cases (like gateways) where multiple instances can exist, concatenate the manifests and apply as one. |
| ym := strings.Join(manifests[k], helm.YAMLSeparator) |
| l.LogAndPrintf("Rendering: %s", componentName) |
| dirName := filepath.Join(outputDir, componentName) |
| if !dryRun { |
| if err := os.MkdirAll(dirName, os.ModePerm); err != nil { |
| return fmt.Errorf("could not create directory %s; %s", outputDir, err) |
| } |
| } |
| fname := filepath.Join(dirName, componentName) + ".yaml" |
| l.LogAndPrintf("Writing manifest to %s", fname) |
| if !dryRun { |
| if err := os.WriteFile(fname, []byte(ym), 0o644); err != nil { |
| return fmt.Errorf("could not write manifest config; %s", err) |
| } |
| } |
| |
| kt, ok := v.(helmreconciler.ComponentTree) |
| if !ok { |
| // Leaf |
| return nil |
| } |
| if err := renderRecursive(manifests, kt, dirName, dryRun, l); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |