github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/migration/migration_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package migration_test
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"net/url"
    12  
    13  	"github.com/juju/errors"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils"
    16  	"github.com/juju/version"
    17  	gc "gopkg.in/check.v1"
    18  	"gopkg.in/juju/charm.v6-unstable"
    19  
    20  	"github.com/juju/juju/core/description"
    21  	"github.com/juju/juju/migration"
    22  	"github.com/juju/juju/provider/dummy"
    23  	_ "github.com/juju/juju/provider/dummy"
    24  	statetesting "github.com/juju/juju/state/testing"
    25  	"github.com/juju/juju/testing"
    26  	"github.com/juju/juju/tools"
    27  )
    28  
    29  type ImportSuite struct {
    30  	statetesting.StateSuite
    31  }
    32  
    33  var _ = gc.Suite(&ImportSuite{})
    34  
    35  func (s *ImportSuite) SetUpTest(c *gc.C) {
    36  	// Specify the config to use for the controller model before calling
    37  	// SetUpTest of the StateSuite, otherwise we get testing.ModelConfig(c).
    38  	// The default provider type specified in the testing.ModelConfig function
    39  	// is one that isn't registered as a valid provider. For our tests here we
    40  	// need a real registered provider, so we use the dummy provider.
    41  	// NOTE: make a better test provider.
    42  	s.InitialConfig = testing.CustomModelConfig(c, dummy.SampleConfig())
    43  	s.StateSuite.SetUpTest(c)
    44  }
    45  
    46  func (s *ImportSuite) TestBadBytes(c *gc.C) {
    47  	bytes := []byte("not a model")
    48  	model, st, err := migration.ImportModel(s.State, bytes)
    49  	c.Check(st, gc.IsNil)
    50  	c.Check(model, gc.IsNil)
    51  	c.Assert(err, gc.ErrorMatches, "yaml: unmarshal errors:\n.*")
    52  }
    53  
    54  func (s *ImportSuite) TestImportModel(c *gc.C) {
    55  	model, err := s.State.Export()
    56  	c.Check(err, jc.ErrorIsNil)
    57  
    58  	// Update the config values in the exported model for different values for
    59  	// "state-port", "api-port", and "ca-cert". Also give the model a new UUID
    60  	// and name so we can import it nicely.
    61  	uuid := utils.MustNewUUID().String()
    62  	model.UpdateConfig(map[string]interface{}{
    63  		"name": "new-model",
    64  		"uuid": uuid,
    65  	})
    66  
    67  	bytes, err := description.Serialize(model)
    68  	c.Check(err, jc.ErrorIsNil)
    69  
    70  	dbModel, dbState, err := migration.ImportModel(s.State, bytes)
    71  	c.Check(err, jc.ErrorIsNil)
    72  	defer dbState.Close()
    73  
    74  	dbConfig, err := dbModel.Config()
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	c.Assert(dbConfig.UUID(), gc.Equals, uuid)
    77  	c.Assert(dbConfig.Name(), gc.Equals, "new-model")
    78  }
    79  
    80  func (s *ImportSuite) TestUploadBinariesConfigValidate(c *gc.C) {
    81  	type T migration.UploadBinariesConfig // alias for brevity
    82  
    83  	check := func(modify func(*T), missing string) {
    84  		config := T{
    85  			CharmDownloader: struct{ migration.CharmDownloader }{},
    86  			CharmUploader:   struct{ migration.CharmUploader }{},
    87  			ToolsDownloader: struct{ migration.ToolsDownloader }{},
    88  			ToolsUploader:   struct{ migration.ToolsUploader }{},
    89  		}
    90  		modify(&config)
    91  		realConfig := migration.UploadBinariesConfig(config)
    92  		c.Check(realConfig.Validate(), gc.ErrorMatches, fmt.Sprintf("missing %s not valid", missing))
    93  	}
    94  
    95  	check(func(c *T) { c.CharmDownloader = nil }, "CharmDownloader")
    96  	check(func(c *T) { c.CharmUploader = nil }, "CharmUploader")
    97  	check(func(c *T) { c.ToolsDownloader = nil }, "ToolsDownloader")
    98  	check(func(c *T) { c.ToolsUploader = nil }, "ToolsUploader")
    99  }
   100  
   101  func (s *ImportSuite) TestBinariesMigration(c *gc.C) {
   102  	downloader := &fakeDownloader{}
   103  	uploader := &fakeUploader{
   104  		charms: make(map[string]string),
   105  		tools:  make(map[version.Binary]string),
   106  	}
   107  
   108  	toolsMap := map[version.Binary]string{
   109  		version.MustParseBinary("2.1.0-trusty-amd64"): "/tools/0",
   110  		version.MustParseBinary("2.0.0-xenial-amd64"): "/tools/1",
   111  	}
   112  	config := migration.UploadBinariesConfig{
   113  		Charms:          []string{"local:trusty/magic", "cs:trusty/postgresql-42"},
   114  		CharmDownloader: downloader,
   115  		CharmUploader:   uploader,
   116  		Tools:           toolsMap,
   117  		ToolsDownloader: downloader,
   118  		ToolsUploader:   uploader,
   119  	}
   120  	err := migration.UploadBinaries(config)
   121  	c.Assert(err, jc.ErrorIsNil)
   122  
   123  	c.Assert(downloader.charms, jc.DeepEquals, []string{
   124  		"local:trusty/magic",
   125  		"cs:trusty/postgresql-42",
   126  	})
   127  	c.Assert(uploader.charms, jc.DeepEquals, map[string]string{
   128  		"local:trusty/magic":      "local:trusty/magic content",
   129  		"cs:trusty/postgresql-42": "cs:trusty/postgresql-42 content",
   130  	})
   131  	c.Assert(downloader.uris, jc.SameContents, []string{
   132  		"/tools/0",
   133  		"/tools/1",
   134  	})
   135  	c.Assert(uploader.tools, jc.DeepEquals, toolsMap)
   136  }
   137  
   138  type fakeDownloader struct {
   139  	charms []string
   140  	uris   []string
   141  }
   142  
   143  func (d *fakeDownloader) OpenCharm(curl *charm.URL) (io.ReadCloser, error) {
   144  	urlStr := curl.String()
   145  	d.charms = append(d.charms, urlStr)
   146  	// Return the charm URL string as the fake charm content
   147  	return ioutil.NopCloser(bytes.NewReader([]byte(urlStr + " content"))), nil
   148  }
   149  
   150  func (d *fakeDownloader) OpenURI(uri string, query url.Values) (io.ReadCloser, error) {
   151  	if query != nil {
   152  		panic("query should be empty")
   153  	}
   154  	d.uris = append(d.uris, uri)
   155  	// Return the URI string as fake content
   156  	return ioutil.NopCloser(bytes.NewReader([]byte(uri))), nil
   157  }
   158  
   159  type fakeUploader struct {
   160  	tools  map[version.Binary]string
   161  	charms map[string]string
   162  }
   163  
   164  func (f *fakeUploader) UploadTools(r io.ReadSeeker, v version.Binary, _ ...string) (tools.List, error) {
   165  	data, err := ioutil.ReadAll(r)
   166  	if err != nil {
   167  		return nil, errors.Trace(err)
   168  	}
   169  	f.tools[v] = string(data)
   170  	return tools.List{&tools.Tools{Version: v}}, nil
   171  }
   172  
   173  func (f *fakeUploader) UploadCharm(u *charm.URL, r io.ReadSeeker) (*charm.URL, error) {
   174  	data, err := ioutil.ReadAll(r)
   175  	if err != nil {
   176  		return nil, errors.Trace(err)
   177  	}
   178  
   179  	f.charms[u.String()] = string(data)
   180  	return u, nil
   181  }
   182  
   183  type ExportSuite struct {
   184  	statetesting.StateSuite
   185  }
   186  
   187  var _ = gc.Suite(&ExportSuite{})
   188  
   189  func (s *ExportSuite) TestExportModel(c *gc.C) {
   190  	bytes, err := migration.ExportModel(s.State)
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	// The bytes must be a valid model.
   193  	_, err = description.Deserialize(bytes)
   194  	c.Assert(err, jc.ErrorIsNil)
   195  }