~ubuntu-branches/ubuntu/trusty/juju-core/trusty-proposed

« back to all changes in this revision

Viewing changes to src/launchpad.net/juju-core/state/apiserver/client/client_test.go

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-02-03 09:22:46 UTC
  • mfrom: (1.1.17)
  • Revision ID: package-import@ubuntu.com-20140203092246-e03vg402vztzo4qa
Tags: 1.17.2-0ubuntu1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
        "net/url"
9
9
        "strconv"
10
10
        "strings"
 
11
        "sync"
 
12
        "time"
11
13
 
12
14
        gc "launchpad.net/gocheck"
13
15
 
15
17
        coreCloudinit "launchpad.net/juju-core/cloudinit"
16
18
        "launchpad.net/juju-core/cloudinit/sshinit"
17
19
        "launchpad.net/juju-core/constraints"
18
 
        "launchpad.net/juju-core/environs"
19
20
        "launchpad.net/juju-core/environs/cloudinit"
20
21
        "launchpad.net/juju-core/environs/config"
21
 
        envtools "launchpad.net/juju-core/environs/tools"
 
22
        "launchpad.net/juju-core/environs/storage"
 
23
        envtesting "launchpad.net/juju-core/environs/testing"
22
24
        "launchpad.net/juju-core/errors"
23
25
        "launchpad.net/juju-core/instance"
 
26
        "launchpad.net/juju-core/provider/dummy"
24
27
        "launchpad.net/juju-core/state"
25
28
        "launchpad.net/juju-core/state/api"
26
29
        "launchpad.net/juju-core/state/api/params"
28
31
        "launchpad.net/juju-core/state/statecmd"
29
32
        coretesting "launchpad.net/juju-core/testing"
30
33
        jc "launchpad.net/juju-core/testing/checkers"
31
 
        coretools "launchpad.net/juju-core/tools"
 
34
        "launchpad.net/juju-core/utils"
32
35
        "launchpad.net/juju-core/version"
33
36
)
34
37
 
504
507
}
505
508
 
506
509
func (s *clientSuite) setupDestroyMachinesTest(c *gc.C) (*state.Machine, *state.Machine, *state.Machine, *state.Unit) {
507
 
        m0, err := s.State.AddMachine("quantal", state.JobHostUnits)
 
510
        m0, err := s.State.AddMachine("quantal", state.JobManageEnviron)
508
511
        c.Assert(err, gc.IsNil)
509
 
        m1, err := s.State.AddMachine("quantal", state.JobManageEnviron)
 
512
        m1, err := s.State.AddMachine("quantal", state.JobHostUnits)
510
513
        c.Assert(err, gc.IsNil)
511
514
        m2, err := s.State.AddMachine("quantal", state.JobHostUnits)
512
515
        c.Assert(err, gc.IsNil)
515
518
        wordpress := s.AddTestingService(c, "wordpress", sch)
516
519
        u, err := wordpress.AddUnit()
517
520
        c.Assert(err, gc.IsNil)
518
 
        err = u.AssignToMachine(m0)
 
521
        err = u.AssignToMachine(m1)
519
522
        c.Assert(err, gc.IsNil)
520
523
 
521
524
        return m0, m1, m2, u
525
528
        m0, m1, m2, u := s.setupDestroyMachinesTest(c)
526
529
 
527
530
        err := s.APIState.Client().DestroyMachines("0", "1", "2")
528
 
        c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 has unit "wordpress/0" assigned; machine 1 is required by the environment`)
 
531
        c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the environment; machine 1 has unit "wordpress/0" assigned`)
529
532
        assertLife(c, m0, state.Alive)
530
533
        assertLife(c, m1, state.Alive)
531
534
        assertLife(c, m2, state.Dying)
533
536
        err = u.UnassignFromMachine()
534
537
        c.Assert(err, gc.IsNil)
535
538
        err = s.APIState.Client().DestroyMachines("0", "1", "2")
536
 
        c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 1 is required by the environment`)
537
 
        assertLife(c, m0, state.Dying)
538
 
        assertLife(c, m1, state.Alive)
 
539
        c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the environment`)
 
