func()

in resource/context.go [261:443]


func (c *Context) AddHcl(input string, skipWhenDuplicate bool) (*types.Reference, error) {
	logrus.Debugf("adding hcl:\n%v, skipWhenDuplicate: %v", input, skipWhenDuplicate)
	inputFile, diags := hclwrite.ParseConfig([]byte(input), "", hcl.InitialPos)
	if diags.HasErrors() {
		logrus.Warnf("failed to parse input:\n%v", input)
		return nil, diags
	}

	labelsMap := make(map[string]map[string]*hclwrite.Block)
	for _, block := range c.File.Body().Blocks() {
		if block.Type() != "data" && block.Type() != "resource" {
			continue
		}
		labels := block.Labels()
		if len(labels) != 2 {
			return nil, fmt.Errorf("label is invalid: %v, input:\n%v", labels, input)
		}
		key := fmt.Sprintf("%s.%s", block.Type(), labels[0])
		if labelsMap[key] == nil {
			labelsMap[key] = make(map[string]*hclwrite.Block)
		}
		labelsMap[key][labels[1]] = block
	}

	// resource/data blocks with same resource name and labels will have conflicts,
	// merge them if their resource types are the same,
	// otherwise, rename the labels
	for _, block := range inputFile.Body().Blocks() {
		if block.Type() != "data" && block.Type() != "resource" {
			continue
		}
		labels := block.Labels()
		if len(labels) != 2 {
			return nil, fmt.Errorf("label is invalid: %v, input:\n%v", labels, input)
		}
		key := fmt.Sprintf("%s.%s", block.Type(), labels[0])
		// no conflict
		conflictBlock := labelsMap[key][labels[1]]
		if conflictBlock == nil {
			continue
		}
		logrus.Debugf("found conflict: %s %v", conflictBlock.Type(), conflictBlock.Labels())
		// if the resource types are the same, there is no conflict
		if utils.TypeValue(block) == utils.TypeValue(conflictBlock) && skipWhenDuplicate {
			continue
		}
		// conflict, rename the labels
		newLabel := labels[1]
		for i := 1; i < 100; i++ {
			newLabel = fmt.Sprintf("%s_%d", labels[1], i)
			if labelsMap[key][newLabel] == nil {
				break
			}
		}
		logrus.Debugf("renaming labels: %v -> %v", labels[1], newLabel)
		block.SetLabels([]string{labels[0], newLabel})
		input = string(inputFile.BuildTokens(nil).Bytes())

		// TODO: improve the following renaming labels logic
		oldAddressPrefix := fmt.Sprintf("%s.", strings.Join(labels, "."))
		if block.Type() == "data" {
			oldAddressPrefix = "data." + oldAddressPrefix
		}
		newAddressPrefix := fmt.Sprintf("%s.", strings.Join(block.Labels(), "."))
		if block.Type() == "data" {
			newAddressPrefix = "data." + newAddressPrefix
		}
		input = strings.ReplaceAll(input, oldAddressPrefix, newAddressPrefix)
	}

	inputFile, diags = hclwrite.ParseConfig([]byte(input), "", hcl.InitialPos)
	if diags.HasErrors() {
		logrus.Warnf("failed to parse input:\n%v", input)
		return nil, diags
	}

	// update the location and name fields
	for _, block := range inputFile.Body().Blocks() {
		if block.Type() != "data" && block.Type() != "resource" {
			continue
		}
		locationAttr := block.Body().GetAttribute("location")
		if locationAttr != nil {
			defaultLocation := utils.AttributeValue(c.locationVarBlock.Body().GetAttribute("default"))
			currentLocation := utils.AttributeValue(locationAttr)
			if currentLocation != defaultLocation && !strings.Contains(currentLocation, "var.") {
				c.locationVarBlock.Body().SetAttributeValue("default", cty.StringVal(currentLocation))
				block.Body().SetAttributeTraversal("location", hcl.Traversal{hcl.TraverseRoot{Name: "var"}, hcl.TraverseAttr{Name: "location"}})
			}
		}

		//TODO: replace location value in the body payload

		nameAttr := block.Body().GetAttribute("name")
		if nameAttr != nil {
			currentName := utils.AttributeValue(nameAttr)
			if isRandomName(currentName) {
				block.Body().SetAttributeTraversal("name", hcl.Traversal{hcl.TraverseRoot{Name: "var"}, hcl.TraverseAttr{Name: "resource_name"}})
			}
		}
	}

	varMap := make(map[string]bool)
	providerMap := make(map[string]bool)
	for _, block := range c.File.Body().Blocks() {
		switch block.Type() {
		case "provider":
			providerMap[strings.Join(block.Labels(), ".")] = true
		case "variable":
			varMap[strings.Join(block.Labels(), ".")] = true
		}
	}

	var lastBlock *hclwrite.Block
	for _, block := range inputFile.Body().Blocks() {
		switch block.Type() {
		case "terraform":
			newProvidersBlock := block.Body().FirstMatchingBlock("required_providers", []string{})
			if newProvidersBlock == nil {
				continue
			}
			oldProvidersBlock := c.terraformBlock.Body().FirstMatchingBlock("required_providers", []string{})
			if oldProvidersBlock != nil {
				for attrName, attr := range newProvidersBlock.Body().Attributes() {
					if oldProvidersBlock.Body().GetAttribute(attrName) == nil {
						oldProvidersBlock.Body().SetAttributeRaw(attrName, attr.Expr().BuildTokens(nil))
					}
				}
			} else {
				logrus.Errorf("required_providers block not found in the input.")
			}
			continue
		case "variable":
			label := strings.Join(block.Labels(), ".")
			if varMap[label] {
				continue
			}
			c.File.Body().AppendBlock(block)
		case "provider":
			label := strings.Join(block.Labels(), ".")
			if providerMap[label] {
				continue
			}
			c.File.Body().AppendBlock(block)
		case "output":
			continue
		case "locals":
			c.File.Body().AppendBlock(block)
			c.File.Body().AppendNewline()
		case "data", "resource":
			labels := block.Labels()
			if len(labels) != 2 {
				return nil, fmt.Errorf("label is invalid: %v, input:\n%v", labels, input)
			}
			key := fmt.Sprintf("%s.%s", block.Type(), labels[0])
			conflictBlock := labelsMap[key][labels[1]]
			if conflictBlock != nil {
				lastBlock = conflictBlock
				continue
			}
			c.File.Body().AppendBlock(block)
			c.File.Body().AppendNewline()
			lastBlock = block
		default:
			c.File.Body().AppendBlock(block)
			c.File.Body().AppendNewline()
		}
	}

	if lastBlock != nil {
		labels := lastBlock.Labels()
		if len(labels) != 2 {
			return nil, fmt.Errorf("label is invalid: %v, input:\n%v", labels, input)
		}
		return &types.Reference{
			Label:    labels[1],
			Kind:     lastBlock.Type(),
			Name:     labels[0],
			Property: "id",
		}, nil
	}
	return nil, fmt.Errorf("no resource or data block found in the input, input:\n%v", input)
}