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