github.com/GoogleCloudPlatform/terraformer@v0.8.18/terraformutils/resource.go (about)

     1  // Copyright 2018 The Terraformer Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package terraformutils
    16  
    17  import (
    18  	"fmt"
    19  	"log"
    20  	"regexp"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/GoogleCloudPlatform/terraformer/terraformutils/providerwrapper"
    25  	"github.com/hashicorp/terraform/terraform"
    26  	"github.com/zclconf/go-cty/cty"
    27  )
    28  
    29  type Resource struct {
    30  	InstanceInfo      *terraform.InstanceInfo
    31  	InstanceState     *terraform.InstanceState
    32  	Outputs           map[string]*terraform.OutputState `json:",omitempty"`
    33  	ResourceName      string
    34  	Provider          string
    35  	Item              map[string]interface{} `json:",omitempty"`
    36  	IgnoreKeys        []string               `json:",omitempty"`
    37  	AllowEmptyValues  []string               `json:",omitempty"`
    38  	AdditionalFields  map[string]interface{} `json:",omitempty"`
    39  	SlowQueryRequired bool
    40  	DataFiles         map[string][]byte
    41  }
    42  
    43  type ApplicableFilter interface {
    44  	IsApplicable(resourceName string) bool
    45  }
    46  
    47  type ResourceFilter struct {
    48  	ApplicableFilter
    49  	ServiceName      string
    50  	FieldPath        string
    51  	AcceptableValues []string
    52  }
    53  
    54  func (rf *ResourceFilter) Filter(resource Resource) bool {
    55  	if !rf.IsApplicable(strings.TrimPrefix(resource.InstanceInfo.Type, resource.Provider+"_")) {
    56  		return true
    57  	}
    58  	var vals []interface{}
    59  	switch {
    60  	case rf.FieldPath == "id":
    61  		vals = []interface{}{resource.InstanceState.ID}
    62  	case rf.AcceptableValues == nil:
    63  		var hasField = WalkAndCheckField(rf.FieldPath, resource.InstanceState.Attributes)
    64  		if hasField {
    65  			return true
    66  		}
    67  		return WalkAndCheckField(rf.FieldPath, resource.Item)
    68  	default:
    69  		vals = WalkAndGet(rf.FieldPath, resource.InstanceState.Attributes)
    70  		if len(vals) == 0 {
    71  			vals = WalkAndGet(rf.FieldPath, resource.Item)
    72  		}
    73  	}
    74  	for _, val := range vals {
    75  		for _, acceptableValue := range rf.AcceptableValues {
    76  			if val == acceptableValue {
    77  				return true
    78  			}
    79  		}
    80  	}
    81  	return false
    82  }
    83  
    84  func (rf *ResourceFilter) IsApplicable(serviceName string) bool {
    85  	return rf.ServiceName == "" || rf.ServiceName == serviceName
    86  }
    87  
    88  func (rf *ResourceFilter) isInitial() bool {
    89  	return rf.FieldPath == "id"
    90  }
    91  
    92  func NewResource(id, resourceName, resourceType, provider string,
    93  	attributes map[string]string,
    94  	allowEmptyValues []string,
    95  	additionalFields map[string]interface{}) Resource {
    96  	return Resource{
    97  		ResourceName: TfSanitize(resourceName),
    98  		Item:         nil,
    99  		Provider:     provider,
   100  		InstanceState: &terraform.InstanceState{
   101  			ID:         id,
   102  			Attributes: attributes,
   103  		},
   104  		InstanceInfo: &terraform.InstanceInfo{
   105  			Type: resourceType,
   106  			Id:   fmt.Sprintf("%s.%s", resourceType, TfSanitize(resourceName)),
   107  		},
   108  		AdditionalFields: additionalFields,
   109  		AllowEmptyValues: allowEmptyValues,
   110  	}
   111  }
   112  
   113  func NewSimpleResource(id, resourceName, resourceType, provider string, allowEmptyValues []string) Resource {
   114  	return NewResource(
   115  		id,
   116  		resourceName,
   117  		resourceType,
   118  		provider,
   119  		map[string]string{},
   120  		allowEmptyValues,
   121  		map[string]interface{}{},
   122  	)
   123  }
   124  
   125  func (r *Resource) Refresh(provider *providerwrapper.ProviderWrapper) {
   126  	var err error
   127  	if r.SlowQueryRequired {
   128  		time.Sleep(200 * time.Millisecond)
   129  	}
   130  	r.InstanceState, err = provider.Refresh(r.InstanceInfo, r.InstanceState)
   131  	if err != nil {
   132  		log.Println(err)
   133  	}
   134  }
   135  
   136  func (r Resource) GetIDKey() string {
   137  	if _, exist := r.InstanceState.Attributes["self_link"]; exist {
   138  		return "self_link"
   139  	}
   140  	return "id"
   141  }
   142  
   143  func (r *Resource) ParseTFstate(parser Flatmapper, impliedType cty.Type) error {
   144  	attributes, err := parser.Parse(impliedType)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	// add Additional Fields to resource
   150  	for key, value := range r.AdditionalFields {
   151  		attributes[key] = value
   152  	}
   153  
   154  	if attributes == nil {
   155  		attributes = map[string]interface{}{} // ensure HCL can represent empty resource correctly
   156  	}
   157  
   158  	r.Item = attributes
   159  	return nil
   160  }
   161  
   162  func (r *Resource) ConvertTFstate(provider *providerwrapper.ProviderWrapper) error {
   163  	ignoreKeys := []*regexp.Regexp{}
   164  	for _, pattern := range r.IgnoreKeys {
   165  		ignoreKeys = append(ignoreKeys, regexp.MustCompile(pattern))
   166  	}
   167  	allowEmptyValues := []*regexp.Regexp{}
   168  	for _, pattern := range r.AllowEmptyValues {
   169  		if pattern != "" {
   170  			allowEmptyValues = append(allowEmptyValues, regexp.MustCompile(pattern))
   171  		}
   172  	}
   173  	parser := NewFlatmapParser(r.InstanceState.Attributes, ignoreKeys, allowEmptyValues)
   174  	schema := provider.GetSchema()
   175  	impliedType := schema.ResourceTypes[r.InstanceInfo.Type].Block.ImpliedType()
   176  	return r.ParseTFstate(parser, impliedType)
   177  }
   178  
   179  func (r *Resource) ServiceName() string {
   180  	return strings.TrimPrefix(r.InstanceInfo.Type, r.Provider+"_")
   181  }