github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/testgrid/cmd/configurator/yaml2proto.go (about) 1 /* 2 Copyright 2016 The Kubernetes Authors. 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 main 18 19 import ( 20 "errors" 21 "fmt" 22 "io" 23 24 "github.com/ghodss/yaml" 25 "github.com/golang/protobuf/proto" 26 "k8s.io/test-infra/testgrid/config" 27 ) 28 29 // Config includes config and defaults to apply on unspecified values. 30 type Config struct { 31 config *config.Configuration 32 defaultConfig *config.DefaultConfiguration 33 } 34 35 // MissingFieldError is an error that includes the missing field. 36 type MissingFieldError struct { 37 Field string 38 } 39 40 func (e MissingFieldError) Error() string { 41 return fmt.Sprintf("field missing or unset: %s", e.Field) 42 } 43 44 // ReconcileTestGroup sets unfilled currentTestGroup fields to the corresponding defaultTestGroup value. 45 func ReconcileTestGroup(currentTestGroup *config.TestGroup, defaultTestGroup *config.TestGroup) { 46 if currentTestGroup.DaysOfResults == 0 { 47 currentTestGroup.DaysOfResults = defaultTestGroup.DaysOfResults 48 } 49 50 if currentTestGroup.TestsNamePolicy == config.TestGroup_TESTS_NAME_MIN { 51 currentTestGroup.TestsNamePolicy = defaultTestGroup.TestsNamePolicy 52 } 53 54 if currentTestGroup.IgnorePending == false { 55 currentTestGroup.IgnorePending = defaultTestGroup.IgnorePending 56 } 57 58 if currentTestGroup.ColumnHeader == nil { 59 currentTestGroup.ColumnHeader = defaultTestGroup.ColumnHeader 60 } 61 62 if currentTestGroup.NumColumnsRecent == 0 { 63 currentTestGroup.NumColumnsRecent = defaultTestGroup.NumColumnsRecent 64 } 65 66 if currentTestGroup.AlertStaleResultsHours == 0 { 67 currentTestGroup.AlertStaleResultsHours = defaultTestGroup.AlertStaleResultsHours 68 } 69 70 if currentTestGroup.NumFailuresToAlert == 0 { 71 currentTestGroup.NumFailuresToAlert = defaultTestGroup.NumFailuresToAlert 72 } 73 74 if currentTestGroup.NumPassesToDisableAlert == 0 { 75 currentTestGroup.NumPassesToDisableAlert = defaultTestGroup.NumPassesToDisableAlert 76 } 77 // is_external and user_kubernetes_client should always be true 78 currentTestGroup.IsExternal = true 79 currentTestGroup.UseKubernetesClient = true 80 } 81 82 // ReconcileDashboardTab sets unfilled currentTab fields to the corresponding defaultTab value. 83 func ReconcileDashboardTab(currentTab *config.DashboardTab, defaultTab *config.DashboardTab) { 84 if currentTab.BugComponent == 0 { 85 currentTab.BugComponent = defaultTab.BugComponent 86 } 87 88 if currentTab.CodeSearchPath == "" { 89 currentTab.CodeSearchPath = defaultTab.CodeSearchPath 90 } 91 92 if currentTab.NumColumnsRecent == 0 { 93 currentTab.NumColumnsRecent = defaultTab.NumColumnsRecent 94 } 95 96 if currentTab.OpenTestTemplate == nil { 97 currentTab.OpenTestTemplate = defaultTab.OpenTestTemplate 98 } 99 100 if currentTab.FileBugTemplate == nil { 101 currentTab.FileBugTemplate = defaultTab.FileBugTemplate 102 } 103 104 if currentTab.AttachBugTemplate == nil { 105 currentTab.AttachBugTemplate = defaultTab.AttachBugTemplate 106 } 107 108 if currentTab.ResultsText == "" { 109 currentTab.ResultsText = defaultTab.ResultsText 110 } 111 112 if currentTab.ResultsUrlTemplate == nil { 113 currentTab.ResultsUrlTemplate = defaultTab.ResultsUrlTemplate 114 } 115 116 if currentTab.CodeSearchUrlTemplate == nil { 117 currentTab.CodeSearchUrlTemplate = defaultTab.CodeSearchUrlTemplate 118 } 119 120 if currentTab.AlertOptions == nil { 121 currentTab.AlertOptions = defaultTab.AlertOptions 122 } 123 } 124 125 // updateDefaults reads any default configuration from yamlData and updates the 126 // defaultConfig in c. 127 // 128 // Returns an error if the defaultConfig remains unset. 129 func (c *Config) updateDefaults(yamlData []byte) error { 130 newDefaults := &config.DefaultConfiguration{} 131 err := yaml.Unmarshal(yamlData, newDefaults) 132 if err != nil { 133 return err 134 } 135 136 if c.defaultConfig == nil { 137 c.defaultConfig = newDefaults 138 } else { 139 if newDefaults.DefaultTestGroup != nil { 140 c.defaultConfig.DefaultTestGroup = newDefaults.DefaultTestGroup 141 } 142 if newDefaults.DefaultDashboardTab != nil { 143 c.defaultConfig.DefaultDashboardTab = newDefaults.DefaultDashboardTab 144 } 145 } 146 147 if c.defaultConfig.DefaultTestGroup == nil { 148 return MissingFieldError{"DefaultTestGroup"} 149 } 150 if c.defaultConfig.DefaultDashboardTab == nil { 151 return MissingFieldError{"DefaultDashboardTab"} 152 } 153 return nil 154 } 155 156 // Update reads the config in yamlData and updates the config in c. 157 // If yamlData does not contain any defaults, the defaults from a 158 // previous call to Update are used instead. 159 func (c *Config) Update(yamlData []byte) error { 160 if err := c.updateDefaults(yamlData); err != nil { 161 return err 162 } 163 164 curConfig := &config.Configuration{} 165 if err := yaml.Unmarshal(yamlData, curConfig); err != nil { 166 return err 167 } 168 169 if c.config == nil { 170 c.config = &config.Configuration{} 171 } 172 173 for _, testgroup := range curConfig.TestGroups { 174 ReconcileTestGroup(testgroup, c.defaultConfig.DefaultTestGroup) 175 c.config.TestGroups = append(c.config.TestGroups, testgroup) 176 } 177 178 for _, dashboard := range curConfig.Dashboards { 179 // validate dashboard tabs 180 for _, dashboardtab := range dashboard.DashboardTab { 181 ReconcileDashboardTab(dashboardtab, c.defaultConfig.DefaultDashboardTab) 182 } 183 c.config.Dashboards = append(c.config.Dashboards, dashboard) 184 } 185 186 for _, dashboardGroup := range curConfig.DashboardGroups { 187 c.config.DashboardGroups = append(c.config.DashboardGroups, dashboardGroup) 188 } 189 190 return nil 191 } 192 193 // validate checks that a configuration is well-formed, having test groups and dashboards set. 194 func (c *Config) validate() error { 195 if c.config == nil { 196 return errors.New("Configuration unset") 197 } 198 if len(c.config.TestGroups) == 0 { 199 return MissingFieldError{"TestGroups"} 200 } 201 if len(c.config.Dashboards) == 0 { 202 return MissingFieldError{"Dashboards"} 203 } 204 205 return nil 206 } 207 208 // MarshalText writes a text version of the parsed configuration to the supplied io.Writer. 209 // Returns an error if config is invalid or writing failed. 210 func (c *Config) MarshalText(w io.Writer) error { 211 if err := c.validate(); err != nil { 212 return err 213 } 214 return proto.MarshalText(w, c.config) 215 } 216 217 // MarshalBytes returns the wire-encoded protobuf data for the parsed configuration. 218 // Returns an error if config is invalid or encoding failed. 219 func (c *Config) MarshalBytes() ([]byte, error) { 220 if err := c.validate(); err != nil { 221 return nil, err 222 } 223 return proto.Marshal(c.config) 224 } 225 226 // Raw returns the raw protocol buffer for the parsed configuration after validation. 227 // Returns an error if validation fails. 228 func (c *Config) Raw() (*config.Configuration, error) { 229 if err := c.validate(); err != nil { 230 return nil, err 231 } 232 return c.config, nil 233 }