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  }