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

     1  package configload
     2  
     3  import (
     4  	"fmt"
     5  	"path"
     6  	"path/filepath"
     7  	"sort"
     8  	"strings"
     9  
    10  	"github.com/avenga/couper/config"
    11  	"github.com/avenga/couper/eval"
    12  	"github.com/hashicorp/hcl/v2"
    13  	"github.com/hashicorp/hcl/v2/hclsyntax"
    14  	"github.com/zclconf/go-cty/cty"
    15  )
    16  
    17  const (
    18  	errMultipleBackends = "Multiple definitions of backend are not allowed in %s."
    19  	errUniqueLabels     = "All %s blocks must have unique labels."
    20  )
    21  
    22  type namedBlocks map[string]*hclsyntax.Block
    23  
    24  func mergeServers(bodies []*hclsyntax.Body, proxies map[string]*hclsyntax.Block) (hclsyntax.Blocks, error) {
    25  	type (
    26  		apiDefinition struct {
    27  			labels          []string
    28  			typeRange       hcl.Range
    29  			labelRanges     []hcl.Range
    30  			openBraceRange  hcl.Range
    31  			closeBraceRange hcl.Range
    32  			attributes      hclsyntax.Attributes
    33  			blocks          namedBlocks
    34  			endpoints       namedBlocks
    35  			errorHandler    namedBlocks
    36  		}
    37  		spaDefinition struct {
    38  			labels          []string
    39  			typeRange       hcl.Range
    40  			labelRanges     []hcl.Range
    41  			openBraceRange  hcl.Range
    42  			closeBraceRange hcl.Range
    43  			attributes      hclsyntax.Attributes
    44  			blocks          namedBlocks
    45  		}
    46  		filesDefinition struct {
    47  			labels          []string
    48  			typeRange       hcl.Range
    49  			labelRanges     []hcl.Range
    50  			openBraceRange  hcl.Range
    51  			closeBraceRange hcl.Range
    52  			attributes      hclsyntax.Attributes
    53  			blocks          namedBlocks
    54  		}
    55  
    56  		tlsDefinition struct {
    57  			*hclsyntax.Block
    58  			typeBlocks      map[string][]*hclsyntax.Block
    59  			preservedBlocks namedBlocks
    60  		}
    61  
    62  		namedAPIs  map[string]*apiDefinition
    63  		namedSPAs  map[string]*spaDefinition
    64  		namedFiles map[string]*filesDefinition
    65  
    66  		serverDefinition struct {
    67  			labels          []string
    68  			typeRange       hcl.Range
    69  			labelRanges     []hcl.Range
    70  			openBraceRange  hcl.Range
    71  			closeBraceRange hcl.Range
    72  			attributes      hclsyntax.Attributes
    73  			apis            namedAPIs
    74  			blocks          namedBlocks
    75  			endpoints       namedBlocks
    76  			files           namedFiles
    77  			spas            namedSPAs
    78  			tls             *tlsDefinition
    79  		}
    80  
    81  		servers map[string]*serverDefinition
    82  	)
    83  
    84  	/*
    85  		serverDefinition[<key>] = {
    86  			attributes       = hclsyntax.Attributes
    87  			blocks[<name>]   = hclsyntax.Block (cors, files, spa)
    88  			endpoints[<key>] = hclsyntax.Block
    89  			apis[<key>]      = {
    90  				attributes           = hclsyntax.Attributes
    91  				blocks[<name>]       = hclsyntax.Block (cors)
    92  				endpoints[<key>]     = hclsyntax.Block
    93  				error_handler[<key>] = hclsyntax.Block
    94  			}
    95  			spas[<key>]      = {
    96  				attributes           = hclsyntax.Attributes
    97  				blocks[<name>]       = hclsyntax.Block (cors)
    98  			}
    99  			files[<key>]      = {
   100  				attributes           = hclsyntax.Attributes
   101  				blocks[<name>]       = hclsyntax.Block (cors)
   102  			}
   103  			tls = {
   104  				blocks[<name>]       = hclsyntax.Block (server_certificate|client_certificate)
   105  			}
   106  		}
   107  	*/
   108  
   109  	results := make(servers)
   110  
   111  	for _, body := range bodies {
   112  		uniqueServerLabels := make(map[string]struct{})
   113  
   114  		for _, outerBlock := range body.Blocks {
   115  			if outerBlock.Type != server {
   116  				continue
   117  			}
   118  
   119  			var serverKey string
   120  
   121  			if len(outerBlock.Labels) > 0 {
   122  				serverKey = outerBlock.Labels[0]
   123  			}
   124  
   125  			if len(bodies) > 1 {
   126  				if _, ok := uniqueServerLabels[serverKey]; ok {
   127  					return nil, newMergeError(errUniqueLabels, outerBlock)
   128  				}
   129  
   130  				uniqueServerLabels[serverKey] = struct{}{}
   131  			} else {
   132  				// Create unique key for multiple server blocks inside a single config file.
   133  				serverKey += fmt.Sprintf("|%p", &serverKey)
   134  			}
   135  
   136  			if results[serverKey] == nil {
   137  				results[serverKey] = &serverDefinition{
   138  					labels:          outerBlock.Labels,
   139  					typeRange:       outerBlock.TypeRange,
   140  					labelRanges:     outerBlock.LabelRanges,
   141  					openBraceRange:  outerBlock.OpenBraceRange,
   142  					closeBraceRange: outerBlock.CloseBraceRange,
   143  					attributes:      make(hclsyntax.Attributes),
   144  					blocks:          make(namedBlocks),
   145  					endpoints:       make(namedBlocks),
   146  					apis:            make(namedAPIs),
   147  					spas:            make(namedSPAs),
   148  					files:           make(namedFiles),
   149  				}
   150  			}
   151  
   152  			for name, attr := range outerBlock.Body.Attributes {
   153  				results[serverKey].attributes[name] = attr
   154  			}
   155  
   156  			for _, block := range outerBlock.Body.Blocks {
   157  				uniqueAPILabels, uniqueSPALabels, uniqueFilesLabels := make(map[string]struct{}), make(map[string]struct{}), make(map[string]struct{})
   158  
   159  				if block.Type == endpoint {
   160  					if err := checkForMultipleBackends(block); err != nil { // Backend block inside a free endpoint block
   161  						return nil, err
   162  					}
   163  
   164  					if len(block.Labels) == 0 {
   165  						return nil, newMergeError(errUniqueLabels, block)
   166  					}
   167  
   168  					if err := addProxy(block, proxies); err != nil {
   169  						return nil, err
   170  					}
   171  
   172  					results[serverKey].endpoints[block.Labels[0]] = block
   173  				} else if block.Type == api {
   174  					var apiKey string
   175  
   176  					if len(block.Labels) > 0 {
   177  						apiKey = block.Labels[0]
   178  					}
   179  
   180  					if len(bodies) > 1 {
   181  						if _, ok := uniqueAPILabels[apiKey]; ok {
   182  							return nil, newMergeError(errUniqueLabels, block)
   183  						}
   184  
   185  						uniqueAPILabels[apiKey] = struct{}{}
   186  					} else {
   187  						// Create unique key for multiple api blocks inside a single config file.
   188  						apiKey += fmt.Sprintf("|%p", &apiKey)
   189  					}
   190  
   191  					if results[serverKey].apis[apiKey] == nil {
   192  						results[serverKey].apis[apiKey] = &apiDefinition{
   193  							labels:          block.Labels,
   194  							typeRange:       block.TypeRange,
   195  							labelRanges:     block.LabelRanges,
   196  							openBraceRange:  block.OpenBraceRange,
   197  							closeBraceRange: block.CloseBraceRange,
   198  							attributes:      make(hclsyntax.Attributes),
   199  							blocks:          make(namedBlocks),
   200  							endpoints:       make(namedBlocks),
   201  							errorHandler:    make(namedBlocks),
   202  						}
   203  					}
   204  
   205  					for name, attr := range block.Body.Attributes {
   206  						results[serverKey].apis[apiKey].attributes[name] = attr
   207  					}
   208  
   209  					for _, subBlock := range block.Body.Blocks {
   210  						if subBlock.Type == endpoint {
   211  							if err := checkForMultipleBackends(subBlock); err != nil {
   212  								return nil, err
   213  							}
   214  
   215  							if len(subBlock.Labels) == 0 {
   216  								return nil, newMergeError(errUniqueLabels, subBlock)
   217  							}
   218  
   219  							if err := addProxy(subBlock, proxies); err != nil {
   220  								return nil, err
   221  							}
   222  
   223  							results[serverKey].apis[apiKey].endpoints[subBlock.Labels[0]] = subBlock
   224  						} else if subBlock.Type == errorHandler {
   225  							if err := checkForMultipleBackends(subBlock); err != nil {
   226  								return nil, err
   227  							}
   228  
   229  							ehKey := newErrorHandlerKey(subBlock)
   230  
   231  							results[serverKey].apis[apiKey].errorHandler[ehKey] = subBlock
   232  						} else {
   233  							results[serverKey].apis[apiKey].blocks[subBlock.Type] = subBlock
   234  						}
   235  					}
   236  				} else if block.Type == spa {
   237  					var spaKey string
   238  
   239  					if len(block.Labels) > 0 {
   240  						spaKey = block.Labels[0]
   241  					}
   242  
   243  					if len(bodies) > 1 {
   244  						if _, ok := uniqueSPALabels[spaKey]; ok {
   245  							return nil, newMergeError(errUniqueLabels, block)
   246  						}
   247  
   248  						uniqueSPALabels[spaKey] = struct{}{}
   249  					} else {
   250  						// Create unique key for multiple spa blocks inside a single config file.
   251  						spaKey += fmt.Sprintf("|%p", &spaKey)
   252  					}
   253  
   254  					if results[serverKey].spas[spaKey] == nil {
   255  						results[serverKey].spas[spaKey] = &spaDefinition{
   256  							labels:          block.Labels,
   257  							typeRange:       block.TypeRange,
   258  							labelRanges:     block.LabelRanges,
   259  							openBraceRange:  block.OpenBraceRange,
   260  							closeBraceRange: block.CloseBraceRange,
   261  							attributes:      make(hclsyntax.Attributes),
   262  							blocks:          make(namedBlocks),
   263  						}
   264  					}
   265  
   266  					for name, attr := range block.Body.Attributes {
   267  						results[serverKey].spas[spaKey].attributes[name] = attr
   268  					}
   269  
   270  					for _, subBlock := range block.Body.Blocks {
   271  						results[serverKey].spas[spaKey].blocks[subBlock.Type] = subBlock
   272  					}
   273  				} else if block.Type == files {
   274  					var filesKey string
   275  
   276  					if len(block.Labels) > 0 {
   277  						filesKey = block.Labels[0]
   278  					}
   279  
   280  					if len(bodies) > 1 {
   281  						if _, ok := uniqueFilesLabels[filesKey]; ok {
   282  							return nil, newMergeError(errUniqueLabels, block)
   283  						}
   284  
   285  						uniqueFilesLabels[filesKey] = struct{}{}
   286  					} else {
   287  						// Create unique key for multiple files blocks inside a single config file.
   288  						filesKey += fmt.Sprintf("|%p", &filesKey)
   289  					}
   290  
   291  					if results[serverKey].files[filesKey] == nil {
   292  						results[serverKey].files[filesKey] = &filesDefinition{
   293  							labels:          block.Labels,
   294  							typeRange:       block.TypeRange,
   295  							labelRanges:     block.LabelRanges,
   296  							openBraceRange:  block.OpenBraceRange,
   297  							closeBraceRange: block.CloseBraceRange,
   298  							attributes:      make(hclsyntax.Attributes),
   299  							blocks:          make(namedBlocks),
   300  						}
   301  					}
   302  
   303  					for name, attr := range block.Body.Attributes {
   304  						results[serverKey].files[filesKey].attributes[name] = attr
   305  					}
   306  
   307  					for _, subBlock := range block.Body.Blocks {
   308  						results[serverKey].files[filesKey].blocks[subBlock.Type] = subBlock
   309  					}
   310  				} else if block.Type == tls {
   311  					if results[serverKey].tls == nil {
   312  						results[serverKey].tls = &tlsDefinition{
   313  							Block:           block,
   314  							preservedBlocks: namedBlocks{},
   315  							typeBlocks:      map[string][]*hclsyntax.Block{},
   316  						}
   317  					}
   318  
   319  					for name, attr := range block.Body.Attributes {
   320  						results[serverKey].tls.Body.Attributes[name] = attr
   321  					}
   322  
   323  					thisTypeBlocks := map[string][]*hclsyntax.Block{}
   324  					for _, subBlock := range block.Body.Blocks {
   325  						blockKey := subBlock.Type
   326  						if len(subBlock.Labels) > 0 {
   327  							blockKey += "_" + subBlock.Labels[0]
   328  							results[serverKey].tls.preservedBlocks[blockKey] = subBlock // override
   329  						} else {
   330  							thisTypeBlocks[subBlock.Type] = append(thisTypeBlocks[subBlock.Type], subBlock)
   331  						}
   332  					}
   333  
   334  					for t, v := range thisTypeBlocks { // reset per file
   335  						if len(v) > 0 {
   336  							results[serverKey].tls.typeBlocks[t] = v
   337  						}
   338  
   339  					}
   340  
   341  				} else {
   342  					results[serverKey].blocks[block.Type] = block
   343  				}
   344  			}
   345  		}
   346  	}
   347  
   348  	var mergedServers hclsyntax.Blocks
   349  
   350  	for _, name := range getSortedMapKeys(results) {
   351  		var serverBlocks hclsyntax.Blocks
   352  		serverBlock := results[name]
   353  		for _, blockName := range getSortedMapKeys(serverBlock.blocks) {
   354  			serverBlocks = append(serverBlocks, serverBlock.blocks[blockName])
   355  		}
   356  
   357  		for _, blockName := range getSortedMapKeys(serverBlock.endpoints) {
   358  			serverBlocks = append(serverBlocks, serverBlock.endpoints[blockName])
   359  		}
   360  
   361  		for _, apiBlock := range serverBlock.apis {
   362  			var apiBlocks hclsyntax.Blocks
   363  
   364  			for _, blockName := range getSortedMapKeys(apiBlock.blocks) {
   365  				apiBlocks = append(apiBlocks, apiBlock.blocks[blockName])
   366  			}
   367  
   368  			for _, blockName := range getSortedMapKeys(apiBlock.endpoints) {
   369  				apiBlocks = append(apiBlocks, apiBlock.endpoints[blockName])
   370  			}
   371  
   372  			for _, blockName := range getSortedMapKeys(apiBlock.errorHandler) {
   373  				apiBlocks = append(apiBlocks, apiBlock.errorHandler[blockName])
   374  			}
   375  
   376  			mergedAPI := &hclsyntax.Block{
   377  				Type:   api,
   378  				Labels: apiBlock.labels,
   379  				Body: &hclsyntax.Body{
   380  					Attributes: apiBlock.attributes,
   381  					Blocks:     apiBlocks,
   382  				},
   383  				TypeRange:       apiBlock.typeRange,
   384  				LabelRanges:     apiBlock.labelRanges,
   385  				OpenBraceRange:  apiBlock.openBraceRange,
   386  				CloseBraceRange: apiBlock.closeBraceRange,
   387  			}
   388  
   389  			serverBlocks = append(serverBlocks, mergedAPI)
   390  		}
   391  
   392  		for _, blockName := range getSortedMapKeys(serverBlock.spas) {
   393  			spaBlock := serverBlock.spas[blockName]
   394  			var spaBlocks hclsyntax.Blocks
   395  
   396  			for _, bn := range getSortedMapKeys(spaBlock.blocks) {
   397  				spaBlocks = append(spaBlocks, spaBlock.blocks[bn])
   398  			}
   399  
   400  			mergedSPA := &hclsyntax.Block{
   401  				Type:   spa,
   402  				Labels: spaBlock.labels,
   403  				Body: &hclsyntax.Body{
   404  					Attributes: spaBlock.attributes,
   405  					Blocks:     spaBlocks,
   406  				},
   407  				TypeRange:       spaBlock.typeRange,
   408  				LabelRanges:     spaBlock.labelRanges,
   409  				OpenBraceRange:  spaBlock.openBraceRange,
   410  				CloseBraceRange: spaBlock.closeBraceRange,
   411  			}
   412  
   413  			serverBlocks = append(serverBlocks, mergedSPA)
   414  		}
   415  
   416  		for _, blockName := range getSortedMapKeys(serverBlock.files) {
   417  			filesBlock := serverBlock.files[blockName]
   418  			var filesBlocks hclsyntax.Blocks
   419  
   420  			for _, bn := range getSortedMapKeys(filesBlock.blocks) {
   421  				filesBlocks = append(filesBlocks, filesBlock.blocks[bn])
   422  			}
   423  
   424  			mergedFiles := &hclsyntax.Block{
   425  				Type:   files,
   426  				Labels: filesBlock.labels,
   427  				Body: &hclsyntax.Body{
   428  					Attributes: filesBlock.attributes,
   429  					Blocks:     filesBlocks,
   430  				},
   431  				TypeRange:       filesBlock.typeRange,
   432  				LabelRanges:     filesBlock.labelRanges,
   433  				OpenBraceRange:  filesBlock.openBraceRange,
   434  				CloseBraceRange: filesBlock.closeBraceRange,
   435  			}
   436  
   437  			serverBlocks = append(serverBlocks, mergedFiles)
   438  		}
   439  
   440  		if serverBlock.tls != nil {
   441  			var tlsCertificateBlocks hclsyntax.Blocks
   442  			for _, subType := range getSortedMapKeys(serverBlock.tls.typeBlocks) {
   443  				tlsCertificateBlocks = append(tlsCertificateBlocks, serverBlock.tls.typeBlocks[subType]...)
   444  			}
   445  			for _, k := range getSortedMapKeys(serverBlock.tls.preservedBlocks) {
   446  				tlsCertificateBlocks = append(tlsCertificateBlocks, serverBlock.tls.preservedBlocks[k])
   447  			}
   448  			serverBlocks = append(serverBlocks, &hclsyntax.Block{
   449  				Type: tls,
   450  				Body: &hclsyntax.Body{
   451  					Attributes: serverBlock.tls.Body.Attributes,
   452  					Blocks:     tlsCertificateBlocks,
   453  				},
   454  				TypeRange:       serverBlock.tls.TypeRange,
   455  				LabelRanges:     serverBlock.tls.LabelRanges,
   456  				OpenBraceRange:  serverBlock.tls.OpenBraceRange,
   457  				CloseBraceRange: serverBlock.tls.CloseBraceRange,
   458  			})
   459  		}
   460  
   461  		mergedServer := &hclsyntax.Block{
   462  			Type:   server,
   463  			Labels: serverBlock.labels,
   464  			Body: &hclsyntax.Body{
   465  				Attributes: serverBlock.attributes,
   466  				Blocks:     serverBlocks,
   467  			},
   468  			TypeRange:       serverBlock.typeRange,
   469  			LabelRanges:     serverBlock.labelRanges,
   470  			OpenBraceRange:  serverBlock.openBraceRange,
   471  			CloseBraceRange: serverBlock.closeBraceRange,
   472  		}
   473  
   474  		mergedServers = append(mergedServers, mergedServer)
   475  	}
   476  
   477  	return mergedServers, nil
   478  }
   479  
   480  func mergeDefinitions(bodies []*hclsyntax.Body) (*hclsyntax.Block, map[string]*hclsyntax.Block, error) {
   481  	definitionsBlock := make(map[string]namedBlocks)
   482  	proxiesList := make(namedBlocks)
   483  
   484  	for _, body := range bodies {
   485  		for _, outerBlock := range body.Blocks {
   486  			if outerBlock.Type == definitions {
   487  				for _, innerBlock := range outerBlock.Body.Blocks {
   488  					if definitionsBlock[innerBlock.Type] == nil {
   489  						definitionsBlock[innerBlock.Type] = make(namedBlocks)
   490  					}
   491  
   492  					if innerBlock.Type == backend {
   493  						if err := checkForMultipleBackendsInBackend(innerBlock); err != nil {
   494  							return nil, nil, err
   495  						}
   496  					}
   497  
   498  					// Count the "backend" blocks and "backend" attributes to
   499  					// forbid multiple backend definitions.
   500  
   501  					var backends int
   502  
   503  					for _, block := range innerBlock.Body.Blocks {
   504  						if block.Type == errorHandler {
   505  							if err := checkForMultipleBackends(block); err != nil {
   506  								return nil, nil, err
   507  							}
   508  						} else if block.Type == backend {
   509  							backends++
   510  							if err := checkForMultipleBackendsInBackend(block); err != nil {
   511  								return nil, nil, err
   512  							}
   513  						}
   514  					}
   515  
   516  					if _, ok := innerBlock.Body.Attributes[backend]; ok {
   517  						backends++
   518  					}
   519  
   520  					if backends > 1 {
   521  						return nil, nil, newMergeError(errMultipleBackends, innerBlock)
   522  					}
   523  
   524  					if innerBlock.Type != proxy {
   525  						definitionsBlock[innerBlock.Type][innerBlock.Labels[0]] = innerBlock
   526  					} else {
   527  						label := innerBlock.Labels[0]
   528  
   529  						if attr, ok := innerBlock.Body.Attributes["name"]; ok {
   530  							name, err := attrStringValue(attr)
   531  							if err != nil {
   532  								return nil, nil, err
   533  							}
   534  
   535  							innerBlock.Labels[0] = name
   536  
   537  							delete(innerBlock.Body.Attributes, "name")
   538  						} else {
   539  							innerBlock.Labels[0] = config.DefaultNameLabel
   540  						}
   541  
   542  						proxiesList[label] = innerBlock
   543  					}
   544  				}
   545  			}
   546  		}
   547  	}
   548  
   549  	var blocks []*hclsyntax.Block
   550  
   551  	for _, name := range getSortedMapKeys(definitionsBlock) {
   552  		for _, label := range getSortedMapKeys(definitionsBlock[name]) {
   553  			blocks = append(blocks, definitionsBlock[name][label])
   554  		}
   555  	}
   556  
   557  	return &hclsyntax.Block{
   558  		Type: definitions,
   559  		Body: &hclsyntax.Body{
   560  			Blocks: blocks,
   561  		},
   562  	}, proxiesList, nil
   563  }
   564  
   565  func mergeDefaults(bodies []*hclsyntax.Body) (*hclsyntax.Block, error) {
   566  	attrs := make(hclsyntax.Attributes)
   567  	envVars := make(map[string]hclsyntax.Expression)
   568  
   569  	for _, body := range bodies {
   570  		for _, block := range body.Blocks {
   571  			if block.Type != defaults {
   572  				continue
   573  			}
   574  
   575  			for name, attr := range block.Body.Attributes {
   576  				if name != environmentVars {
   577  					attrs[name] = attr // Currently not used
   578  					continue
   579  				}
   580  
   581  				expObj, ok := attr.Expr.(*hclsyntax.ObjectConsExpr)
   582  				if !ok {
   583  					r := attr.Expr.Range()
   584  					return nil, newDiagErr(&r, fmt.Sprintf("%s must be object type", environmentVars))
   585  				}
   586  
   587  				for _, item := range expObj.Items {
   588  					k := item.KeyExpr.(*hclsyntax.ObjectConsKeyExpr)
   589  					r := item.KeyExpr.Range()
   590  					var keyName string
   591  					switch exp := k.Wrapped.(type) {
   592  					case *hclsyntax.ScopeTraversalExpr:
   593  						if len(exp.Traversal) > 1 {
   594  							return nil, newDiagErr(&r, "unsupported key scope traversal expression")
   595  						}
   596  						keyName = exp.Traversal.RootName()
   597  					case *hclsyntax.TemplateExpr:
   598  						if !exp.IsStringLiteral() {
   599  							return nil, newDiagErr(&r, "unsupported key template expression")
   600  						}
   601  						v, _ := exp.Value(nil)
   602  						keyName = v.AsString()
   603  					default:
   604  						r := item.KeyExpr.Range()
   605  						return nil, newDiagErr(&r, "unsupported key expression")
   606  					}
   607  
   608  					envVars[keyName] = item.ValueExpr
   609  				}
   610  			}
   611  		}
   612  	}
   613  
   614  	if len(envVars) > 0 {
   615  		items := make([]hclsyntax.ObjectConsItem, 0)
   616  		for k, v := range envVars {
   617  			items = append(items, hclsyntax.ObjectConsItem{
   618  				KeyExpr: &hclsyntax.ObjectConsKeyExpr{
   619  					Wrapped: &hclsyntax.ScopeTraversalExpr{
   620  						Traversal: hcl.Traversal{hcl.TraverseRoot{Name: k}},
   621  					},
   622  					ForceNonLiteral: false,
   623  				},
   624  				ValueExpr: v,
   625  			})
   626  		}
   627  
   628  		attrs[environmentVars] = &hclsyntax.Attribute{
   629  			Name: environmentVars,
   630  			Expr: &hclsyntax.ObjectConsExpr{
   631  				Items: items,
   632  			},
   633  		}
   634  	}
   635  
   636  	defaultsBlock := &hclsyntax.Block{
   637  		Type: defaults,
   638  		Body: &hclsyntax.Body{
   639  			Attributes: attrs,
   640  		},
   641  	}
   642  	return defaultsBlock, nil
   643  }
   644  
   645  func mergeSettings(bodies []*hclsyntax.Body) *hclsyntax.Block {
   646  	attrs := make(hclsyntax.Attributes)
   647  
   648  	for _, body := range bodies {
   649  		for _, block := range body.Blocks {
   650  			if block.Type == settings {
   651  				for name, attr := range block.Body.Attributes {
   652  					attrs[name] = attr
   653  				}
   654  			}
   655  		}
   656  	}
   657  
   658  	return &hclsyntax.Block{
   659  		Type: settings,
   660  		Body: &hclsyntax.Body{
   661  			Attributes: attrs,
   662  		},
   663  	}
   664  }
   665  
   666  func newMergeError(msg string, block *hclsyntax.Block) error {
   667  	defRange := block.DefRange()
   668  
   669  	return hcl.Diagnostics{
   670  		&hcl.Diagnostic{
   671  			Severity: hcl.DiagError,
   672  			Summary:  fmt.Sprintf(msg, block.Type),
   673  			Subject:  &defRange,
   674  		},
   675  	}
   676  }
   677  
   678  // absPath replaces the given attribute path expression with an absolute path
   679  // related to its filename if not already an absolute one.
   680  func absPath(attr *hclsyntax.Attribute) hclsyntax.Expression {
   681  	value, diags := attr.Expr.Value(envContext)
   682  	if diags.HasErrors() || strings.Index(value.AsString(), "/") == 0 {
   683  		return attr.Expr // Return unchanged in error cases and for absolute path values.
   684  	}
   685  
   686  	return &hclsyntax.LiteralValueExpr{
   687  		Val: cty.StringVal(
   688  			path.Join(filepath.Dir(attr.SrcRange.Filename), value.AsString()),
   689  		),
   690  		SrcRange: attr.SrcRange,
   691  	}
   692  }
   693  
   694  // checkForMultipleBackends searches for "backend" blocks inside a proxy or request block to
   695  // count the "backend"
   696  // blocks and "backend" attributes to forbid multiple backend definitions.
   697  func checkForMultipleBackends(block *hclsyntax.Block) error {
   698  	for _, subBlock := range block.Body.Blocks {
   699  		if subBlock.Type == errorHandler {
   700  			return checkForMultipleBackends(subBlock) // Recursive call
   701  		}
   702  
   703  		if subBlock.Type != proxy && subBlock.Type != request {
   704  			continue
   705  		}
   706  
   707  		var backends int
   708  
   709  		for _, subSubBlock := range subBlock.Body.Blocks {
   710  			if subSubBlock.Type == backend {
   711  				backends++
   712  				if err := checkForMultipleBackendsInBackend(subSubBlock); err != nil {
   713  					return err
   714  				}
   715  			}
   716  		}
   717  
   718  		if _, ok := subBlock.Body.Attributes[backend]; ok {
   719  			backends++
   720  		}
   721  
   722  		if backends > 1 {
   723  			return newMergeError(errMultipleBackends, subBlock)
   724  		}
   725  	}
   726  
   727  	return nil
   728  }
   729  
   730  func checkForMultipleBackendsInBackend(block *hclsyntax.Block) error {
   731  	for _, subBlock := range block.Body.Blocks {
   732  		if subBlock.Type != oauth2 && subBlock.Type != tokenRequest {
   733  			continue
   734  		}
   735  
   736  		var backends int
   737  
   738  		for _, subSubBlock := range subBlock.Body.Blocks {
   739  			if subSubBlock.Type == backend {
   740  				backends++
   741  				if err := checkForMultipleBackendsInBackend(subSubBlock); err != nil { // Recursive call
   742  					return err
   743  				}
   744  			}
   745  		}
   746  
   747  		if _, ok := subBlock.Body.Attributes[backend]; ok {
   748  			backends++
   749  		}
   750  
   751  		if backends > 1 {
   752  			return newMergeError(errMultipleBackends, subBlock)
   753  		}
   754  	}
   755  
   756  	return nil
   757  }
   758  
   759  func addProxy(block *hclsyntax.Block, proxies map[string]*hclsyntax.Block) error {
   760  	if attr, ok := block.Body.Attributes[proxy]; ok {
   761  		reference, err := attrStringValue(attr)
   762  		if err != nil {
   763  			return err
   764  		}
   765  
   766  		proxyBlock, ok := proxies[reference]
   767  		if !ok {
   768  			sr := attr.Expr.StartRange()
   769  
   770  			return newDiagErr(&sr, fmt.Sprintf("referenced proxy %q is not defined", reference))
   771  		}
   772  
   773  		delete(block.Body.Attributes, proxy)
   774  
   775  		block.Body.Blocks = append(block.Body.Blocks, proxyBlock)
   776  	}
   777  
   778  	return nil
   779  }
   780  
   781  func attrStringValue(attr *hclsyntax.Attribute) (string, error) {
   782  	v, err := eval.Value(nil, attr.Expr)
   783  	if err != nil {
   784  		return "", err
   785  	}
   786  
   787  	if v.Type() != cty.String {
   788  		sr := attr.Expr.StartRange()
   789  		return "", newDiagErr(&sr, fmt.Sprintf("%s must evaluate to string", attr.Name))
   790  	}
   791  
   792  	return v.AsString(), nil
   793  }
   794  
   795  // newErrorHandlerKey returns a merge key based on a possible mixed error-kind format.
   796  // "label1" and/or "label2 label3" results in "label1 label2 label3".
   797  func newErrorHandlerKey(block *hclsyntax.Block) (key string) {
   798  	if len(block.Labels) == 0 {
   799  		return key
   800  	}
   801  
   802  	var sorted []string
   803  	for _, l := range block.Labels {
   804  		sorted = append(sorted, strings.Split(l, errorHandlerLabelSep)...)
   805  	}
   806  	sort.Strings(sorted)
   807  
   808  	return strings.Join(sorted, errorHandlerLabelSep)
   809  }
   810  
   811  func getSortedMapKeys[K string, V any](m map[K]V) []string {
   812  	var result []string
   813  	for k := range m {
   814  		result = append(result, string(k))
   815  	}
   816  	sort.Strings(result)
   817  	return result
   818  }