github.com/avenga/couper@v1.12.2/config/configload/error_handler.go (about)

     1  package configload
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/hashicorp/hcl/v2"
     8  	"github.com/hashicorp/hcl/v2/gohcl"
     9  	"github.com/hashicorp/hcl/v2/hclsyntax"
    10  
    11  	"github.com/avenga/couper/config"
    12  	hclbody "github.com/avenga/couper/config/body"
    13  	"github.com/avenga/couper/config/configload/collect"
    14  	"github.com/avenga/couper/errors"
    15  )
    16  
    17  type kindContent struct {
    18  	body  *hclsyntax.Body
    19  	kinds []string
    20  }
    21  
    22  func configureErrorHandler(setter []collect.ErrorHandlerSetter, helper *helper) error {
    23  	for _, ehs := range setter {
    24  		body, ok := ehs.(config.Body)
    25  		if !ok {
    26  			continue
    27  		}
    28  
    29  		kinds, ehc, err := newErrorHandlerContent(body.HCLBody())
    30  		if err != nil {
    31  			return err
    32  		}
    33  
    34  		for _, hc := range ehc {
    35  			errHandlerConf, confErr := newErrorHandlerConfig(hc, helper)
    36  			if confErr != nil {
    37  				return confErr
    38  			}
    39  
    40  			ehs.Set(errHandlerConf)
    41  		}
    42  
    43  		if handler, has := ehs.(config.ErrorHandlerGetter); has {
    44  			for _, defaultHandler := range handler.DefaultErrorHandlers() {
    45  				_, exist := kinds[errors.Wildcard]
    46  				if !exist {
    47  					for _, kind := range defaultHandler.Kinds {
    48  						_, exist = kinds[kind]
    49  						if exist {
    50  							break
    51  						}
    52  					}
    53  				}
    54  
    55  				if !exist {
    56  					ehs.Set(defaultHandler)
    57  				}
    58  			}
    59  		}
    60  	}
    61  	return nil
    62  }
    63  
    64  // newErrorHandlerContent reads given error_handler block contents and maps them by unique
    65  // error kind declaration.
    66  func newErrorHandlerContent(content *hclsyntax.Body) (map[string]struct{}, []kindContent, error) {
    67  	if content == nil {
    68  		return nil, nil, fmt.Errorf("empty hcl content")
    69  	}
    70  
    71  	configuredKinds := make(map[string]struct{})
    72  	var kindContents []kindContent
    73  
    74  	for _, block := range hclbody.BlocksOfType(content, errorHandler) {
    75  		kinds, err := newKindsFromLabels(block, true)
    76  		if err != nil {
    77  			return nil, nil, err
    78  		}
    79  		for _, k := range kinds {
    80  			if _, exist := configuredKinds[k]; exist {
    81  				subjRange := block.DefRange()
    82  				if len(block.LabelRanges) > 0 {
    83  					subjRange = block.LabelRanges[0]
    84  				}
    85  
    86  				return nil, nil, hcl.Diagnostics{&hcl.Diagnostic{
    87  					Severity: hcl.DiagError,
    88  					Summary:  fmt.Sprintf("duplicate error type registration: %q", k),
    89  					Subject:  &subjRange,
    90  				}}
    91  			}
    92  
    93  			if k != errors.Wildcard && !errors.IsKnown(k) {
    94  				subjRange := block.DefRange()
    95  				if len(block.LabelRanges) > 0 {
    96  					subjRange = block.LabelRanges[0]
    97  				}
    98  
    99  				return nil, nil, hcl.Diagnostics{&hcl.Diagnostic{
   100  					Severity: hcl.DiagError,
   101  					Summary:  fmt.Sprintf("error type is unknown: %q", k),
   102  					Subject:  &subjRange,
   103  				}}
   104  			}
   105  
   106  			configuredKinds[k] = struct{}{}
   107  		}
   108  		kindContents = append(kindContents, kindContent{
   109  			body:  block.Body,
   110  			kinds: kinds,
   111  		})
   112  	}
   113  
   114  	return configuredKinds, kindContents, nil
   115  }
   116  
   117  const errorHandlerLabelSep = " "
   118  
   119  // newKindsFromLabels reads two possible kind formats and returns them per slice entry.
   120  func newKindsFromLabels(block *hclsyntax.Block, addWildcard bool) ([]string, error) {
   121  	var allKinds []string
   122  	for _, kinds := range block.Labels {
   123  		all := strings.Split(kinds, errorHandlerLabelSep)
   124  		for i, a := range all {
   125  			if a == "" {
   126  				err := hcl.Diagnostic{
   127  					Severity: hcl.DiagError,
   128  					Summary:  "empty error_handler label",
   129  					Subject:  &block.LabelRanges[i],
   130  				}
   131  				return nil, errors.Configuration.Message(err.Error())
   132  			}
   133  		}
   134  		allKinds = append(allKinds, all...)
   135  	}
   136  	if addWildcard && len(allKinds) == 0 {
   137  		allKinds = append(allKinds, errors.Wildcard)
   138  	}
   139  	return allKinds, nil
   140  }
   141  
   142  func newErrorHandlerConfig(content kindContent, helper *helper) (*config.ErrorHandler, error) {
   143  	errHandlerConf := &config.ErrorHandler{Kinds: content.kinds}
   144  	if d := gohcl.DecodeBody(content.body, helper.context, errHandlerConf); d.HasErrors() {
   145  		return nil, d
   146  	}
   147  
   148  	ep := &config.Endpoint{
   149  		ErrorFile: errHandlerConf.ErrorFile,
   150  		Proxies:   errHandlerConf.Proxies,
   151  		Response:  errHandlerConf.Response,
   152  		Remain:    content.body,
   153  		Requests:  errHandlerConf.Requests,
   154  	}
   155  
   156  	if err := refineEndpoints(helper, config.Endpoints{ep}, false, nil); err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	errHandlerConf.Requests = ep.Requests
   161  	errHandlerConf.Proxies = ep.Proxies
   162  
   163  	return errHandlerConf, nil
   164  }