github.com/wikibal01/hashicorp-terraform@v0.11.12-beta1/terraform/transform_import_state.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 ) 6 7 // ImportStateTransformer is a GraphTransformer that adds nodes to the 8 // graph to represent the imports we want to do for resources. 9 type ImportStateTransformer struct { 10 Targets []*ImportTarget 11 } 12 13 func (t *ImportStateTransformer) Transform(g *Graph) error { 14 nodes := make([]*graphNodeImportState, 0, len(t.Targets)) 15 for _, target := range t.Targets { 16 addr, err := ParseResourceAddress(target.Addr) 17 if err != nil { 18 return fmt.Errorf( 19 "failed to parse resource address '%s': %s", 20 target.Addr, err) 21 } 22 23 nodes = append(nodes, &graphNodeImportState{ 24 Addr: addr, 25 ID: target.ID, 26 ProviderName: target.Provider, 27 }) 28 } 29 30 // Build the graph vertices 31 for _, n := range nodes { 32 g.Add(n) 33 } 34 35 return nil 36 } 37 38 type graphNodeImportState struct { 39 Addr *ResourceAddress // Addr is the resource address to import to 40 ID string // ID is the ID to import as 41 ProviderName string // Provider string 42 ResolvedProvider string // provider node address 43 44 states []*InstanceState 45 } 46 47 func (n *graphNodeImportState) Name() string { 48 return fmt.Sprintf("%s (import id: %s)", n.Addr, n.ID) 49 } 50 51 func (n *graphNodeImportState) ProvidedBy() string { 52 return resourceProvider(n.Addr.Type, n.ProviderName) 53 } 54 55 func (n *graphNodeImportState) SetProvider(p string) { 56 n.ResolvedProvider = p 57 } 58 59 // GraphNodeSubPath 60 func (n *graphNodeImportState) Path() []string { 61 return normalizeModulePath(n.Addr.Path) 62 } 63 64 // GraphNodeEvalable impl. 65 func (n *graphNodeImportState) EvalTree() EvalNode { 66 var provider ResourceProvider 67 info := &InstanceInfo{ 68 Id: fmt.Sprintf("%s.%s", n.Addr.Type, n.Addr.Name), 69 ModulePath: n.Path(), 70 Type: n.Addr.Type, 71 } 72 73 // Reset our states 74 n.states = nil 75 76 // Return our sequence 77 return &EvalSequence{ 78 Nodes: []EvalNode{ 79 &EvalGetProvider{ 80 Name: n.ResolvedProvider, 81 Output: &provider, 82 }, 83 &EvalImportState{ 84 Provider: &provider, 85 Info: info, 86 Id: n.ID, 87 Output: &n.states, 88 }, 89 }, 90 } 91 } 92 93 // GraphNodeDynamicExpandable impl. 94 // 95 // We use DynamicExpand as a way to generate the subgraph of refreshes 96 // and state inserts we need to do for our import state. Since they're new 97 // resources they don't depend on anything else and refreshes are isolated 98 // so this is nearly a perfect use case for dynamic expand. 99 func (n *graphNodeImportState) DynamicExpand(ctx EvalContext) (*Graph, error) { 100 g := &Graph{Path: ctx.Path()} 101 102 // nameCounter is used to de-dup names in the state. 103 nameCounter := make(map[string]int) 104 105 // Compile the list of addresses that we'll be inserting into the state. 106 // We do this ahead of time so we can verify that we aren't importing 107 // something that already exists. 108 addrs := make([]*ResourceAddress, len(n.states)) 109 for i, state := range n.states { 110 addr := *n.Addr 111 if t := state.Ephemeral.Type; t != "" { 112 addr.Type = t 113 } 114 115 // Determine if we need to suffix the name to de-dup 116 key := addr.String() 117 count, ok := nameCounter[key] 118 if ok { 119 count++ 120 addr.Name += fmt.Sprintf("-%d", count) 121 } 122 nameCounter[key] = count 123 124 // Add it to our list 125 addrs[i] = &addr 126 } 127 128 // Verify that all the addresses are clear 129 state, lock := ctx.State() 130 lock.RLock() 131 defer lock.RUnlock() 132 filter := &StateFilter{State: state} 133 for _, addr := range addrs { 134 result, err := filter.Filter(addr.String()) 135 if err != nil { 136 return nil, fmt.Errorf("Error verifying address %s: %s", addr, err) 137 } 138 139 // Go through the filter results and it is an error if we find 140 // a matching InstanceState, meaning that we would have a collision. 141 for _, r := range result { 142 if _, ok := r.Value.(*InstanceState); ok { 143 return nil, fmt.Errorf( 144 "Can't import %s, would collide with an existing resource.\n\n"+ 145 "Please remove or rename this resource before continuing.", 146 addr) 147 } 148 } 149 } 150 151 // For each of the states, we add a node to handle the refresh/add to state. 152 // "n.states" is populated by our own EvalTree with the result of 153 // ImportState. Since DynamicExpand is always called after EvalTree, this 154 // is safe. 155 for i, state := range n.states { 156 g.Add(&graphNodeImportStateSub{ 157 Target: addrs[i], 158 Path_: n.Path(), 159 State: state, 160 ProviderName: n.ProviderName, 161 ResolvedProvider: n.ResolvedProvider, 162 }) 163 } 164 165 // Root transform for a single root 166 t := &RootTransformer{} 167 if err := t.Transform(g); err != nil { 168 return nil, err 169 } 170 171 // Done! 172 return g, nil 173 } 174 175 // graphNodeImportStateSub is the sub-node of graphNodeImportState 176 // and is part of the subgraph. This node is responsible for refreshing 177 // and adding a resource to the state once it is imported. 178 type graphNodeImportStateSub struct { 179 Target *ResourceAddress 180 State *InstanceState 181 Path_ []string 182 ProviderName string 183 ResolvedProvider string 184 } 185 186 func (n *graphNodeImportStateSub) Name() string { 187 return fmt.Sprintf("import %s result: %s", n.Target, n.State.ID) 188 } 189 190 func (n *graphNodeImportStateSub) Path() []string { 191 return n.Path_ 192 } 193 194 // GraphNodeEvalable impl. 195 func (n *graphNodeImportStateSub) EvalTree() EvalNode { 196 // If the Ephemeral type isn't set, then it is an error 197 if n.State.Ephemeral.Type == "" { 198 err := fmt.Errorf( 199 "import of %s didn't set type for %s", 200 n.Target.String(), n.State.ID) 201 return &EvalReturnError{Error: &err} 202 } 203 204 // DeepCopy so we're only modifying our local copy 205 state := n.State.DeepCopy() 206 207 // Build the resource info 208 info := &InstanceInfo{ 209 Id: fmt.Sprintf("%s.%s", n.Target.Type, n.Target.Name), 210 ModulePath: n.Path_, 211 Type: n.State.Ephemeral.Type, 212 } 213 214 // Key is the resource key 215 key := &ResourceStateKey{ 216 Name: n.Target.Name, 217 Type: info.Type, 218 Index: n.Target.Index, 219 } 220 221 // The eval sequence 222 var provider ResourceProvider 223 return &EvalSequence{ 224 Nodes: []EvalNode{ 225 &EvalGetProvider{ 226 Name: n.ResolvedProvider, 227 Output: &provider, 228 }, 229 &EvalRefresh{ 230 Provider: &provider, 231 State: &state, 232 Info: info, 233 Output: &state, 234 }, 235 &EvalImportStateVerify{ 236 Info: info, 237 Id: n.State.ID, 238 State: &state, 239 }, 240 &EvalWriteState{ 241 Name: key.String(), 242 ResourceType: info.Type, 243 Provider: n.ResolvedProvider, 244 State: &state, 245 }, 246 }, 247 } 248 }