terraformutils/utils.go (186 lines of code) (raw):

// Copyright 2018 The Terraformer Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package terraformutils import ( "bytes" "log" "sync" "github.com/GoogleCloudPlatform/terraformer/terraformutils/providerwrapper" "github.com/hashicorp/terraform/terraform" ) type BaseResource struct { Tags map[string]string `json:"tags,omitempty"` } func NewTfState(resources []Resource) *terraform.State { tfstate := &terraform.State{ Version: terraform.StateVersion, TFVersion: terraform.VersionString(), //nolint Serial: 1, } outputs := map[string]*terraform.OutputState{} for _, r := range resources { for k, v := range r.Outputs { outputs[k] = v } } tfstate.Modules = []*terraform.ModuleState{ { Path: []string{"root"}, Resources: map[string]*terraform.ResourceState{}, Outputs: outputs, }, } for _, resource := range resources { resourceState := &terraform.ResourceState{ Type: resource.InstanceInfo.Type, Primary: resource.InstanceState, Provider: "provider." + resource.Provider, } tfstate.Modules[0].Resources[resource.InstanceInfo.Type+"."+resource.ResourceName] = resourceState } return tfstate } func PrintTfState(resources []Resource) ([]byte, error) { state := NewTfState(resources) var buf bytes.Buffer err := terraform.WriteState(state, &buf) return buf.Bytes(), err } func RefreshResources(resources []*Resource, provider *providerwrapper.ProviderWrapper, slowProcessingResources [][]*Resource) ([]*Resource, error) { refreshedResources := []*Resource{} input := make(chan *Resource, len(resources)) var wg sync.WaitGroup poolSize := 15 for i := range resources { wg.Add(1) input <- resources[i] } close(input) for i := 0; i < poolSize; i++ { go RefreshResourceWorker(input, &wg, provider) } spInputs := []chan *Resource{} for i, resourceGroup := range slowProcessingResources { spInputs = append(spInputs, make(chan *Resource, len(resourceGroup))) for j := range resourceGroup { spInputs[i] <- resourceGroup[j] } close(spInputs[i]) } for i := 0; i < len(spInputs); i++ { wg.Add(len(slowProcessingResources[i])) go RefreshResourceWorker(spInputs[i], &wg, provider) } wg.Wait() for _, r := range resources { if r.InstanceState != nil && r.InstanceState.ID != "" { refreshedResources = append(refreshedResources, r) } else { log.Printf("ERROR: Unable to refresh resource %s", r.ResourceName) } } for _, resourceGroup := range slowProcessingResources { for i := range resourceGroup { r := resourceGroup[i] if r.InstanceState != nil && r.InstanceState.ID != "" { refreshedResources = append(refreshedResources, r) } else { log.Printf("ERROR: Unable to refresh resource %s", r.ResourceName) } } } return refreshedResources, nil } func RefreshResourcesByProvider(providersMapping *ProvidersMapping, providerWrapper *providerwrapper.ProviderWrapper) error { allResources := providersMapping.ShuffleResources() slowProcessingResources := make(map[ProviderGenerator][]*Resource) regularResources := []*Resource{} for i := range allResources { resource := allResources[i] if resource.SlowQueryRequired { provider := providersMapping.MatchProvider(resource) if slowProcessingResources[provider] == nil { slowProcessingResources[provider] = []*Resource{} } slowProcessingResources[provider] = append(slowProcessingResources[provider], resource) } else { regularResources = append(regularResources, resource) } } var spResourcesList [][]*Resource for p := range slowProcessingResources { spResourcesList = append(spResourcesList, slowProcessingResources[p]) } refreshedResources, err := RefreshResources(regularResources, providerWrapper, spResourcesList) if err != nil { return err } providersMapping.SetResources(refreshedResources) return nil } func RefreshResourceWorker(input chan *Resource, wg *sync.WaitGroup, provider *providerwrapper.ProviderWrapper) { for r := range input { log.Println("Refreshing state...", r.InstanceInfo.Id) r.Refresh(provider) wg.Done() } } func IgnoreKeys(resourcesTypes []string, p *providerwrapper.ProviderWrapper) map[string][]string { readOnlyAttributes, err := p.GetReadOnlyAttributes(resourcesTypes) if err != nil { log.Println("plugin error 2:", err) return map[string][]string{} } return readOnlyAttributes } func ParseFilterValues(value string) []string { var values []string valueBuffering := true wrapped := false var valueBuffer []byte for i := 0; i < len(value); i++ { if value[i] == '\'' { wrapped = !wrapped continue } else if value[i] == ':' { if len(valueBuffer) == 0 { continue } else if valueBuffering && !wrapped { values = append(values, string(valueBuffer)) valueBuffering = false valueBuffer = []byte{} continue } } valueBuffering = true valueBuffer = append(valueBuffer, value[i]) } if len(valueBuffer) > 0 { values = append(values, string(valueBuffer)) } return values } func FilterCleanup(s *Service, isInitial bool) { if len(s.Filter) == 0 { return } var newListOfResources []Resource for _, resource := range s.Resources { allPredicatesTrue := true for _, filter := range s.Filter { if filter.isInitial() == isInitial { allPredicatesTrue = allPredicatesTrue && filter.Filter(resource) } } if allPredicatesTrue && !ContainsResource(newListOfResources, resource) { newListOfResources = append(newListOfResources, resource) } } s.Resources = newListOfResources } func ContainsResource(s []Resource, e Resource) bool { for _, a := range s { if a.InstanceInfo.Id == e.InstanceInfo.Id { return true } } return false }