istio.io/istio@v0.0.0-20240520182934-d79c90f27776/operator/cmd/mesh/profile-diff.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 mesh
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"os"
    21  
    22  	"github.com/spf13/cobra"
    23  
    24  	"istio.io/istio/operator/pkg/manifest"
    25  	"istio.io/istio/operator/pkg/util"
    26  	"istio.io/istio/operator/pkg/util/clog"
    27  )
    28  
    29  type profileDiffArgs struct {
    30  	// manifestsPath is a path to a charts and profiles directory in the local filesystem with a release tgz.
    31  	manifestsPath string
    32  }
    33  
    34  func addProfileDiffFlags(cmd *cobra.Command, args *profileDiffArgs) {
    35  	cmd.PersistentFlags().StringVarP(&args.manifestsPath, "charts", "", "", ChartsDeprecatedStr)
    36  	cmd.PersistentFlags().StringVarP(&args.manifestsPath, "manifests", "d", "", ManifestsFlagHelpStr)
    37  }
    38  
    39  func profileDiffCmd(pfArgs *profileDiffArgs) *cobra.Command {
    40  	return &cobra.Command{
    41  		Use:   "diff <profile|file1.yaml> <profile|file2.yaml>",
    42  		Short: "Diffs two Istio configuration profiles",
    43  		Long:  "The diff subcommand displays the differences between two Istio configuration profiles.",
    44  		Example: `  # Profile diff by providing yaml files
    45    istioctl profile diff manifests/profiles/default.yaml manifests/profiles/demo.yaml
    46  
    47    # Profile diff by providing a profile name
    48    istioctl profile diff default demo`,
    49  		Args: func(cmd *cobra.Command, args []string) error {
    50  			if len(args) != 2 {
    51  				return fmt.Errorf("diff requires two profiles")
    52  			}
    53  			return nil
    54  		},
    55  		RunE: func(cmd *cobra.Command, args []string) error {
    56  			isdifferent, err := profileDiff(cmd, pfArgs, args)
    57  			if err != nil {
    58  				return err
    59  			}
    60  			if isdifferent {
    61  				os.Exit(1)
    62  			}
    63  			return nil
    64  		},
    65  	}
    66  }
    67  
    68  // profileDiff compare two profile files.
    69  func profileDiff(cmd *cobra.Command, pfArgs *profileDiffArgs, args []string) (bool, error) {
    70  	l := clog.NewConsoleLogger(cmd.OutOrStdout(), cmd.OutOrStderr(), nil)
    71  	setFlags := make([]string, 0)
    72  	if pfArgs.manifestsPath != "" {
    73  		setFlags = append(setFlags, fmt.Sprintf("installPackagePath=%s", pfArgs.manifestsPath))
    74  	}
    75  	return profileDiffInternal(args[0], args[1], setFlags, cmd.OutOrStdout(), l)
    76  }
    77  
    78  func profileDiffInternal(profileA, profileB string, setFlags []string, writer io.Writer, l clog.Logger) (bool, error) {
    79  	a, _, err := manifest.GenIOPFromProfile(profileA, "", setFlags, true, true, nil, l)
    80  	if err != nil {
    81  		return false, fmt.Errorf("could not read %q: %v", profileA, err)
    82  	}
    83  
    84  	b, _, err := manifest.GenIOPFromProfile(profileB, "", setFlags, true, true, nil, l)
    85  	if err != nil {
    86  		return false, fmt.Errorf("could not read %q: %v", profileB, err)
    87  	}
    88  
    89  	diff := util.YAMLDiff(a, b)
    90  	if diff == "" {
    91  		fmt.Fprintln(writer, "Profiles are identical")
    92  	} else {
    93  		fmt.Fprintf(writer, "The difference between profiles:\n%s", diff)
    94  		return true, nil
    95  	}
    96  
    97  	return false, nil
    98  }