blob: 6d7ae55d3437b522410c69ba1238723f93a59dba [file] [log] [blame]
// 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
}