github.com/mmatczuk/gohan@v0.0.0-20170206152520-30e45d9bdb69/db/db_test.go (about)

     1  // Copyright (C) 2015 NTT Innovation Institute, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package db_test
    17  
    18  import (
    19  	"os"
    20  
    21  	"github.com/cloudwan/gohan/db"
    22  	"github.com/cloudwan/gohan/db/transaction"
    23  	"github.com/cloudwan/gohan/schema"
    24  	"github.com/cloudwan/gohan/util"
    25  	. "github.com/onsi/ginkgo"
    26  	. "github.com/onsi/gomega"
    27  )
    28  
    29  var _ = Describe("Database operation test", func() {
    30  	var (
    31  		err error
    32  		ok  bool
    33  
    34  		conn   string
    35  		dbType string
    36  
    37  		manager          *schema.Manager
    38  		networkSchema    *schema.Schema
    39  		serverSchema     *schema.Schema
    40  		networkResource1 *schema.Resource
    41  		networkResource2 *schema.Resource
    42  		subnetResource   *schema.Resource
    43  		serverResource   *schema.Resource
    44  
    45  		dataStore db.DB
    46  	)
    47  
    48  	BeforeEach(func() {
    49  		manager = schema.GetManager()
    50  		Expect(manager.LoadSchemaFromFile("../etc/schema/gohan.json")).To(Succeed())
    51  	})
    52  
    53  	AfterEach(func() {
    54  		schema.ClearManager()
    55  		if os.Getenv("MYSQL_TEST") != "true" {
    56  			os.Remove(conn)
    57  		}
    58  	})
    59  
    60  	Describe("Base operations", func() {
    61  		var (
    62  			tx transaction.Transaction
    63  		)
    64  
    65  		BeforeEach(func() {
    66  			Expect(manager.LoadSchemaFromFile("../tests/test_abstract_schema.yaml")).To(Succeed())
    67  			Expect(manager.LoadSchemaFromFile("../tests/test_schema.yaml")).To(Succeed())
    68  			networkSchema, ok = manager.Schema("network")
    69  			Expect(ok).To(BeTrue())
    70  			_, ok = manager.Schema("subnet")
    71  			Expect(ok).To(BeTrue())
    72  			serverSchema, ok = manager.Schema("server")
    73  			Expect(ok).To(BeTrue())
    74  
    75  			network1 := map[string]interface{}{
    76  				"id":                "networkRed",
    77  				"name":              "NetworkRed",
    78  				"description":       "A crimson network",
    79  				"tenant_id":         "red",
    80  				"shared":            false,
    81  				"route_targets":     []string{"1000:10000", "2000:20000"},
    82  				"providor_networks": map[string]interface{}{"segmentation_id": 10, "segmentation_type": "vlan"}}
    83  			networkResource1, err = manager.LoadResource("network", network1)
    84  			Expect(err).ToNot(HaveOccurred())
    85  
    86  			network2 := map[string]interface{}{
    87  				"id":                "networkBlue",
    88  				"name":              "NetworkBlue",
    89  				"description":       "A crimson network",
    90  				"tenant_id":         "blue",
    91  				"shared":            false,
    92  				"route_targets":     []string{"1000:10000", "2000:20000"},
    93  				"providor_networks": map[string]interface{}{"segmentation_id": 10, "segmentation_type": "vlan"}}
    94  			networkResource2, err = manager.LoadResource("network", network2)
    95  			Expect(err).ToNot(HaveOccurred())
    96  
    97  			subnet := map[string]interface{}{
    98  				"id":          "subnetRed",
    99  				"name":        "SubnetRed",
   100  				"description": "A crimson subnet",
   101  				"tenant_id":   "red",
   102  				"cidr":        "10.0.0.0/24"}
   103  			subnetResource, err = manager.LoadResource("subnet", subnet)
   104  			Expect(err).ToNot(HaveOccurred())
   105  			subnetResource.SetParentID("networkRed")
   106  			Expect(subnetResource.Path()).To(Equal("/v2.0/subnets/subnetRed"))
   107  
   108  			server := map[string]interface{}{
   109  				"id":          "serverRed",
   110  				"name":        "serverRed",
   111  				"tenant_id":   "red",
   112  				"network_id":  "networkRed",
   113  				"description": "red server",
   114  				"cidr":        "10.0.0.0/24"}
   115  			serverResource, err = manager.LoadResource("server", server)
   116  			Expect(err).ToNot(HaveOccurred())
   117  		})
   118  
   119  		JustBeforeEach(func() {
   120  			os.Remove(conn)
   121  			dataStore, err = db.ConnectDB(dbType, conn, db.DefaultMaxOpenConn)
   122  			Expect(err).ToNot(HaveOccurred())
   123  
   124  			for _, s := range manager.Schemas() {
   125  				Expect(dataStore.RegisterTable(s, false)).To(Succeed())
   126  			}
   127  
   128  			tx, err = dataStore.Begin()
   129  			Expect(err).ToNot(HaveOccurred())
   130  		})
   131  
   132  		AfterEach(func() {
   133  			tx.Close()
   134  		})
   135  
   136  		Describe("Using sql", func() {
   137  			BeforeEach(func() {
   138  				if os.Getenv("MYSQL_TEST") == "true" {
   139  					conn = "root@/gohan_test"
   140  					dbType = "mysql"
   141  				} else {
   142  					conn = "./test.db"
   143  					dbType = "sqlite3"
   144  				}
   145  			})
   146  
   147  			Context("When the database is empty", func() {
   148  				It("Returns an empty list", func() {
   149  					list, num, err := tx.List(networkSchema, nil, nil)
   150  					Expect(err).ToNot(HaveOccurred())
   151  					Expect(num).To(Equal(uint64(0)))
   152  					Expect(list).To(BeEmpty())
   153  					Expect(tx.Commit()).To(Succeed())
   154  				})
   155  
   156  				It("Creates a resource", func() {
   157  					Expect(tx.Create(networkResource1)).To(Succeed())
   158  
   159  					Expect(tx.Commit()).To(Succeed())
   160  				})
   161  			})
   162  
   163  			Describe("When the database is not empty", func() {
   164  				JustBeforeEach(func() {
   165  					Expect(tx.Create(networkResource1)).To(Succeed())
   166  					Expect(tx.Create(networkResource2)).To(Succeed())
   167  					Expect(tx.Create(serverResource)).To(Succeed())
   168  					Expect(tx.Commit()).To(Succeed())
   169  					tx.Close()
   170  					tx, err = dataStore.Begin()
   171  					Expect(err).ToNot(HaveOccurred())
   172  				})
   173  
   174  				It("Returns the expected list", func() {
   175  					list, num, err := tx.List(networkSchema, nil, nil)
   176  					Expect(err).ToNot(HaveOccurred())
   177  					Expect(num).To(Equal(uint64(2)))
   178  					Expect(list).To(HaveLen(2))
   179  					Expect(list[0]).To(util.MatchAsJSON(networkResource1))
   180  					Expect(list[1]).To(util.MatchAsJSON(networkResource2))
   181  					Expect(tx.Commit()).To(Succeed())
   182  				})
   183  
   184  				It("Returns the expected list with filter", func() {
   185  					filter := map[string]interface{}{
   186  						"tenant_id": []string{"red"},
   187  					}
   188  					list, num, err := tx.List(networkSchema, filter, nil)
   189  					Expect(err).ToNot(HaveOccurred())
   190  					Expect(num).To(Equal(uint64(1)))
   191  					Expect(list).To(HaveLen(1))
   192  					Expect(list[0]).To(util.MatchAsJSON(networkResource1))
   193  					Expect(tx.Commit()).To(Succeed())
   194  				})
   195  
   196  				It("Returns the error with invalid filter", func() {
   197  					filter := map[string]interface{}{
   198  						"bad_filter": []string{"red"},
   199  					}
   200  					_, _, err := tx.List(networkSchema, filter, nil)
   201  					Expect(err).To(HaveOccurred())
   202  				})
   203  
   204  				It("Shows related resources", func() {
   205  					list, num, err := tx.List(serverSchema, nil, nil)
   206  					Expect(err).ToNot(HaveOccurred())
   207  					Expect(num).To(Equal(uint64(1)))
   208  					Expect(list).To(HaveLen(1))
   209  					Expect(list[0].Data()).To(HaveKeyWithValue("network", HaveKeyWithValue("name", networkResource1.Data()["name"])))
   210  					Expect(tx.Commit()).To(Succeed())
   211  				})
   212  
   213  				It("Fetches an existing resource", func() {
   214  					networkResourceFetched, err := tx.Fetch(networkSchema, transaction.IDFilter(networkResource1.ID()))
   215  					Expect(err).ToNot(HaveOccurred())
   216  					Expect(networkResourceFetched).To(util.MatchAsJSON(networkResource1))
   217  					Expect(tx.Commit()).To(Succeed())
   218  				})
   219  
   220  				It("Updates the resource properly", func() {
   221  					By("Not allowing to update some fields")
   222  					Expect(networkResource1.Update(map[string]interface{}{"id": "new_id"})).ToNot(Succeed())
   223  
   224  					By("Updating other fields")
   225  					Expect(networkResource1.Update(map[string]interface{}{"name": "new_name"})).To(Succeed())
   226  					Expect(tx.Update(networkResource1)).To(Succeed())
   227  					Expect(tx.Commit()).To(Succeed())
   228  				})
   229  
   230  				It("Creates a dependent resource", func() {
   231  					Expect(tx.Create(subnetResource)).To(Succeed())
   232  					Expect(tx.Commit()).To(Succeed())
   233  				})
   234  
   235  				It("Deletes the resource", func() {
   236  					Expect(tx.Delete(serverSchema, serverResource.ID())).To(Succeed())
   237  					Expect(tx.Delete(networkSchema, networkResource1.ID())).To(Succeed())
   238  					Expect(tx.Commit()).To(Succeed())
   239  				})
   240  
   241  				Context("Using StateFetch", func() {
   242  					It("Returns the defaults", func() {
   243  						beforeState, err := tx.StateFetch(networkSchema, transaction.IDFilter(networkResource1.ID()))
   244  						Expect(err).ToNot(HaveOccurred())
   245  						Expect(tx.Commit()).To(Succeed())
   246  						Expect(beforeState.ConfigVersion).To(Equal(int64(1)))
   247  						Expect(beforeState.StateVersion).To(Equal(int64(0)))
   248  						Expect(beforeState.State).To(Equal(""))
   249  						Expect(beforeState.Error).To(Equal(""))
   250  						Expect(beforeState.Monitoring).To(Equal(""))
   251  					})
   252  				})
   253  			})
   254  		})
   255  	})
   256  
   257  	Context("Initialization", func() {
   258  		BeforeEach(func() {
   259  			conn = "test.db"
   260  			dbType = "sqlite3"
   261  			Expect(manager.LoadSchemaFromFile("../tests/test_abstract_schema.yaml")).To(Succeed())
   262  			Expect(manager.LoadSchemaFromFile("../tests/test_schema.yaml")).To(Succeed())
   263  		})
   264  
   265  		It("Should initialize the database without error", func() {
   266  			Expect(db.InitDBWithSchemas(dbType, conn, false, false)).To(Succeed())
   267  		})
   268  	})
   269  
   270  	Context("Converting", func() {
   271  		BeforeEach(func() {
   272  			Expect(manager.LoadSchemaFromFile("test_data/conv_in.yaml")).To(Succeed())
   273  		})
   274  
   275  		It("Should do it properly", func() {
   276  			inDB, err := db.ConnectDB("yaml", "test_data/conv_in.yaml", db.DefaultMaxOpenConn)
   277  			Expect(err).ToNot(HaveOccurred())
   278  			defer os.Remove("test_data/conv_in.db")
   279  
   280  			db.InitDBWithSchemas("sqlite3", "test_data/conv_out.db", false, false)
   281  			outDB, err := db.ConnectDB("sqlite3", "test_data/conv_out.db", db.DefaultMaxOpenConn)
   282  			Expect(err).ToNot(HaveOccurred())
   283  			defer os.Remove("test_data/conv_out.db")
   284  
   285  			db.InitDBWithSchemas("yaml", "test_data/conv_verify.yaml", false, false)
   286  			verifyDB, err := db.ConnectDB("yaml", "test_data/conv_verify.yaml", db.DefaultMaxOpenConn)
   287  			Expect(err).ToNot(HaveOccurred())
   288  			defer os.Remove("test_data/conv_verify.yaml")
   289  
   290  			Expect(db.CopyDBResources(inDB, outDB, true)).To(Succeed())
   291  
   292  			Expect(db.CopyDBResources(outDB, verifyDB, true)).To(Succeed())
   293  
   294  			inTx, err := inDB.Begin()
   295  			Expect(err).ToNot(HaveOccurred())
   296  			defer inTx.Close()
   297  
   298  			// SQL returns different types than JSON/YAML Database
   299  			// So we need to move it back again so that DeepEqual would work correctly
   300  			verifyTx, err := verifyDB.Begin()
   301  			Expect(err).ToNot(HaveOccurred())
   302  			defer verifyTx.Close()
   303  
   304  			for _, s := range manager.OrderedSchemas() {
   305  				if s.Metadata["type"] == "metaschema" {
   306  					continue
   307  				}
   308  				resources, _, err := inTx.List(s, nil, nil)
   309  				Expect(err).ToNot(HaveOccurred())
   310  				for _, inResource := range resources {
   311  					outResource, err := verifyTx.Fetch(s, transaction.Filter{"id": inResource.ID()})
   312  					Expect(err).ToNot(HaveOccurred())
   313  					Expect(outResource).To(Equal(inResource))
   314  				}
   315  			}
   316  		})
   317  
   318  		It("Should not override existing rows", func() {
   319  			inDB, err := db.ConnectDB("yaml", "test_data/conv_in.yaml", db.DefaultMaxOpenConn)
   320  			Expect(err).ToNot(HaveOccurred())
   321  			defer os.Remove("test_data/conv_in.db")
   322  
   323  			db.InitDBWithSchemas("sqlite3", "test_data/conv_out.db", false, false)
   324  			outDB, err := db.ConnectDB("sqlite3", "test_data/conv_out.db", db.DefaultMaxOpenConn)
   325  			Expect(err).ToNot(HaveOccurred())
   326  			defer os.Remove("test_data/conv_out.db")
   327  
   328  			Expect(err).ToNot(HaveOccurred())
   329  			defer os.Remove("test_data/conv_verify.yaml")
   330  
   331  			Expect(db.CopyDBResources(inDB, outDB, false)).To(Succeed())
   332  			subnetSchema, _ := manager.Schema("subnet")
   333  
   334  			// Update some data
   335  			tx, err := outDB.Begin()
   336  			Expect(err).ToNot(HaveOccurred())
   337  			list, _, err := tx.List(subnetSchema, map[string]interface{}{
   338  				"name": "subnetRedA",
   339  			}, nil)
   340  			Expect(err).ToNot(HaveOccurred())
   341  			Expect(list).To(HaveLen(1))
   342  			subnet := list[0]
   343  			subnet.Data()["description"] = "Updated description"
   344  			err = tx.Update(subnet)
   345  			Expect(err).ToNot(HaveOccurred())
   346  			tx.Commit()
   347  			tx.Close()
   348  
   349  			Expect(db.CopyDBResources(inDB, outDB, false)).To(Succeed())
   350  			// check description of subnetRedA
   351  			tx, err = outDB.Begin()
   352  			Expect(err).ToNot(HaveOccurred())
   353  			list, _, err = tx.List(subnetSchema, map[string]interface{}{
   354  				"name": "subnetRedA",
   355  			}, nil)
   356  			Expect(err).ToNot(HaveOccurred())
   357  			Expect(list).To(HaveLen(1))
   358  			subnet = list[0]
   359  			Expect(subnet.Data()["description"]).To(Equal("Updated description"))
   360  			tx.Close()
   361  		})
   362  	})
   363  })