540
        assertLife(c, m0, state.Alive)
 
541
        assertLife(c, m1, state.Dying)
539
542
        assertLife(c, m2, state.Dying)
540
543
}
541
544
 
543
546
        m0, m1, m2, u := s.setupDestroyMachinesTest(c)
544
547
 
545
548
        err := s.APIState.Client().ForceDestroyMachines("0", "1", "2")
546
 
        c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 1 is required by the environment`)
 
549
        c.Assert(err, gc.ErrorMatches, `some machines were not destroyed: machine 0 is required by the environment`)
547
550
        assertLife(c, m0, state.Alive)
548
551
        assertLife(c, m1, state.Alive)
549
552
        assertLife(c, m2, state.Alive)
551
554
 
552
555
        err = s.State.Cleanup()
553
556
        c.Assert(err, gc.IsNil)
554
 
        assertLife(c, m0, state.Dead)
555
 
        assertLife(c, m1, state.Alive)
 
557
        assertLife(c, m0, state.Alive)
 
558
        assertLife(c, m1, state.Dead)
556
559
        assertLife(c, m2, state.Dead)
557
560
        assertRemoved(c, u)
558
561
}
1654
1657
        c.Assert(results.Machines, gc.HasLen, 1)
1655
1658
}
1656
1659
 
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"),
1663
 
                Nonce:      "foo",
1664
 
                HardwareCharacteristics: hc,
1665
 
                Addrs: addrs,
1666
 
        }
1667
 
        machines, err := s.APIState.Client().AddMachines([]params.AddMachineParams{apiParams})
1668
 
        c.Assert(err, gc.IsNil)
1669
 
        c.Assert(len(machines), gc.Equals, 1)
1670
 
 
1671
 
        machineId := machines[0].Machine
1672
 
        machineConfig, err := s.APIState.Client().MachineConfig(machineId)
1673
 
        c.Assert(err, gc.IsNil)
1674
 
 
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")
1689
 
}
1690
 
 
1691
 
func (s *clientSuite) TestMachineConfigNoArch(c *gc.C) {
1692
 
        apiParams := params.AddMachineParams{
1693
 
                Jobs:       []params.MachineJob{params.JobHostUnits},
1694
 
                InstanceId: instance.Id("1234"),
1695
 
                Nonce:      "foo",
1696
 
        }
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))
1702
 
}
1703
 
 
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{
1709
 
                Series:     "quantal",
1710
 
                Jobs:       []params.MachineJob{params.JobHostUnits},
1711
 
                InstanceId: instance.Id("1234"),
1712
 
                Nonce:      "foo",
1713
 
                HardwareCharacteristics: hc,
1714
 
                Addrs: addrs,
1715
 
        }
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())
1720
 
}
1721
 
 
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,
 
1681
        })
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)
1764
1703
        }
1765
1704
}
1766
1705
 
 
1706
func (s *clientSuite) TestProvisioningScriptDisablePackageCommands(c *gc.C) {
 
1707
        apiParams := params.AddMachineParams{
 
1708
                Jobs:       []params.MachineJob{params.JobHostUnits},
 
1709
                InstanceId: instance.Id("1234"),
 
1710
                Nonce:      "foo",
 
1711
                HardwareCharacteristics: instance.MustParseHardware("arch=amd64"),
 
1712
        }
 
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,
 
1722
                })
 
1723
                c.Assert(err, gc.IsNil)
 
1724
                var checker gc.Checker = jc.Contains
 
1725
                if disable {
 
1726
                        // We disabled package commands: there should be no "apt" commands in the script.
 
1727
                        checker = gc.Not(checker)
 
1728
                }
 
1729
                c.Assert(script, checker, "apt-get")
 
1730
        }
 
1731
}
 
1732
 
1767
1733
func (s *clientSuite) TestClientAuthorizeStoreOnDeployServiceSetCharmAndAddCharm(c *gc.C) {
1768
1734
        store, restore := makeMockCharmStore()
1769
1735
        defer restore()
1827
1793
        bundleURL, err := url.Parse("http://bundles.testing.invalid/" + ident)
1828
1794
        c.Assert(err, gc.IsNil)
1829
1795
        sch, err := s.State.AddCharm(charmDir, curl, bundleURL, ident+"-sha256")
 
1796
        c.Assert(err, gc.IsNil)
1830
1797
 
1831
1798
        name := charm.Quote(sch.URL().String())
1832
1799
        storage := s.Conn.Environ.Storage()
1844
1811
        err = client.AddCharm(curl)
1845
1812
        c.Assert(err, gc.IsNil)
1846
1813
 
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)
1850
 
 
1851
 
        name = charm.Quote(curl.String())
1852
 
        storageURL, err := storage.URL(name)
1853
 
        c.Assert(err, gc.IsNil)
1854
 
 
1855
 
        c.Assert(sch.BundleURL().String(), gc.Equals, storageURL)
1856
 
        // We don't care about the exact value of the hash here, just that
1857
 
        // it's set.
1858
 
        c.Assert(sch.BundleSha256(), gc.Not(gc.Equals), "")
1859
 
 
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())
 
1818
}
 
1819
 
 
1820
func (s *clientSuite) TestAddCharmConcurrently(c *gc.C) {
 
1821
        store, restore := makeMockCharmStore()
 
1822
        defer restore()
 
1823
 
 
1824
        client := s.APIState.Client()
 
1825
        curl, _ := addCharm(c, store, "wordpress")
 
1826
 
 
1827
        // Expect storage Put() to be called once for each goroutine
 
1828
        // below.
 
1829
        ops := make(chan dummy.Operation, 500)
 
1830
        dummy.Listen(ops)
 
1831
        go s.assertPutCalled(c, ops, 10)
 
1832
 
 
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
 
1836
        // created.
 
1837
 
 
1838
        var wg sync.WaitGroup
 
1839
        for i := 0; i < 10; i++ {
 
1840
                wg.Add(1)
 
1841
                go func(index int) {
 
1842
                        defer wg.Done()
 
1843
 
 
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)
 
1850
                }(i)
 
1851
        }
 
1852
        wg.Wait()
 
1853
        close(ops)
 
1854
 
 
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())
 
1866
}
 
1867
 
 
1868
func (s *clientSuite) TestAddCharmOverwritesPlaceholders(c *gc.C) {
 
1869
        store, restore := makeMockCharmStore()
 
1870
        defer restore()
 
1871
 
 
1872
        client := s.APIState.Client()
 
1873
        curl, _ := addCharm(c, store, "wordpress")
 
1874
 
 
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)
 
1880
 
 
1881
        // Now try to add the charm, which will convert the placeholder to
 
1882
        // a pending charm.
 
1883
        err = client.AddCharm(curl)
 
1884
        c.Assert(err, gc.IsNil)
 
1885
 
 
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)
 
1892
}
 
1893
 
 
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)
 
1900
        }
 
1901
}
 
1902
 
 
1903
func (s *clientSuite) assertPutCalled(c *gc.C, ops chan dummy.Operation, numCalls int) {
 
1904
        calls := 0
 
1905
        select {
 
1906
        case op, ok := <-ops:
 
1907
                if !ok {
 
1908
                        return
 
1909
                }
 
1910
                if op, ok := op.(dummy.OpPutFile); ok {
 
1911
                        calls++
 
1912
                        if calls > numCalls {
 
1913
                                c.Fatalf("storage Put() called %d times, expected %d times", calls, numCalls)
 
1914
                                return
 
1915
                        }
 
1916
                        nameFormat := "[0-9a-z-]+-[0-9]+-[0-9a-f-]+"
 
1917
                        c.Assert(op.FileName, gc.Matches, nameFormat)
 
1918
                }
 
1919
        case <-time.After(coretesting.LongWait):
 
1920
                c.Fatalf("timed out while waiting for a storage Put() calls")
 
1921
                return
 
1922
        }
 
1923
}
 
1924
 
 
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)
 
1933
}
 
1934
 
 
1935
func getArchiveName(bundleURL *url.URL) string {
 
1936
        return strings.TrimPrefix(bundleURL.RequestURI(), "/dummyenv/private/")
1863
1937
}