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  }