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