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 }