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 }