1package statefile
2
3import (
4	"fmt"
5	"log"
6
7	"github.com/mitchellh/copystructure"
8)
9
10// upgradeStateV1ToV2 is used to upgrade a V1 state representation
11// into a V2 state representation
12func upgradeStateV1ToV2(old *stateV1) (*stateV2, error) {
13	log.Printf("[TRACE] statefile.Read: upgrading format from v1 to v2")
14	if old == nil {
15		return nil, nil
16	}
17
18	remote, err := old.Remote.upgradeToV2()
19	if err != nil {
20		return nil, fmt.Errorf("Error upgrading State V1: %v", err)
21	}
22
23	modules := make([]*moduleStateV2, len(old.Modules))
24	for i, module := range old.Modules {
25		upgraded, err := module.upgradeToV2()
26		if err != nil {
27			return nil, fmt.Errorf("Error upgrading State V1: %v", err)
28		}
29		modules[i] = upgraded
30	}
31	if len(modules) == 0 {
32		modules = nil
33	}
34
35	newState := &stateV2{
36		Version: 2,
37		Serial:  old.Serial,
38		Remote:  remote,
39		Modules: modules,
40	}
41
42	return newState, nil
43}
44
45func (old *remoteStateV1) upgradeToV2() (*remoteStateV2, error) {
46	if old == nil {
47		return nil, nil
48	}
49
50	config, err := copystructure.Copy(old.Config)
51	if err != nil {
52		return nil, fmt.Errorf("Error upgrading RemoteState V1: %v", err)
53	}
54
55	return &remoteStateV2{
56		Type:   old.Type,
57		Config: config.(map[string]string),
58	}, nil
59}
60
61func (old *moduleStateV1) upgradeToV2() (*moduleStateV2, error) {
62	if old == nil {
63		return nil, nil
64	}
65
66	pathRaw, err := copystructure.Copy(old.Path)
67	if err != nil {
68		return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err)
69	}
70	path, ok := pathRaw.([]string)
71	if !ok {
72		return nil, fmt.Errorf("Error upgrading ModuleState V1: path is not a list of strings")
73	}
74	if len(path) == 0 {
75		// We found some V1 states with a nil path. Assume root.
76		path = []string{"root"}
77	}
78
79	// Outputs needs upgrading to use the new structure
80	outputs := make(map[string]*outputStateV2)
81	for key, output := range old.Outputs {
82		outputs[key] = &outputStateV2{
83			Type:      "string",
84			Value:     output,
85			Sensitive: false,
86		}
87	}
88
89	resources := make(map[string]*resourceStateV2)
90	for key, oldResource := range old.Resources {
91		upgraded, err := oldResource.upgradeToV2()
92		if err != nil {
93			return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err)
94		}
95		resources[key] = upgraded
96	}
97
98	dependencies, err := copystructure.Copy(old.Dependencies)
99	if err != nil {
100		return nil, fmt.Errorf("Error upgrading ModuleState V1: %v", err)
101	}
102
103	return &moduleStateV2{
104		Path:         path,
105		Outputs:      outputs,
106		Resources:    resources,
107		Dependencies: dependencies.([]string),
108	}, nil
109}
110
111func (old *resourceStateV1) upgradeToV2() (*resourceStateV2, error) {
112	if old == nil {
113		return nil, nil
114	}
115
116	dependencies, err := copystructure.Copy(old.Dependencies)
117	if err != nil {
118		return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err)
119	}
120
121	primary, err := old.Primary.upgradeToV2()
122	if err != nil {
123		return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err)
124	}
125
126	deposed := make([]*instanceStateV2, len(old.Deposed))
127	for i, v := range old.Deposed {
128		upgraded, err := v.upgradeToV2()
129		if err != nil {
130			return nil, fmt.Errorf("Error upgrading ResourceState V1: %v", err)
131		}
132		deposed[i] = upgraded
133	}
134	if len(deposed) == 0 {
135		deposed = nil
136	}
137
138	return &resourceStateV2{
139		Type:         old.Type,
140		Dependencies: dependencies.([]string),
141		Primary:      primary,
142		Deposed:      deposed,
143		Provider:     old.Provider,
144	}, nil
145}
146
147func (old *instanceStateV1) upgradeToV2() (*instanceStateV2, error) {
148	if old == nil {
149		return nil, nil
150	}
151
152	attributes, err := copystructure.Copy(old.Attributes)
153	if err != nil {
154		return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err)
155	}
156
157	meta, err := copystructure.Copy(old.Meta)
158	if err != nil {
159		return nil, fmt.Errorf("Error upgrading InstanceState V1: %v", err)
160	}
161
162	newMeta := make(map[string]interface{})
163	for k, v := range meta.(map[string]string) {
164		newMeta[k] = v
165	}
166
167	return &instanceStateV2{
168		ID:         old.ID,
169		Attributes: attributes.(map[string]string),
170		Meta:       newMeta,
171	}, nil
172}
173