1654
1657
c.Assert(results.Machines, gc.HasLen, 1)
1657
func (s *clientSuite) TestMachineConfig(c *gc.C) {
1658
addrs := []instance.Address{instance.NewAddress("1.2.3.4")}
1659
hc := instance.MustParseHardware("mem=4G arch=amd64")
1660
apiParams := params.AddMachineParams{
1661
Jobs: []params.MachineJob{params.JobHostUnits},
1662
InstanceId: instance.Id("1234"),
1664
HardwareCharacteristics: hc,
1667
machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams})
1668
c.Assert(err, gc.IsNil)
1669
c.Assert(len(machines), gc.Equals, 1)
1671
machineId := machines[0].Machine
1672
machineConfig, err := s.APIState.Client().MachineConfig(machineId)
1673
c.Assert(err, gc.IsNil)
1675
envConfig, err := s.State.EnvironConfig()
1676
c.Assert(err, gc.IsNil)
1677
env, err := environs.New(envConfig)
1678
c.Assert(err, gc.IsNil)
1679
stateInfo, apiInfo, err := env.StateInfo()
1680
c.Assert(err, gc.IsNil)
1681
c.Assert(machineConfig.StateAddrs, gc.DeepEquals, stateInfo.Addrs)
1682
c.Assert(machineConfig.APIAddrs, gc.DeepEquals, apiInfo.Addrs)
1683
c.Assert(machineConfig.Tag, gc.Equals, "machine-0")
1684
caCert, _ := envConfig.CACert()
1685
c.Assert(machineConfig.CACert, gc.DeepEquals, caCert)
1686
c.Assert(machineConfig.Password, gc.Not(gc.Equals), "")
1687
c.Assert(machineConfig.Tools.URL, gc.Not(gc.Equals), "")
1688
c.Assert(machineConfig.EnvironAttrs["name"], gc.Equals, "dummyenv")
1691
func (s *clientSuite) TestMachineConfigNoArch(c *gc.C) {
1692
apiParams := params.AddMachineParams{
1693
Jobs: []params.MachineJob{params.JobHostUnits},
1694
InstanceId: instance.Id("1234"),
1697
machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams})
1698
c.Assert(err, gc.IsNil)
1699
c.Assert(len(machines), gc.Equals, 1)
1700
_, err = s.APIState.Client().MachineConfig(machines[0].Machine)
1701
c.Assert(err, gc.ErrorMatches, fmt.Sprintf("arch is not set for %q", "machine-"+machines[0].Machine))
1704
func (s *clientSuite) TestMachineConfigNoTools(c *gc.C) {
1705
s.PatchValue(&envtools.DefaultBaseURL, "")
1706
addrs := []instance.Address{instance.NewAddress("1.2.3.4")}
1707
hc := instance.MustParseHardware("mem=4G arch=amd64")
1708
apiParams := params.AddMachineParams{
1710
Jobs: []params.MachineJob{params.JobHostUnits},
1711
InstanceId: instance.Id("1234"),
1713
HardwareCharacteristics: hc,
1716
machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams})
1717
c.Assert(err, gc.IsNil)
1718
_, err = s.APIState.Client().MachineConfig(machines[0].Machine)
1719
c.Assert(err, gc.ErrorMatches, coretools.ErrNoMatches.Error())
1722
1660
func (s *clientSuite) TestProvisioningScript(c *gc.C) {
1723
1661
// Inject a machine and then call the ProvisioningScript API.
1724
1662
// The result should be the same as when calling MachineConfig,
1734
1672
c.Assert(err, gc.IsNil)
1735
1673
c.Assert(len(machines), gc.Equals, 1)
1736
1674
machineId := machines[0].Machine
1737
machineConfig, err := s.APIState.Client().MachineConfig(machineId)
1738
c.Assert(err, gc.IsNil)
1739
1675
// Call ProvisioningScript. Normally ProvisioningScript and
1740
1676
// MachineConfig are mutually exclusive; both of them will
1741
1677
// allocate a state/api password for the machine agent.
1742
script, err := s.APIState.Client().ProvisioningScript(machineId, apiParams.Nonce)
1678
script, err := s.APIState.Client().ProvisioningScript(params.ProvisioningScriptParams{
1679
MachineId: machineId,
1680
Nonce: apiParams.Nonce,
1743
1682
c.Assert(err, gc.IsNil)
1744
mcfg, err := statecmd.FinishMachineConfig(machineConfig, machineId, apiParams.Nonce, "")
1683
mcfg, err := statecmd.MachineConfig(s.State, machineId, apiParams.Nonce, "")
1745
1684
c.Assert(err, gc.IsNil)
1746
1685
cloudcfg := coreCloudinit.New()
1747
1686
err = cloudinit.ConfigureJuju(mcfg, cloudcfg)
1706
func (s *clientSuite) TestProvisioningScriptDisablePackageCommands(c *gc.C) {
1707
apiParams := params.AddMachineParams{
1708
Jobs: []params.MachineJob{params.JobHostUnits},
1709
InstanceId: instance.Id("1234"),
1711
HardwareCharacteristics: instance.MustParseHardware("arch=amd64"),
1713
machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams})
1714
c.Assert(err, gc.IsNil)
1715
c.Assert(len(machines), gc.Equals, 1)
1716
machineId := machines[0].Machine
1717
for _, disable := range []bool{false, true} {
1718
script, err := s.APIState.Client().ProvisioningScript(params.ProvisioningScriptParams{
1719
MachineId: machineId,
1720
Nonce: apiParams.Nonce,
1721
DisablePackageCommands: disable,
1723
c.Assert(err, gc.IsNil)
1724
var checker gc.Checker = jc.Contains
1726
// We disabled package commands: there should be no "apt" commands in the script.
1727
checker = gc.Not(checker)
1729
c.Assert(script, checker, "apt-get")
1767
1733
func (s *clientSuite) TestClientAuthorizeStoreOnDeployServiceSetCharmAndAddCharm(c *gc.C) {
1768
1734
store, restore := makeMockCharmStore()
1769
1735
defer restore()
1844
1811
err = client.AddCharm(curl)
1845
1812
c.Assert(err, gc.IsNil)
1847
// Verify it's in state.
1814
// Verify it's in state and it got uploaded.
1848
1815
sch, err = s.State.Charm(curl)
1849
1816
c.Assert(err, gc.IsNil)
1851
name = charm.Quote(curl.String())
1852
storageURL, err := storage.URL(name)
1853
c.Assert(err, gc.IsNil)
1855
c.Assert(sch.BundleURL().String(), gc.Equals, storageURL)
1856
// We don't care about the exact value of the hash here, just that
1858
c.Assert(sch.BundleSha256(), gc.Not(gc.Equals), "")
1860
// Verify it's added to storage.
1861
_, err = storage.Get(name)
1862
c.Assert(err, gc.IsNil)
1817
s.assertUploaded(c, storage, sch.BundleURL(), sch.BundleSha256())
1820
func (s *clientSuite) TestAddCharmConcurrently(c *gc.C) {
1821
store, restore := makeMockCharmStore()
1824
client := s.APIState.Client()
1825
curl, _ := addCharm(c, store, "wordpress")
1827
// Expect storage Put() to be called once for each goroutine
1829
ops := make(chan dummy.Operation, 500)
1831
go s.assertPutCalled(c, ops, 10)
1833
// Try adding the same charm concurrently from multiple goroutines
1834
// to test no "duplicate key errors" are reported (see lp bug
1835
// #1067979) and also at the end only one charm document is
1838
var wg sync.WaitGroup
1839
for i := 0; i < 10; i++ {
1841
go func(index int) {
1844
c.Assert(client.AddCharm(curl), gc.IsNil, gc.Commentf("goroutine %d", index))
1845
sch, err := s.State.Charm(curl)
1846
c.Assert(err, gc.IsNil, gc.Commentf("goroutine %d", index))
1847
c.Assert(sch.URL(), jc.DeepEquals, curl, gc.Commentf("goroutine %d", index))
1848
expectedName := fmt.Sprintf("%s-%d-[0-9a-f-]+", curl.Name, curl.Revision)
1849
c.Assert(getArchiveName(sch.BundleURL()), gc.Matches, expectedName)
1855
// Verify there is only a single uploaded charm remains and it
1856
// contains the correct data.
1857
sch, err := s.State.Charm(curl)
1858
c.Assert(err, gc.IsNil)
1859
storage, err := envtesting.GetEnvironStorage(s.State)
1860
c.Assert(err, gc.IsNil)
1861
uploads, err := storage.List(fmt.Sprintf("%s-%d-", curl.Name, curl.Revision))
1862
c.Assert(err, gc.IsNil)
1863
c.Assert(uploads, gc.HasLen, 1)
1864
c.Assert(getArchiveName(sch.BundleURL()), gc.Equals, uploads[0])
1865
s.assertUploaded(c, storage, sch.BundleURL(), sch.BundleSha256())
1868
func (s *clientSuite) TestAddCharmOverwritesPlaceholders(c *gc.C) {
1869
store, restore := makeMockCharmStore()
1872
client := s.APIState.Client()
1873
curl, _ := addCharm(c, store, "wordpress")
1875
// Add a placeholder with the same charm URL.
1876
err := s.State.AddStoreCharmPlaceholder(curl)
1877
c.Assert(err, gc.IsNil)
1878
_, err = s.State.Charm(curl)
1879
c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
1881
// Now try to add the charm, which will convert the placeholder to
1883
err = client.AddCharm(curl)
1884
c.Assert(err, gc.IsNil)
1886
// Make sure the document's flags were reset as expected.
1887
sch, err := s.State.Charm(curl)
1888
c.Assert(err, gc.IsNil)
1889
c.Assert(sch.URL(), jc.DeepEquals, curl)
1890
c.Assert(sch.IsPlaceholder(), jc.IsFalse)
1891
c.Assert(sch.IsUploaded(), jc.IsTrue)
1894
func (s *clientSuite) TestCharmArchiveName(c *gc.C) {
1895
for rev, name := range []string{"Foo", "bar", "wordpress", "mysql"} {
1896
archiveFormat := fmt.Sprintf("%s-%d-[0-9a-f-]+", name, rev)
1897
archiveName, err := client.CharmArchiveName(name, rev)
1898
c.Check(err, gc.IsNil)
1899
c.Check(archiveName, gc.Matches, archiveFormat)
1903
func (s *clientSuite) assertPutCalled(c *gc.C, ops chan dummy.Operation, numCalls int) {
1906
case op, ok := <-ops:
1910
if op, ok := op.(dummy.OpPutFile); ok {
1912
if calls > numCalls {
1913
c.Fatalf("storage Put() called %d times, expected %d times", calls, numCalls)
1916
nameFormat := "[0-9a-z-]+-[0-9]+-[0-9a-f-]+"
1917
c.Assert(op.FileName, gc.Matches, nameFormat)
1919
case <-time.After(coretesting.LongWait):
1920
c.Fatalf("timed out while waiting for a storage Put() calls")
1925
func (s *clientSuite) assertUploaded(c *gc.C, storage storage.Storage, bundleURL *url.URL, expectedSHA256 string) {
1926
archiveName := getArchiveName(bundleURL)
1927
reader, err := storage.Get(archiveName)
1928
c.Assert(err, gc.IsNil)
1929
defer reader.Close()
1930
downloadedSHA256, _, err := utils.ReadSHA256(reader)
1931
c.Assert(err, gc.IsNil)
1932
c.Assert(downloadedSHA256, gc.Equals, expectedSHA256)
1935
func getArchiveName(bundleURL *url.URL) string {
1936
return strings.TrimPrefix(bundleURL.RequestURI(), "/dummyenv/private/")