github.com/splunk/dan1-qbec@v0.7.3/internal/commands/component.go (about)

     1  /*
     2     Copyright 2019 Splunk Inc.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package commands
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io"
    24  
    25  	"github.com/ghodss/yaml"
    26  	"github.com/spf13/cobra"
    27  	"github.com/splunk/qbec/internal/diff"
    28  	"github.com/splunk/qbec/internal/model"
    29  )
    30  
    31  func newComponentCommand(cp ConfigProvider) *cobra.Command {
    32  	cmd := &cobra.Command{
    33  		Use:   "component <subcommand>",
    34  		Short: "component lists and diffs",
    35  	}
    36  	cmd.AddCommand(newComponentListCommand(cp), newComponentDiffCommand(cp))
    37  	return cmd
    38  }
    39  
    40  func listComponents(components []model.Component, formatSpecified bool, format string, w io.Writer) error {
    41  	if !formatSpecified {
    42  		fmt.Fprintf(w, "%-30s %s\n", "COMPONENT", "FILE")
    43  		for _, c := range components {
    44  			fmt.Fprintf(w, "%-30s %s\n", c.Name, c.File)
    45  		}
    46  		return nil
    47  	}
    48  	switch format {
    49  	case "yaml":
    50  		b, err := yaml.Marshal(components)
    51  		if err != nil {
    52  			return err
    53  		}
    54  		fmt.Fprintln(w, "---")
    55  		fmt.Fprintf(w, "%s\n", b)
    56  		return nil
    57  	case "json":
    58  		encoder := json.NewEncoder(w)
    59  		encoder.SetIndent("", "  ")
    60  		return encoder.Encode(components)
    61  	default:
    62  		return newUsageError(fmt.Sprintf("listComponents: unsupported format %q", format))
    63  	}
    64  }
    65  
    66  type componentListCommandConfig struct {
    67  	*Config
    68  	format  string
    69  	objects bool
    70  }
    71  
    72  func doComponentList(args []string, config componentListCommandConfig) error {
    73  	if len(args) != 1 {
    74  		return newUsageError("exactly one environment required")
    75  	}
    76  	env := args[0]
    77  	if config.objects {
    78  		objects, err := filteredObjects(config.Config, env, nil, filterParams{})
    79  		if err != nil {
    80  			return err
    81  		}
    82  		return showNames(objects, config.format != "", config.format, config.Stdout())
    83  	}
    84  	components, err := config.App().ComponentsForEnvironment(env, nil, nil)
    85  	if err != nil {
    86  		return err
    87  	}
    88  	return listComponents(components, config.format != "", config.format, config.Stdout())
    89  }
    90  
    91  func newComponentListCommand(cp ConfigProvider) *cobra.Command {
    92  	cmd := &cobra.Command{
    93  		Use:     "list [-objects] <environment>",
    94  		Short:   "list all components for an environment, optionally listing all objects as well",
    95  		Example: componentListExamples(),
    96  	}
    97  
    98  	config := componentListCommandConfig{}
    99  	cmd.Flags().BoolVarP(&config.objects, "objects", "O", false, "set to true to also list objects in each component")
   100  	cmd.Flags().StringVarP(&config.format, "format", "o", "", "use json|yaml to display machine readable input")
   101  
   102  	cmd.RunE = func(c *cobra.Command, args []string) error {
   103  		config.Config = cp()
   104  		return wrapError(doComponentList(args, config))
   105  	}
   106  	return cmd
   107  }
   108  
   109  type componentDiffCommandConfig struct {
   110  	*Config
   111  	objects bool
   112  }
   113  
   114  func doComponentDiff(args []string, config componentDiffCommandConfig) error {
   115  	var leftEnv, rightEnv string
   116  	switch len(args) {
   117  	case 1:
   118  		leftEnv = model.Baseline
   119  		rightEnv = args[0]
   120  	case 2:
   121  		leftEnv = args[0]
   122  		rightEnv = args[1]
   123  	default:
   124  		return newUsageError("one or two environments required")
   125  	}
   126  
   127  	getComponents := func(env string) (str string, name string, err error) {
   128  		comps, err := config.App().ComponentsForEnvironment(env, nil, nil)
   129  		if err != nil {
   130  			return
   131  		}
   132  		var buf bytes.Buffer
   133  		if err = listComponents(comps, false, "", &buf); err != nil {
   134  			return
   135  		}
   136  		str = buf.String()
   137  		name = "environment: " + env
   138  		if env == model.Baseline {
   139  			name = "baseline"
   140  		}
   141  		return
   142  	}
   143  
   144  	getObjects := func(env string) (str string, name string, err error) {
   145  		objs, err := filteredObjects(config.Config, env, nil, filterParams{})
   146  		if err != nil {
   147  			return
   148  		}
   149  		var buf bytes.Buffer
   150  		if err = showNames(objs, false, "", &buf); err != nil {
   151  			return
   152  		}
   153  		str = buf.String()
   154  		name = "environment: " + env
   155  		if env == model.Baseline {
   156  			name = "baseline"
   157  		}
   158  		return
   159  	}
   160  	var left, right, leftName, rightName string
   161  	var err error
   162  
   163  	if config.objects {
   164  		left, leftName, err = getObjects(leftEnv)
   165  		if err != nil {
   166  			return err
   167  		}
   168  		right, rightName, err = getObjects(rightEnv)
   169  		if err != nil {
   170  			return err
   171  		}
   172  	} else {
   173  		left, leftName, err = getComponents(leftEnv)
   174  		if err != nil {
   175  			return err
   176  		}
   177  		right, rightName, err = getComponents(rightEnv)
   178  		if err != nil {
   179  			return err
   180  		}
   181  	}
   182  
   183  	opts := diff.Options{Context: -1, LeftName: leftName, RightName: rightName, Colorize: config.Colorize()}
   184  	d, err := diff.Strings(left, right, opts)
   185  	if err != nil {
   186  		return err
   187  	}
   188  	fmt.Fprintln(config.Stdout(), string(d))
   189  	return nil
   190  }
   191  
   192  func newComponentDiffCommand(cp ConfigProvider) *cobra.Command {
   193  	cmd := &cobra.Command{
   194  		Use:     "diff [-objects] <environment>|_ [<environment>|_]",
   195  		Short:   "diff component lists across two environments or between the baseline (use _ for baseline) and an environment",
   196  		Example: componentDiffExamples(),
   197  	}
   198  
   199  	config := componentDiffCommandConfig{}
   200  	cmd.Flags().BoolVarP(&config.objects, "objects", "O", false, "set to true to also list objects in each component")
   201  
   202  	cmd.RunE = func(c *cobra.Command, args []string) error {
   203  		config.Config = cp()
   204  		return wrapError(doComponentDiff(args, config))
   205  	}
   206  	return cmd
   207  }