github.com/codeherentuk/terraform@v0.11.12-beta1/backend/remote-state/swift/backend_test.go (about) 1 package swift 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "testing" 8 "time" 9 10 "github.com/gophercloud/gophercloud" 11 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers" 12 "github.com/gophercloud/gophercloud/openstack/objectstorage/v1/objects" 13 "github.com/gophercloud/gophercloud/pagination" 14 "github.com/hashicorp/terraform/backend" 15 "github.com/hashicorp/terraform/state/remote" 16 "github.com/hashicorp/terraform/terraform" 17 ) 18 19 // verify that we are doing ACC tests or the Swift tests specifically 20 func testACC(t *testing.T) { 21 skip := os.Getenv("TF_ACC") == "" && os.Getenv("TF_SWIFT_TEST") == "" 22 if skip { 23 t.Log("swift backend tests require setting TF_ACC or TF_SWIFT_TEST") 24 t.Skip() 25 } 26 t.Log("swift backend acceptance tests enabled") 27 } 28 29 func TestBackend_impl(t *testing.T) { 30 var _ backend.Backend = new(Backend) 31 } 32 33 func testAccPreCheck(t *testing.T) { 34 v := os.Getenv("OS_AUTH_URL") 35 if v == "" { 36 t.Fatal("OS_AUTH_URL must be set for acceptance tests") 37 } 38 } 39 40 func TestBackendConfig(t *testing.T) { 41 testACC(t) 42 43 // Build config 44 config := map[string]interface{}{ 45 "archive_container": "test-tfstate-archive", 46 "container": "test-tfstate", 47 } 48 49 b := backend.TestBackendConfig(t, New(), config).(*Backend) 50 51 if b.container != "test-tfstate" { 52 t.Fatal("Incorrect path was provided.") 53 } 54 if b.archiveContainer != "test-tfstate-archive" { 55 t.Fatal("Incorrect archivepath was provided.") 56 } 57 } 58 59 func TestBackend(t *testing.T) { 60 testACC(t) 61 62 container := fmt.Sprintf("terraform-state-swift-test-%x", time.Now().Unix()) 63 64 b := backend.TestBackendConfig(t, New(), map[string]interface{}{ 65 "container": container, 66 }).(*Backend) 67 68 defer deleteSwiftContainer(t, b.client, container) 69 70 backend.TestBackendStates(t, b) 71 } 72 73 func TestBackendPath(t *testing.T) { 74 testACC(t) 75 76 path := fmt.Sprintf("terraform-state-swift-test-%x", time.Now().Unix()) 77 t.Logf("[DEBUG] Generating backend config") 78 b := backend.TestBackendConfig(t, New(), map[string]interface{}{ 79 "path": path, 80 }).(*Backend) 81 t.Logf("[DEBUG] Backend configured") 82 83 defer deleteSwiftContainer(t, b.client, path) 84 85 t.Logf("[DEBUG] Testing Backend") 86 87 // Generate some state 88 state1 := terraform.NewState() 89 // state1Lineage := state1.Lineage 90 t.Logf("state1 lineage = %s, serial = %d", state1.Lineage, state1.Serial) 91 92 // RemoteClient to test with 93 client := &RemoteClient{ 94 client: b.client, 95 archive: b.archive, 96 archiveContainer: b.archiveContainer, 97 container: b.container, 98 } 99 100 stateMgr := &remote.State{Client: client} 101 stateMgr.WriteState(state1) 102 if err := stateMgr.PersistState(); err != nil { 103 t.Fatal(err) 104 } 105 106 if err := stateMgr.RefreshState(); err != nil { 107 t.Fatal(err) 108 } 109 110 // Add some state 111 state1.AddModuleState(&terraform.ModuleState{ 112 Path: []string{"root"}, 113 Outputs: map[string]*terraform.OutputState{ 114 "bar": &terraform.OutputState{ 115 Type: "string", 116 Sensitive: false, 117 Value: "baz", 118 }, 119 }, 120 }) 121 stateMgr.WriteState(state1) 122 if err := stateMgr.PersistState(); err != nil { 123 t.Fatal(err) 124 } 125 126 } 127 128 func TestBackendArchive(t *testing.T) { 129 testACC(t) 130 131 container := fmt.Sprintf("terraform-state-swift-test-%x", time.Now().Unix()) 132 archiveContainer := fmt.Sprintf("%s_archive", container) 133 134 b := backend.TestBackendConfig(t, New(), map[string]interface{}{ 135 "archive_container": archiveContainer, 136 "container": container, 137 }).(*Backend) 138 139 defer deleteSwiftContainer(t, b.client, container) 140 defer deleteSwiftContainer(t, b.client, archiveContainer) 141 142 // Generate some state 143 state1 := terraform.NewState() 144 // state1Lineage := state1.Lineage 145 t.Logf("state1 lineage = %s, serial = %d", state1.Lineage, state1.Serial) 146 147 // RemoteClient to test with 148 client := &RemoteClient{ 149 client: b.client, 150 archive: b.archive, 151 archiveContainer: b.archiveContainer, 152 container: b.container, 153 } 154 155 stateMgr := &remote.State{Client: client} 156 stateMgr.WriteState(state1) 157 if err := stateMgr.PersistState(); err != nil { 158 t.Fatal(err) 159 } 160 161 if err := stateMgr.RefreshState(); err != nil { 162 t.Fatal(err) 163 } 164 165 // Add some state 166 state1.AddModuleState(&terraform.ModuleState{ 167 Path: []string{"root"}, 168 Outputs: map[string]*terraform.OutputState{ 169 "bar": &terraform.OutputState{ 170 Type: "string", 171 Sensitive: false, 172 Value: "baz", 173 }, 174 }, 175 }) 176 stateMgr.WriteState(state1) 177 if err := stateMgr.PersistState(); err != nil { 178 t.Fatal(err) 179 } 180 181 archiveObjects := getSwiftObjectNames(t, b.client, archiveContainer) 182 t.Logf("archiveObjects len = %d. Contents = %+v", len(archiveObjects), archiveObjects) 183 if len(archiveObjects) != 1 { 184 t.Fatalf("Invalid number of archive objects. Expected 1, got %d", len(archiveObjects)) 185 } 186 187 // Download archive state to validate 188 archiveData := downloadSwiftObject(t, b.client, archiveContainer, archiveObjects[0]) 189 t.Logf("Archive data downloaded... Looks like: %+v", archiveData) 190 archiveState, err := terraform.ReadState(archiveData) 191 if err != nil { 192 t.Fatalf("Error Reading State: %s", err) 193 } 194 195 t.Logf("Archive state lineage = %s, serial = %d, lineage match = %t", archiveState.Lineage, archiveState.Serial, stateMgr.State().SameLineage(archiveState)) 196 if !stateMgr.State().SameLineage(archiveState) { 197 t.Fatal("Got a different lineage") 198 } 199 200 } 201 202 // Helper function to download an object in a Swift container 203 func downloadSwiftObject(t *testing.T, osClient *gophercloud.ServiceClient, container, object string) (data io.Reader) { 204 t.Logf("Attempting to download object %s from container %s", object, container) 205 res := objects.Download(osClient, container, object, nil) 206 if res.Err != nil { 207 t.Fatalf("Error downloading object: %s", res.Err) 208 } 209 data = res.Body 210 return 211 } 212 213 // Helper function to get a list of objects in a Swift container 214 func getSwiftObjectNames(t *testing.T, osClient *gophercloud.ServiceClient, container string) (objectNames []string) { 215 _ = objects.List(osClient, container, nil).EachPage(func(page pagination.Page) (bool, error) { 216 // Get a slice of object names 217 names, err := objects.ExtractNames(page) 218 if err != nil { 219 t.Fatalf("Error extracting object names from page: %s", err) 220 } 221 for _, object := range names { 222 objectNames = append(objectNames, object) 223 } 224 225 return true, nil 226 }) 227 return 228 } 229 230 // Helper function to delete Swift container 231 func deleteSwiftContainer(t *testing.T, osClient *gophercloud.ServiceClient, container string) { 232 warning := "WARNING: Failed to delete the test Swift container. It may have been left in your Openstack account and may incur storage charges. (error was %s)" 233 234 // Remove any objects 235 deleteSwiftObjects(t, osClient, container) 236 237 // Delete the container 238 deleteResult := containers.Delete(osClient, container) 239 if deleteResult.Err != nil { 240 if _, ok := deleteResult.Err.(gophercloud.ErrDefault404); !ok { 241 t.Fatalf(warning, deleteResult.Err) 242 } 243 } 244 } 245 246 // Helper function to delete Swift objects within a container 247 func deleteSwiftObjects(t *testing.T, osClient *gophercloud.ServiceClient, container string) { 248 // Get a slice of object names 249 objectNames := getSwiftObjectNames(t, osClient, container) 250 251 for _, object := range objectNames { 252 result := objects.Delete(osClient, container, object, nil) 253 if result.Err != nil { 254 t.Fatalf("Error deleting object %s from container %s: %s", object, container, result.Err) 255 } 256 } 257 258 }