diff -Nru juju-core-1-1.25.4/debian/changelog juju-core-1-1.25.5/debian/changelog --- juju-core-1-1.25.4/debian/changelog 2016-04-07 19:09:30.000000000 +0000 +++ juju-core-1-1.25.5/debian/changelog 2016-04-15 00:07:21.000000000 +0000 @@ -1,3 +1,11 @@ +juju-core-1 (1.25.5-0ubuntu1) xenial; urgency=medium + + * New upstream release. + * Fix debian/tests for new juju-1 name and other issues. + * Revert depend on juju-mongodb2.6 pending upstream support. + + -- Martin Packman Thu, 14 Apr 2016 15:17:52 +0000 + juju-core-1 (1.25.4-0ubuntu5) xenial; urgency=medium * Back to juju-1 as binary package but keep full version in paths. diff -Nru juju-core-1-1.25.4/debian/control juju-core-1-1.25.5/debian/control --- juju-core-1-1.25.4/debian/control 2016-04-07 19:09:30.000000000 +0000 +++ juju-core-1-1.25.5/debian/control 2016-04-15 00:07:21.000000000 +0000 @@ -14,7 +14,7 @@ golang-juju-loggo-dev, lsb-release, python -Standards-Version: 3.9.6 +Standards-Version: 3.9.7 Homepage: http://launchpad.net/juju-core Testsuite: autopkgtest @@ -35,6 +35,8 @@ with Juju environments. Package: juju-core +Section: oldlibs +Priority: extra Architecture: all Depends: juju-1, ${misc:Depends} Description: transitional package for supporting multiple Juju versions diff -Nru juju-core-1-1.25.4/debian/juju-1.lintian-overrides juju-core-1-1.25.5/debian/juju-1.lintian-overrides --- juju-core-1-1.25.4/debian/juju-1.lintian-overrides 2016-04-07 19:09:30.000000000 +0000 +++ juju-core-1-1.25.5/debian/juju-1.lintian-overrides 2016-04-15 00:07:21.000000000 +0000 @@ -1,10 +1,5 @@ -# golang always links statically -statically-linked-binary usr/lib/juju-*/bin/juju -statically-linked-binary usr/lib/juju-*/bin/jujud -statically-linked-binary usr/lib/juju-*/bin/juju-metadata -statically-linked-binary usr/lib/juju-*/bin/juju-restore # golang binaries should not be stripped -unstripped-binary-or-object usr/lib/juju-*/bin/juju -unstripped-binary-or-object usr/lib/juju-*/bin/jujud -unstripped-binary-or-object usr/lib/juju-*/bin/juju-metadata -unstripped-binary-or-object usr/lib/juju-*/bin/juju-restore +unstripped-binary-or-object usr/lib/juju-*/bin/* +# false positive from using golang port of libyaml +embedded-library usr/lib/juju-*/bin/*: libyaml +hardening-no-relro usr/lib/juju-*/bin/* diff -Nru juju-core-1-1.25.4/debian/rules juju-core-1-1.25.5/debian/rules --- juju-core-1-1.25.4/debian/rules 2016-04-07 19:09:30.000000000 +0000 +++ juju-core-1-1.25.5/debian/rules 2016-04-15 00:07:21.000000000 +0000 @@ -16,7 +16,7 @@ endif ifeq ($(shell lsb_release -cs | sed -r 's/xenial/new/'),new) - DB_DEP = -Vdist:Depends="juju-mongodb2.6" + DB_DEP = -Vdist:Depends="juju-mongodb" else DB_DEP = -Vdist:Depends="juju-mongodb (>= 2.4.6)" endif diff -Nru juju-core-1-1.25.4/debian/tests/client juju-core-1-1.25.5/debian/tests/client --- juju-core-1-1.25.4/debian/tests/client 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/debian/tests/client 2016-04-15 00:07:21.000000000 +0000 @@ -3,5 +3,5 @@ set -ex echo "Testing juju version: " -juju version +juju-1 version echo "OK" diff -Nru juju-core-1-1.25.4/debian/tests/control juju-core-1-1.25.5/debian/tests/control --- juju-core-1-1.25.4/debian/tests/control 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/debian/tests/control 2016-04-15 00:07:21.000000000 +0000 @@ -1,11 +1,11 @@ Tests: client -Depends: juju +Depends: juju-1 Restrictions: allow-stderr -Tests: current-local-provider current-manual-provider -Depends: juju, juju-local, sudo -Restrictions: allow-stderr breaks-testbed isolation-machine needs-root +Tests: current-local-provider future-local-provider +Depends: juju-local, sudo +Restrictions: allow-stderr breaks-testbed isolation-container needs-root -Tests: future-local-provider future-manual-provider -Depends: juju, juju-local, sudo -Restrictions: allow-stderr breaks-testbed isolation-machine needs-root +Tests: current-manual-provider future-manual-provider +Depends: juju-1, openssh-client, openssh-server, sudo +Restrictions: allow-stderr breaks-testbed isolation-container needs-root diff -Nru juju-core-1-1.25.4/debian/tests/local-provider juju-core-1-1.25.5/debian/tests/local-provider --- juju-core-1-1.25.4/debian/tests/local-provider 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/debian/tests/local-provider 2016-04-15 00:07:21.000000000 +0000 @@ -12,13 +12,13 @@ EOF echo "Testing juju bootstrap: " -juju bootstrap --upload-tools --debug +juju-1 bootstrap --upload-tools --debug echo "OK" echo "Waiting for environment to bootstrap: " -juju status --debug +juju-1 status --debug echo "OK" echo "Destroying environment: " -juju destroy-environment --debug --yes local-test +juju-1 destroy-environment --debug --yes local-test echo "OK" diff -Nru juju-core-1-1.25.4/debian/tests/manual-provider juju-core-1-1.25.5/debian/tests/manual-provider --- juju-core-1-1.25.4/debian/tests/manual-provider 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/debian/tests/manual-provider 2016-04-15 00:07:21.000000000 +0000 @@ -22,4 +22,4 @@ bootstrap-host: $ip EOT cat ~/.juju/environments.yaml -juju bootstrap --upload-tools --debug +juju-1 bootstrap --upload-tools --debug diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/api/base/testing/apicaller.go juju-core-1-1.25.5/src/github.com/juju/juju/api/base/testing/apicaller.go --- juju-core-1-1.25.4/src/github.com/juju/juju/api/base/testing/apicaller.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/api/base/testing/apicaller.go 2016-04-15 00:07:21.000000000 +0000 @@ -115,3 +115,21 @@ }, ) } + +// CheckingAPICallerMultiArgs checks each call against the indexed expected argument. Once expected +// arguments run out it doesn't check them. This is useful if your test continues to make calls after +// you have checked the ones you care about. +func CheckingAPICallerMultiArgs(c *gc.C, args []CheckArgs, numCalls *int, err error) base.APICallCloser { + if numCalls == nil { + panic("numCalls must be non-nill") + } + return APICallerFunc( + func(facade string, version int, id, method string, inArgs, outResults interface{}) error { + if len(args) < *numCalls { + checkArgs(c, &args[*numCalls], facade, version, id, method, inArgs, outResults) + } + *numCalls++ + return err + }, + ) +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/api/facadeversions.go juju-core-1-1.25.5/src/github.com/juju/juju/api/facadeversions.go --- juju-core-1-1.25.4/src/github.com/juju/juju/api/facadeversions.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/api/facadeversions.go 2016-04-15 00:07:21.000000000 +0000 @@ -60,6 +60,7 @@ "Uniter": 2, "UserManager": 0, "VolumeAttachmentsWatcher": 1, + "ProxyUpdater": 1, } // bestVersion tries to find the newest version in the version list that we can diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/api/proxyupdater/proxyupdater.go juju-core-1-1.25.5/src/github.com/juju/juju/api/proxyupdater/proxyupdater.go --- juju-core-1-1.25.4/src/github.com/juju/juju/api/proxyupdater/proxyupdater.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/api/proxyupdater/proxyupdater.go 2016-04-15 00:07:21.000000000 +0000 @@ -0,0 +1,45 @@ +// Copyright 2016 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package proxyupdater + +import ( + "github.com/juju/juju/api/base" + "github.com/juju/juju/api/watcher" + "github.com/juju/juju/apiserver/params" +) + +const proxyUpdaterFacade = "ProxyUpdater" + +// API provides access to the ProxyUpdater API facade. +type API struct { + facade base.FacadeCaller +} + +// NewAPI creates a new client-side ProxyUpdater facade. +func NewAPI(caller base.APICaller) *API { + if caller == nil { + panic("caller is nil") + } + return &API{ + facade: base.NewFacadeCaller(caller, proxyUpdaterFacade), + } +} + +// WatchForProxyConfigAndAPIHostPortChanges returns a NotifyWatcher waiting for +// changes in the proxy configuration or API host ports +func (api *API) WatchForProxyConfigAndAPIHostPortChanges() (watcher.NotifyWatcher, error) { + var result params.NotifyWatchResult + err := api.facade.FacadeCall("WatchForProxyConfigAndAPIHostPortChanges", nil, &result) + if err != nil { + return nil, err + } + return watcher.NewNotifyWatcher(api.facade.RawAPICaller(), result), nil +} + +// ProxyConfig returns the current environment configuration. +func (api *API) ProxyConfig() (params.ProxyConfigResult, error) { + var result params.ProxyConfigResult + err := api.facade.FacadeCall("ProxyConfig", nil, &result) + return result, err +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/api/proxyupdater/proxyupdater_test.go juju-core-1-1.25.5/src/github.com/juju/juju/api/proxyupdater/proxyupdater_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/api/proxyupdater/proxyupdater_test.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/api/proxyupdater/proxyupdater_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -0,0 +1,79 @@ +// Copyright 2016 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package proxyupdater_test + +import ( + stdtesting "testing" + + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" + + "github.com/juju/juju/api/base" + apitesting "github.com/juju/juju/api/base/testing" + "github.com/juju/juju/api/proxyupdater" + coretesting "github.com/juju/juju/testing" +) + +func TestAll(t *stdtesting.T) { + coretesting.MgoTestPackage(t) +} + +type ProxyUpdaterSuite struct { + coretesting.BaseSuite + + called int + apiCaller base.APICallCloser + api *proxyupdater.API +} + +var _ = gc.Suite(&ProxyUpdaterSuite{}) + +func (s *ProxyUpdaterSuite) init(c *gc.C, args []apitesting.CheckArgs, err error) { + s.called = 0 + s.apiCaller = apitesting.CheckingAPICallerMultiArgs(c, args, &s.called, err) + s.api = proxyupdater.NewAPI(s.apiCaller) + c.Check(s.api, gc.NotNil) + c.Check(s.called, gc.Equals, 0) +} + +func (s *ProxyUpdaterSuite) TestNewAPISuccess(c *gc.C) { + var called int + apiCaller := apitesting.CheckingAPICaller(c, nil, &called, nil) + api := proxyupdater.NewAPI(apiCaller) + c.Check(api, gc.NotNil) + c.Check(called, gc.Equals, 0) +} + +func (s *ProxyUpdaterSuite) TestNewAPIWithNilCaller(c *gc.C) { + panicFunc := func() { proxyupdater.NewAPI(nil) } + c.Assert(panicFunc, gc.PanicMatches, "caller is nil") +} + +func (s *ProxyUpdaterSuite) TestWatchForProxyConfigAndAPIHostPortChanges(c *gc.C) { + args := []apitesting.CheckArgs{{ + Facade: "ProxyUpdater", + Method: "WatchForProxyConfigAndAPIHostPortChanges", + Args: nil, + Results: nil, + }} + + s.init(c, args, nil) + _, err := s.api.WatchForProxyConfigAndAPIHostPortChanges() + c.Assert(s.called, gc.Equals, 1) + c.Assert(err, jc.ErrorIsNil) +} + +func (s *ProxyUpdaterSuite) TestProxyConfig(c *gc.C) { + args := []apitesting.CheckArgs{{ + Facade: "ProxyUpdater", + Method: "ProxyConfig", + Args: nil, + Results: nil, + }} + + s.init(c, args, nil) + _, err := s.api.ProxyConfig() + c.Assert(s.called, gc.Equals, 1) + c.Assert(err, jc.ErrorIsNil) +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/allfacades.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/allfacades.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/allfacades.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/allfacades.go 2016-04-15 00:07:21.000000000 +0000 @@ -33,6 +33,7 @@ _ "github.com/juju/juju/apiserver/metricsmanager" _ "github.com/juju/juju/apiserver/networker" _ "github.com/juju/juju/apiserver/provisioner" + _ "github.com/juju/juju/apiserver/proxyupdater" _ "github.com/juju/juju/apiserver/reboot" _ "github.com/juju/juju/apiserver/resumer" _ "github.com/juju/juju/apiserver/rsyslog" diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/client/status.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/client/status.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/client/status.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/client/status.go 2016-04-15 00:07:21.000000000 +0000 @@ -258,7 +258,7 @@ relations map[string][]*state.Relation units map[string]map[string]*state.Unit networks map[string]*state.Network - latestCharms map[charm.URL]string + latestCharms map[charm.URL]*state.Charm } // fetchMachines returns a map from top level machine id to machines, where machines[0] is the host @@ -298,11 +298,11 @@ func fetchAllServicesAndUnits( st *state.State, matchAny bool, -) (map[string]*state.Service, map[string]map[string]*state.Unit, map[charm.URL]string, error) { +) (map[string]*state.Service, map[string]map[string]*state.Unit, map[charm.URL]*state.Charm, error) { svcMap := make(map[string]*state.Service) unitMap := make(map[string]map[string]*state.Unit) - latestCharms := make(map[charm.URL]string) + latestCharms := make(map[charm.URL]*state.Charm) services, err := st.AllServices() if err != nil { return nil, nil, nil, err @@ -323,7 +323,7 @@ // the latest store revision can be looked up. charmURL, _ := s.CharmURL() if charmURL.Schema == "cs" { - latestCharms[*charmURL.WithRevision(-1)] = "" + latestCharms[*charmURL.WithRevision(-1)] = nil } } } @@ -335,7 +335,7 @@ if err != nil { return nil, nil, nil, err } - latestCharms[baseURL] = ch.String() + latestCharms[baseURL] = ch } return svcMap, unitMap, latestCharms, nil } @@ -586,10 +586,12 @@ status.Exposed = service.IsExposed() status.Life = processLife(service) - latestCharm, ok := context.latestCharms[*serviceCharmURL.WithRevision(-1)] - if ok && latestCharm != serviceCharmURL.String() { - status.CanUpgradeTo = latestCharm + if latestCharm, ok := context.latestCharms[*serviceCharmURL.WithRevision(-1)]; ok && latestCharm != nil { + if latestCharm.Revision() > serviceCharmURL.Revision { + status.CanUpgradeTo = latestCharm.String() + } } + var err error status.Relations, status.SubordinateTo, err = context.processServiceRelations(service) if err != nil { diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/client/status_test.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/client/status_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/client/status_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/client/status_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -4,6 +4,11 @@ package client_test import ( + "github.com/juju/juju/apiserver/charmrevisionupdater" + "github.com/juju/juju/apiserver/charmrevisionupdater/testing" + "github.com/juju/juju/apiserver/common" + apiservertesting "github.com/juju/juju/apiserver/testing" + jujutesting "github.com/juju/juju/juju/testing" jc "github.com/juju/testing/checkers" gc "gopkg.in/check.v1" @@ -164,3 +169,64 @@ } } } + +type statusUpgradeUnitSuite struct { + testing.CharmSuite + jujutesting.JujuConnSuite + + charmrevisionupdater *charmrevisionupdater.CharmRevisionUpdaterAPI + resources *common.Resources + authoriser apiservertesting.FakeAuthorizer +} + +var _ = gc.Suite(&statusUpgradeUnitSuite{}) + +func (s *statusUpgradeUnitSuite) SetUpSuite(c *gc.C) { + s.JujuConnSuite.SetUpSuite(c) + s.CharmSuite.SetUpSuite(c, &s.JujuConnSuite) +} + +func (s *statusUpgradeUnitSuite) TearDownSuite(c *gc.C) { + s.CharmSuite.TearDownSuite(c) + s.JujuConnSuite.TearDownSuite(c) +} + +func (s *statusUpgradeUnitSuite) SetUpTest(c *gc.C) { + s.JujuConnSuite.SetUpTest(c) + s.CharmSuite.SetUpTest(c) + s.resources = common.NewResources() + s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) + s.authoriser = apiservertesting.FakeAuthorizer{ + EnvironManager: true, + } + var err error + s.charmrevisionupdater, err = charmrevisionupdater.NewCharmRevisionUpdaterAPI(s.State, s.resources, s.authoriser) + c.Assert(err, jc.ErrorIsNil) +} + +func (s *statusUpgradeUnitSuite) TearDownTest(c *gc.C) { + s.CharmSuite.TearDownTest(c) + s.JujuConnSuite.TearDownTest(c) +} + +func (s *statusUpgradeUnitSuite) TestUpdateRevisions(c *gc.C) { + s.AddMachine(c, "0", 2) + s.SetupScenario(c) + client := s.APIState.Client() + status, _ := client.Status(nil) + + serviceStatus, ok := status.Services["mysql"] + c.Assert(ok, gc.Equals, true) + c.Assert(serviceStatus.CanUpgradeTo, gc.Equals, "") + + // Update to the latest available charm revision. + result, err := s.charmrevisionupdater.UpdateLatestRevisions() + c.Assert(err, jc.ErrorIsNil) + c.Assert(result.Error, gc.IsNil) + + // Check if CanUpgradeTo suggest the latest revision. + status, _ = client.Status(nil) + serviceStatus, ok = status.Services["mysql"] + c.Assert(ok, gc.Equals, true) + c.Assert(serviceStatus.CanUpgradeTo, gc.Equals, "cs:quantal/mysql-23") +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/common/export_test.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/common/export_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/common/export_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/common/export_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -3,8 +3,6 @@ package common -import "github.com/juju/juju/state" - var ( MachineJobFromParams = machineJobFromParams ValidateNewFacade = validateNewFacade @@ -31,8 +29,3 @@ func DescriptionFromVersions(name string, vers Versions) FacadeDescription { return descriptionFromVersions(name, versions(vers)) } - -func NewMultiNotifyWatcher(w ...state.NotifyWatcher) state.NotifyWatcher { - mw := newMultiNotifyWatcher(w...) - return mw -} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/common/storage.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/common/storage.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/common/storage.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/common/storage.go 2016-04-15 00:07:21.000000000 +0000 @@ -204,7 +204,7 @@ return nil, errors.Errorf("invalid storage kind %v", storageInstance.Kind()) } watchers = append(watchers, st.WatchStorageAttachment(storageTag, unitTag)) - return newMultiNotifyWatcher(watchers...), nil + return NewMultiNotifyWatcher(watchers...), nil } // volumeAttachmentDevicePath returns the absolute device path for diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/common/watch.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/common/watch.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/common/watch.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/common/watch.go 2016-04-15 00:07:21.000000000 +0000 @@ -92,11 +92,11 @@ changes chan struct{} } -// newMultiNotifyWatcher creates a NotifyWatcher that combines +// NewMultiNotifyWatcher creates a NotifyWatcher that combines // each of the NotifyWatchers passed in. Each watcher's initial // event is consumed, and a single initial event is sent. // Subsequent events are not coalesced. -func newMultiNotifyWatcher(w ...state.NotifyWatcher) *multiNotifyWatcher { +func NewMultiNotifyWatcher(w ...state.NotifyWatcher) *multiNotifyWatcher { m := &multiNotifyWatcher{ watchers: w, changes: make(chan struct{}), diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/params/network.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/params/network.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/params/network.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/params/network.go 2016-04-15 00:07:21.000000000 +0000 @@ -7,6 +7,7 @@ "net" "github.com/juju/juju/network" + "github.com/juju/utils/proxy" ) // ----- @@ -564,3 +565,8 @@ Subnets []Subnet `json:"Subnets"` Error *Error `json:"Error,omitempty"` } + +type ProxyConfigResult struct { + ProxySettings proxy.Settings + APTProxySettings proxy.Settings +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/provisioner/provisioner.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/provisioner/provisioner.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/provisioner/provisioner.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/provisioner/provisioner.go 2016-04-15 00:07:21.000000000 +0000 @@ -306,6 +306,7 @@ result.SSLHostnameVerification = config.SSLHostnameVerification() result.Proxy = config.ProxySettings() result.AptProxy = config.AptProxySettings() + result.AptMirror = config.AptMirror() result.PreferIPv6 = config.PreferIPv6() result.AllowLXCLoopMounts, _ = config.AllowLXCLoopMounts() diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/provisioner/provisioner_test.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/provisioner/provisioner_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/provisioner/provisioner_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/provisioner/provisioner_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -1425,6 +1425,7 @@ attrs := map[string]interface{}{ "http-proxy": "http://proxy.example.com:9000", "allow-lxc-loop-mounts": true, + "apt-mirror": "http://example.mirror.com", } err := s.State.UpdateEnvironConfig(attrs, nil, nil) c.Assert(err, jc.ErrorIsNil) @@ -1440,6 +1441,7 @@ c.Check(results.SSLHostnameVerification, jc.IsTrue) c.Check(results.Proxy, gc.DeepEquals, expectedProxy) c.Check(results.AptProxy, gc.DeepEquals, expectedProxy) + c.Check(results.AptMirror, gc.DeepEquals, "http://example.mirror.com") c.Check(results.PreferIPv6, jc.IsTrue) c.Check(results.AllowLXCLoopMounts, jc.IsTrue) } diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/proxyupdater/package_test.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/proxyupdater/package_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/proxyupdater/package_test.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/proxyupdater/package_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -0,0 +1,14 @@ +// Copyright 2016 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package proxyupdater + +import ( + "testing" + + gc "gopkg.in/check.v1" +) + +func TestPackage(t *testing.T) { + gc.TestingT(t) +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/proxyupdater/proxyupdater.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/proxyupdater/proxyupdater.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/proxyupdater/proxyupdater.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/proxyupdater/proxyupdater.go 2016-04-15 00:07:21.000000000 +0000 @@ -0,0 +1,105 @@ +// Copyright 2016 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package proxyupdater + +import ( + "strings" + + "github.com/juju/juju/apiserver/common" + "github.com/juju/juju/apiserver/params" + "github.com/juju/juju/environs/config" + "github.com/juju/juju/network" + "github.com/juju/juju/state" + "github.com/juju/juju/state/watcher" + "github.com/juju/utils/set" +) + +func init() { + common.RegisterStandardFacade("ProxyUpdater", 1, NewAPI) +} + +// API defines the methods the ProxyUpdater API facade implements. +type API interface { + WatchForProxyConfigAndAPIHostPortChanges() (params.NotifyWatchResult, error) + ProxyConfig() (params.ProxyConfigResult, error) +} + +// Backend defines the state methods this facade needs, so they can be +// mocked for testing. +type Backend interface { + EnvironConfig() (*config.Config, error) + APIHostPorts() ([][]network.HostPort, error) + WatchAPIHostPorts() state.NotifyWatcher + WatchForEnvironConfigChanges() state.NotifyWatcher +} + +type proxyUpdaterAPI struct { + st Backend + resources *common.Resources + authorizer common.Authorizer +} + +// NewAPI creates a new API server-side facade with a state.State backing. +func NewAPI(st *state.State, res *common.Resources, auth common.Authorizer) (API, error) { + return newAPIWithBacking(&stateShim{st: st}, res, auth) +} + +// newAPIWithBacking creates a new server-side API facade with the given Backing. +func newAPIWithBacking(st Backend, resources *common.Resources, authorizer common.Authorizer) (API, error) { + if !(authorizer.AuthMachineAgent() || authorizer.AuthUnitAgent()) { + return nil, common.ErrPerm + } + return &proxyUpdaterAPI{ + st: st, + resources: resources, + authorizer: authorizer, + }, nil +} + +// WatchChanges watches for cleanups to be perfomed in state +func (api *proxyUpdaterAPI) WatchForProxyConfigAndAPIHostPortChanges() (params.NotifyWatchResult, error) { + watch := common.NewMultiNotifyWatcher( + api.st.WatchForEnvironConfigChanges(), + api.st.WatchAPIHostPorts()) + if _, ok := <-watch.Changes(); ok { + return params.NotifyWatchResult{ + NotifyWatcherId: api.resources.Register(watch), + }, nil + } + return params.NotifyWatchResult{ + Error: common.ServerError(watcher.EnsureErr(watch)), + }, nil +} + +func (api *proxyUpdaterAPI) ProxyConfig() (params.ProxyConfigResult, error) { + var cfg params.ProxyConfigResult + env, err := api.st.EnvironConfig() + if err != nil { + return cfg, err + } + + apiHostPorts, err := api.st.APIHostPorts() + if err != nil { + return cfg, err + } + + cfg.ProxySettings = env.ProxySettings() + cfg.APTProxySettings = env.AptProxySettings() + + var noProxy []string + if cfg.ProxySettings.NoProxy != "" { + noProxy = strings.Split(cfg.ProxySettings.NoProxy, ",") + } + + noProxySet := set.NewStrings(noProxy...) + + for _, host := range apiHostPorts { + for _, hp := range host { + noProxySet.Add(hp.Address.Value) + } + } + + cfg.ProxySettings.NoProxy = strings.Join(noProxySet.SortedValues(), ",") + return cfg, nil +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/proxyupdater/proxyupdater_test.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/proxyupdater/proxyupdater_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/proxyupdater/proxyupdater_test.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/proxyupdater/proxyupdater_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -0,0 +1,246 @@ +// Copyright 2016 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package proxyupdater + +import ( + "errors" + + "github.com/juju/names" + jc "github.com/juju/testing/checkers" + "github.com/juju/utils/proxy" + gc "gopkg.in/check.v1" + + "github.com/juju/juju/apiserver/common" + "github.com/juju/juju/apiserver/params" + apiservertesting "github.com/juju/juju/apiserver/testing" + "github.com/juju/juju/environs/config" + "github.com/juju/juju/network" + "github.com/juju/juju/state" + coretesting "github.com/juju/juju/testing" + "github.com/juju/testing" +) + +type ProxyUpdaterSuite struct { + coretesting.BaseSuite + apiservertesting.StubNetwork + + state *stubBackend + resources *common.Resources + authorizer apiservertesting.FakeAuthorizer + facade API +} + +var _ = gc.Suite(&ProxyUpdaterSuite{}) + +func (s *ProxyUpdaterSuite) SetUpSuite(c *gc.C) { + s.BaseSuite.SetUpSuite(c) +} + +func (s *ProxyUpdaterSuite) TearDownSuite(c *gc.C) { + s.BaseSuite.TearDownSuite(c) +} + +func (s *ProxyUpdaterSuite) SetUpTest(c *gc.C) { + s.BaseSuite.SetUpTest(c) + s.resources = common.NewResources() + s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) + s.authorizer = apiservertesting.FakeAuthorizer{ + Tag: names.NewMachineTag("1"), + EnvironManager: false, + } + s.state = &stubBackend{} + s.state.SetUp(c) + s.AddCleanup(func(_ *gc.C) { s.state.Kill() }) + + var err error + s.facade, err = newAPIWithBacking(s.state, s.resources, s.authorizer) + c.Assert(err, jc.ErrorIsNil) + c.Assert(s.facade, gc.NotNil) + + // Shouldn't have any calls yet + apiservertesting.CheckMethodCalls(c, s.state.Stub) +} + +func (s *ProxyUpdaterSuite) TestWatchForProxyConfigAndAPIHostPortChanges(c *gc.C) { + // WatchForProxyConfigAndAPIHostPortChanges combines WatchForEnvironConfigChanges + // and WatchAPIHostPorts. Check that they are both called and we get the + // expected result. + wr, err := s.facade.WatchForProxyConfigAndAPIHostPortChanges() + c.Assert(err, jc.ErrorIsNil) + c.Assert(wr, jc.DeepEquals, params.NotifyWatchResult{NotifyWatcherId: "1"}) + + s.state.Stub.CheckCallNames(c, + "WatchForEnvironConfigChanges", + "WatchAPIHostPorts", + ) + +} + +func (s *ProxyUpdaterSuite) TestProxyConfig(c *gc.C) { + // Check that the ProxyConfig combines data from EnvironConfig and APIHostPorts + cfg, err := s.facade.ProxyConfig() + c.Assert(err, jc.ErrorIsNil) + s.state.Stub.CheckCallNames(c, + "EnvironConfig", + "APIHostPorts", + ) + + noProxy := "0.1.2.3,0.1.2.4,0.1.2.5" + + c.Assert(cfg, jc.DeepEquals, params.ProxyConfigResult{ + ProxySettings: proxy.Settings{ + Http: "http proxy", Https: "https proxy", Ftp: "", NoProxy: noProxy}, + APTProxySettings: proxy.Settings{ + Http: "http://http proxy", Https: "https://https proxy", Ftp: "", NoProxy: ""}, + }) +} + +func (s *ProxyUpdaterSuite) TestProxyConfigExtendsExisting(c *gc.C) { + // Check that the ProxyConfig combines data from EnvironConfig and APIHostPorts + s.state.SetEnvironConfig(coretesting.Attrs{ + "http-proxy": "http proxy", + "https-proxy": "https proxy", + "no-proxy": "9.9.9.9", + }) + cfg, err := s.facade.ProxyConfig() + c.Assert(err, jc.ErrorIsNil) + s.state.Stub.CheckCallNames(c, + "EnvironConfig", + "APIHostPorts", + ) + + expectedNoProxy := "0.1.2.3,0.1.2.4,0.1.2.5,9.9.9.9" + + c.Assert(cfg, jc.DeepEquals, params.ProxyConfigResult{ + ProxySettings: proxy.Settings{ + Http: "http proxy", Https: "https proxy", Ftp: "", NoProxy: expectedNoProxy}, + APTProxySettings: proxy.Settings{ + Http: "http://http proxy", Https: "https://https proxy", Ftp: "", NoProxy: ""}, + }) +} + +func (s *ProxyUpdaterSuite) TestProxyConfigNoDuplicates(c *gc.C) { + // Check that the ProxyConfig combines data from EnvironConfig and APIHostPorts + s.state.SetEnvironConfig(coretesting.Attrs{ + "http-proxy": "http proxy", + "https-proxy": "https proxy", + "no-proxy": "0.1.2.3", + }) + cfg, err := s.facade.ProxyConfig() + c.Assert(err, jc.ErrorIsNil) + s.state.Stub.CheckCallNames(c, + "EnvironConfig", + "APIHostPorts", + ) + + expectedNoProxy := "0.1.2.3,0.1.2.4,0.1.2.5" + + c.Assert(cfg, jc.DeepEquals, params.ProxyConfigResult{ + ProxySettings: proxy.Settings{ + Http: "http proxy", Https: "https proxy", Ftp: "", NoProxy: expectedNoProxy}, + APTProxySettings: proxy.Settings{ + Http: "http://http proxy", Https: "https://https proxy", Ftp: "", NoProxy: ""}, + }) +} + +type stubBackend struct { + *testing.Stub + + EnvConfig *config.Config + c *gc.C + configAttrs coretesting.Attrs + hpWatcher notAWatcher + confWatcher notAWatcher +} + +func (sb *stubBackend) SetUp(c *gc.C) { + sb.Stub = &testing.Stub{} + sb.c = c + sb.configAttrs = coretesting.Attrs{ + "http-proxy": "http proxy", + "https-proxy": "https proxy", + } + sb.hpWatcher = newFakeWatcher() + sb.confWatcher = newFakeWatcher() +} + +func (sb *stubBackend) Kill() { + sb.hpWatcher.Kill() + sb.confWatcher.Kill() +} + +func (sb *stubBackend) SetEnvironConfig(ca coretesting.Attrs) { + sb.configAttrs = ca +} + +func (sb *stubBackend) EnvironConfig() (*config.Config, error) { + sb.MethodCall(sb, "EnvironConfig") + if err := sb.NextErr(); err != nil { + return nil, err + } + return coretesting.CustomEnvironConfig(sb.c, sb.configAttrs), nil +} + +func (sb *stubBackend) APIHostPorts() ([][]network.HostPort, error) { + sb.MethodCall(sb, "APIHostPorts") + if err := sb.NextErr(); err != nil { + return nil, err + } + hps := [][]network.HostPort{ + network.NewHostPorts(1234, "0.1.2.3"), + network.NewHostPorts(1234, "0.1.2.4"), + network.NewHostPorts(1234, "0.1.2.5"), + } + return hps, nil +} + +func (sb *stubBackend) WatchAPIHostPorts() state.NotifyWatcher { + sb.MethodCall(sb, "WatchAPIHostPorts") + return sb.hpWatcher +} + +func (sb *stubBackend) WatchForEnvironConfigChanges() state.NotifyWatcher { + sb.MethodCall(sb, "WatchForEnvironConfigChanges") + return sb.confWatcher +} + +type notAWatcher struct { + changes chan struct{} + die chan struct{} +} + +func newFakeWatcher() notAWatcher { + ch := make(chan struct{}, 2) + ch <- struct{}{} + ch <- struct{}{} + return notAWatcher{ + changes: ch, + die: make(chan struct{}), + } +} + +func (w notAWatcher) Changes() <-chan struct{} { + return w.changes +} + +func (w notAWatcher) Stop() error { + return nil +} + +func (w notAWatcher) Err() error { + return errors.New("An error") +} + +func (w notAWatcher) Kill() { + select { + case <-w.die: // already closed (don't close a closed channel) + default: + close(w.die) + } +} + +func (w notAWatcher) Wait() error { + <-w.die // Wait until Kill is called. + return nil +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/proxyupdater/shims.go juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/proxyupdater/shims.go --- juju-core-1-1.25.4/src/github.com/juju/juju/apiserver/proxyupdater/shims.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/apiserver/proxyupdater/shims.go 2016-04-15 00:07:21.000000000 +0000 @@ -0,0 +1,32 @@ +// Copyright 2016 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package proxyupdater + +import ( + "github.com/juju/juju/environs/config" + "github.com/juju/juju/network" + "github.com/juju/juju/state" +) + +// stateShim forwards and adapts state.State methods to Backend +type stateShim struct { + Backend + st *state.State +} + +func (s *stateShim) EnvironConfig() (*config.Config, error) { + return s.st.EnvironConfig() +} + +func (s *stateShim) APIHostPorts() ([][]network.HostPort, error) { + return s.st.APIHostPorts() +} + +func (s *stateShim) WatchAPIHostPorts() state.NotifyWatcher { + return s.st.WatchAPIHostPorts() +} + +func (s *stateShim) WatchForEnvironConfigChanges() state.NotifyWatcher { + return s.st.WatchForEnvironConfigChanges() +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/cmd/juju/backups/backups.go juju-core-1-1.25.5/src/github.com/juju/juju/cmd/juju/backups/backups.go --- juju-core-1-1.25.4/src/github.com/juju/juju/cmd/juju/backups/backups.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/cmd/juju/backups/backups.go 2016-04-15 00:07:21.000000000 +0000 @@ -61,7 +61,7 @@ backupsCmd.Register(envcmd.Wrap(&DownloadCommand{})) backupsCmd.Register(envcmd.Wrap(&UploadCommand{})) backupsCmd.Register(envcmd.Wrap(&RemoveCommand{})) - backupsCmd.Register(envcmd.Wrap(&RestoreCommand{})) + backupsCmd.Register(newRestoreCommand()) return &backupsCmd } diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/cmd/juju/backups/export_test.go juju-core-1-1.25.5/src/github.com/juju/juju/cmd/juju/backups/export_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/cmd/juju/backups/export_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/cmd/juju/backups/export_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -3,6 +3,13 @@ package backups +import ( + "github.com/juju/cmd" + + "github.com/juju/juju/cmd/envcmd" + "github.com/juju/juju/environs" +) + const ( NotSet = notset DownloadWarning = downloadWarning @@ -11,3 +18,8 @@ var ( NewAPIClient = &newAPIClient ) + +func RestoreCommandForTest(environTestFunc func() (environs.Environ, error)) cmd.Command { + c := &RestoreCommand{environFunc: environTestFunc} + return envcmd.Wrap(c) +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/cmd/juju/backups/restore.go juju-core-1-1.25.5/src/github.com/juju/juju/cmd/juju/backups/restore.go --- juju-core-1-1.25.4/src/github.com/juju/juju/cmd/juju/backups/restore.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/cmd/juju/backups/restore.go 2016-04-15 00:07:21.000000000 +0000 @@ -21,6 +21,12 @@ "github.com/juju/juju/environs/configstore" ) +func newRestoreCommand() cmd.Command { + restoreCmd := &RestoreCommand{} + restoreCmd.environFunc = restoreCmd.getEnviron + return envcmd.Wrap(restoreCmd) +} + // RestoreCommand is a subcommand of backups that implement the restore behaior // it is invoked with "juju backups restore". type RestoreCommand struct { @@ -30,6 +36,7 @@ backupId string bootstrap bool uploadTools bool + environFunc func() (environs.Environ, error) } var restoreDoc = ` @@ -50,6 +57,8 @@ to that effect. ` +var BootstrapFunc = bootstrap.Bootstrap + // Info returns the content for --help. func (c *RestoreCommand) Info() *cmd.Info { return &cmd.Info{ @@ -129,16 +138,14 @@ return nil } -// rebootstrap will bootstrap a new server in safe-mode (not killing any other agent) -// if there is no current server available to restore to. -func (c *RestoreCommand) rebootstrap(ctx *cmd.Context) error { +func (c *RestoreCommand) getEnviron() (environs.Environ, error) { store, err := configstore.Default() if err != nil { - return errors.Trace(err) + return nil, errors.Trace(err) } cfg, err := c.Config(store, nil) if err != nil { - return errors.Trace(err) + return nil, errors.Trace(err) } // Turn on safe mode so that the newly bootstrapped instance // will not destroy all the instances it does not know about. @@ -146,30 +153,39 @@ "provisioner-safe-mode": true, }) if err != nil { - return errors.Annotatef(err, "cannot enable provisioner-safe-mode") + return nil, errors.Annotatef(err, "cannot enable provisioner-safe-mode") } env, err := environs.New(cfg) if err != nil { + return nil, errors.Trace(err) + } + return env, nil +} + +// rebootstrap will bootstrap a new server in safe-mode (not killing any other agent) +// if there is no current server available to restore to. +func (c *RestoreCommand) rebootstrap(ctx *cmd.Context) error { + env, err := c.environFunc() + if err != nil { return errors.Trace(err) } instanceIds, err := env.StateServerInstances() if err != nil { return errors.Annotatef(err, "cannot determine state server instances") } - if len(instanceIds) == 0 { - return errors.Errorf("no instances found; perhaps the environment was not bootstrapped") - } - inst, err := env.Instances(instanceIds) - if err == nil { - return errors.Errorf("old bootstrap instance %q still seems to exist; will not replace", inst) - } - if err != environs.ErrNoInstances { - return errors.Annotatef(err, "cannot detect whether old instance is still running") + if len(instanceIds) > 0 { + inst, err := env.Instances(instanceIds) + if err == nil { + return errors.Errorf("old bootstrap instance %q still seems to exist; will not replace", inst) + } + if err != environs.ErrNoInstances { + return errors.Annotatef(err, "cannot detect whether old instance is still running") + } } cons := c.constraints args := bootstrap.BootstrapParams{Constraints: cons, UploadTools: c.uploadTools} - if err := bootstrap.Bootstrap(envcmd.BootstrapContext(ctx), env, args); err != nil { + if err := BootstrapFunc(envcmd.BootstrapContext(ctx), env, args); err != nil { return errors.Annotatef(err, "cannot bootstrap new instance") } return nil diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/cmd/juju/backups/restore_test.go juju-core-1-1.25.5/src/github.com/juju/juju/cmd/juju/backups/restore_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/cmd/juju/backups/restore_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/cmd/juju/backups/restore_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -4,26 +4,31 @@ package backups_test import ( - //jc "github.com/juju/testing/checkers" + "github.com/juju/cmd" + "github.com/juju/errors" gc "gopkg.in/check.v1" "github.com/juju/juju/cmd/juju/backups" + "github.com/juju/juju/environs" + "github.com/juju/juju/environs/bootstrap" + "github.com/juju/juju/instance" "github.com/juju/juju/testing" ) type restoreSuite struct { BaseBackupsSuite - subcommand *backups.RestoreCommand + + restoreCommand cmd.Command } var _ = gc.Suite(&restoreSuite{}) func (s *restoreSuite) SetUpTest(c *gc.C) { s.BaseBackupsSuite.SetUpTest(c) - s.subcommand = &backups.RestoreCommand{} } func (s *restoreSuite) TestRestoreArgs(c *gc.C) { + s.restoreCommand = backups.RestoreCommandForTest(nil) _, err := testing.RunCommand(c, s.command, "restore") c.Assert(err, gc.ErrorMatches, "you must specify either a file or a backup id.") @@ -33,3 +38,43 @@ _, err = testing.RunCommand(c, s.command, "restore", "--id", "anid", "-b") c.Assert(err, gc.ErrorMatches, "it is not possible to rebootstrap and restore from an id.") } + +func (s *restoreSuite) TestRestoreReboostrapControllerExists(c *gc.C) { + fakeEnv := fakeEnviron{controllerInstances: []instance.Id{"1"}} + s.restoreCommand = backups.RestoreCommandForTest(func() (environs.Environ, error) { + return fakeEnv, nil + }) + _, err := testing.RunCommand(c, s.restoreCommand, "restore", "--file", "afile", "-b") + c.Assert(err, gc.ErrorMatches, ".*still seems to exist.*") +} + +func (s *restoreSuite) TestRestoreReboostrapNoControllers(c *gc.C) { + fakeEnv := fakeEnviron{} + s.restoreCommand = backups.RestoreCommandForTest(func() (environs.Environ, error) { + return fakeEnv, nil + }) + s.PatchValue(&backups.BootstrapFunc, func(ctx environs.BootstrapContext, environ environs.Environ, args bootstrap.BootstrapParams) error { + return errors.New("failed to bootstrap new controller") + }) + + _, err := testing.RunCommand(c, s.restoreCommand, "restore", "--file", "afile", "-b") + c.Assert(err, gc.ErrorMatches, ".*failed to bootstrap new controller") +} + +type fakeInstance struct { + instance.Instance + id instance.Id +} + +type fakeEnviron struct { + environs.Environ + controllerInstances []instance.Id +} + +func (f fakeEnviron) StateServerInstances() ([]instance.Id, error) { + return f.controllerInstances, nil +} + +func (f fakeEnviron) Instances(ids []instance.Id) ([]instance.Instance, error) { + return []instance.Instance{fakeInstance{id: "1"}}, nil +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/cmd/jujud/agent/agent_test.go juju-core-1-1.25.5/src/github.com/juju/juju/cmd/jujud/agent/agent_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/cmd/jujud/agent/agent_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/cmd/jujud/agent/agent_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -15,7 +15,7 @@ "github.com/juju/juju/agent" agenttools "github.com/juju/juju/agent/tools" - apienvironment "github.com/juju/juju/api/environment" + "github.com/juju/juju/api/base" "github.com/juju/juju/apiserver/params" agenttesting "github.com/juju/juju/cmd/jujud/agent/testing" cmdutil "github.com/juju/juju/cmd/jujud/util" @@ -95,7 +95,7 @@ } err := s.State.SetAPIHostPorts(hostPorts) c.Assert(err, jc.ErrorIsNil) - s.PatchValue(&proxyupdater.New, func(*apienvironment.Facade, bool) worker.Worker { + s.PatchValue(&proxyupdater.New, func(base.APICaller, bool) worker.Worker { return newDummyWorker() }) } diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/cmd/jujud/agent/machine.go juju-core-1-1.25.5/src/github.com/juju/juju/cmd/jujud/agent/machine.go --- juju-core-1-1.25.4/src/github.com/juju/juju/cmd/jujud/agent/machine.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/cmd/jujud/agent/machine.go 2016-04-15 00:07:21.000000000 +0000 @@ -44,8 +44,6 @@ cmdutil "github.com/juju/juju/cmd/jujud/util" "github.com/juju/juju/container" "github.com/juju/juju/container/kvm" - "github.com/juju/juju/container/lxc" - "github.com/juju/juju/container/lxc/lxcutils" "github.com/juju/juju/environs" "github.com/juju/juju/environs/config" "github.com/juju/juju/feature" @@ -724,7 +722,7 @@ // before we do anything else. writeSystemFiles := shouldWriteProxyFiles(agentConfig) runner.StartWorker("proxyupdater", func() (worker.Worker, error) { - return proxyupdater.New(st.Environment(), writeSystemFiles), nil + return proxyupdater.New(st, writeSystemFiles), nil }) if isEnvironManager { @@ -933,13 +931,8 @@ // initialises suitable infrastructure to support such containers. func (a *MachineAgent) setupContainerSupport(runner worker.Runner, st api.Connection, agentConfig agent.Config) error { var supportedContainers []instance.ContainerType - // LXC containers are only supported on bare metal and fully virtualized linux systems - // Nested LXC containers and Windows machines cannot run LXC containers - supportsLXC, err := lxc.IsLXCSupported() - if err != nil { - logger.Warningf("no lxc containers possible: %v", err) - } - if err == nil && supportsLXC { + supportsContainers := container.ContainersSupported() + if supportsContainers { supportedContainers = append(supportedContainers, instance.LXC) } @@ -1770,10 +1763,8 @@ errors = append(errors, err) } - insideLXC, err := lxcutils.RunningInsideLXC() - if err != nil { - errors = append(errors, err) - } else if insideLXC { + insideContainer := container.RunningInContainer() + if insideContainer { // We're running inside LXC, so loop devices may leak. Detach // any loop devices that are backed by files on this machine. // diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/cmd/jujud/agent/machine_test.go juju-core-1-1.25.5/src/github.com/juju/juju/cmd/jujud/agent/machine_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/cmd/jujud/agent/machine_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/cmd/jujud/agent/machine_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -32,12 +32,13 @@ "github.com/juju/juju/agent" "github.com/juju/juju/api" apiaddresser "github.com/juju/juju/api/addresser" + "github.com/juju/juju/api/base" apideployer "github.com/juju/juju/api/deployer" - apienvironment "github.com/juju/juju/api/environment" apifirewaller "github.com/juju/juju/api/firewaller" apiinstancepoller "github.com/juju/juju/api/instancepoller" apimetricsmanager "github.com/juju/juju/api/metricsmanager" apinetworker "github.com/juju/juju/api/networker" + apiproxyupdater "github.com/juju/juju/api/proxyupdater" apirsyslog "github.com/juju/juju/api/rsyslog" charmtesting "github.com/juju/juju/apiserver/charmrevisionupdater/testing" "github.com/juju/juju/apiserver/params" @@ -1340,25 +1341,33 @@ }) // Make sure there are some proxy settings to write. - expectSettings := proxy.Settings{ - Http: "http proxy", - Https: "https proxy", - Ftp: "ftp proxy", + updateAttrs := config.ProxyConfigMap(proxy.Settings{ + Http: "http proxy", + Https: "https proxy", + Ftp: "ftp proxy", + NoProxy: "", + }) + + expected := params.ProxyConfigResult{ + ProxySettings: proxy.Settings{ + Http: "http proxy", Https: "https proxy", Ftp: "ftp proxy", NoProxy: "localhost"}, + APTProxySettings: proxy.Settings{ + Http: "http://http proxy", Https: "https://https proxy", Ftp: "ftp://ftp proxy", NoProxy: ""}, } - updateAttrs := config.ProxyConfigMap(expectSettings) + err := s.State.UpdateEnvironConfig(updateAttrs, nil, nil) c.Assert(err, jc.ErrorIsNil) // Patch out the actual worker func. started := make(chan struct{}) - mockNew := func(api *apienvironment.Facade, writeSystemFiles bool) worker.Worker { + mockNew := func(api base.APICaller, writeSystemFiles bool) worker.Worker { // Direct check of the behaviour flag. c.Check(writeSystemFiles, gc.Equals, expectWriteSystemFiles) // Indirect check that we get a functional API. - conf, err := api.EnvironConfig() + proxyAPI := apiproxyupdater.NewAPI(api) + conf, err := proxyAPI.ProxyConfig() if c.Check(err, jc.ErrorIsNil) { - actualSettings := conf.ProxySettings() - c.Check(actualSettings, jc.DeepEquals, expectSettings) + c.Check(conf, jc.DeepEquals, expected) } return worker.NewSimpleWorker(func(_ <-chan struct{}) error { close(started) diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/export_test.go juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/export_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/export_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/export_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -18,8 +18,6 @@ RestartSymlink = restartSymlink ReleaseVersion = &releaseVersion PreferFastLXC = preferFastLXC - RuntimeGOOS = &runtimeGOOS - RunningInsideLXC = &runningInsideLXC WriteWgetTmpFile = &writeWgetTmpFile ) diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxc.go juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxc.go --- juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxc.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxc.go 2016-04-15 00:07:21.000000000 +0000 @@ -11,7 +11,6 @@ "os" "os/exec" "path/filepath" - "runtime" "strconv" "strings" "text/template" @@ -29,7 +28,6 @@ "github.com/juju/juju/cloudconfig/containerinit" "github.com/juju/juju/cloudconfig/instancecfg" "github.com/juju/juju/container" - "github.com/juju/juju/container/lxc/lxcutils" "github.com/juju/juju/instance" "github.com/juju/juju/juju/arch" "github.com/juju/juju/storage/looputil" @@ -43,8 +41,6 @@ LxcContainerDir = golxc.GetDefaultLXCContainerDir() LxcRestartDir = "/etc/lxc/auto" LxcObjectFactory = golxc.Factory() - runtimeGOOS = runtime.GOOS - runningInsideLXC = lxcutils.RunningInsideLXC writeWgetTmpFile = ioutil.WriteFile lxcRetryCount = 3 lxcRetryDelay = 10 @@ -86,20 +82,6 @@ return lines[1], nil } -// IsLXCSupported returns a boolean value indicating whether or not -// we can run LXC containers. -func IsLXCSupported() (bool, error) { - if runtimeGOOS != "linux" { - return false, nil - } - // We do not support running nested LXC containers. - insideLXC, err := runningInsideLXC() - if err != nil { - return false, errors.Trace(err) - } - return !insideLXC, nil -} - type containerManager struct { name string logdir string @@ -368,8 +350,7 @@ // method as we have passed it through at container creation time. This // is necessary to get the appropriate rootfs reference without explicitly // setting it ourselves. - if err = lxcContainer.Start("", consoleFile); err != nil { - logger.Warningf("container failed to start %v", err) + if err := lxcContainer.Start("", consoleFile); err != nil { // if the container fails to start we should try to destroy it // check if the container has been constructed if lxcContainer.IsConstructed() { @@ -377,13 +358,10 @@ if derr := lxcContainer.Destroy(); derr != nil { // if an error is reported there is probably a leftover // container that the user should clean up manually - logger.Errorf("container failed to start and failed to destroy: %v", derr) - return nil, nil, errors.Annotate(err, "container failed to start and failed to destroy: manual cleanup of containers needed") + return nil, nil, errors.Annotatef(err, "container failed to start and failed to destroy: manual cleanup of containers needed: %v", derr) } - logger.Warningf("container failed to start and was destroyed - safe to retry") return nil, nil, errors.Wrap(err, instance.NewRetryableCreationError("container failed to start and was destroyed: "+lxcContainer.Name(), 1, 0)) } - logger.Warningf("container failed to start: %v", err) return nil, nil, errors.Annotate(err, "container failed to start") } diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxc_test.go juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxc_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxc_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxc_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -1266,34 +1266,6 @@ ) } -func (s *LxcSuite) TestIsLXCSupportedOnHost(c *gc.C) { - s.PatchValue(lxc.RunningInsideLXC, func() (bool, error) { - return false, nil - }) - supports, err := lxc.IsLXCSupported() - c.Assert(err, jc.ErrorIsNil) - c.Assert(supports, jc.IsTrue) -} - -func (s *LxcSuite) TestIsLXCSupportedOnLXCContainer(c *gc.C) { - s.PatchValue(lxc.RunningInsideLXC, func() (bool, error) { - return true, nil - }) - supports, err := lxc.IsLXCSupported() - c.Assert(err, jc.ErrorIsNil) - c.Assert(supports, jc.IsFalse) -} - -func (s *LxcSuite) TestIsLXCSupportedNonLinuxSystem(c *gc.C) { - s.PatchValue(lxc.RuntimeGOOS, "windows") - s.PatchValue(lxc.RunningInsideLXC, func() (bool, error) { - panic("should not be called") - }) - supports, err := lxc.IsLXCSupported() - c.Assert(err, jc.ErrorIsNil) - c.Assert(supports, jc.IsFalse) -} - func (s *LxcSuite) TestWgetEnvironmentUsesNoProxy(c *gc.C) { var wgetScript []byte fakeCert := []byte("fakeCert") diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxcutils/export_test.go juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxcutils/export_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxcutils/export_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxcutils/export_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -// Copyright 2015 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package lxcutils - -var ( - InitProcessCgroupFile = &initProcessCgroupFile -) diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxcutils/lxcutils_test.go juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxcutils/lxcutils_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxcutils/lxcutils_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxcutils/lxcutils_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,98 +0,0 @@ -// Copyright 2015 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package lxcutils_test - -import ( - "path/filepath" - "runtime" - stdtesting "testing" - - jc "github.com/juju/testing/checkers" - ft "github.com/juju/testing/filetesting" - gc "gopkg.in/check.v1" - - "github.com/juju/juju/container/lxc/lxcutils" - "github.com/juju/juju/testing" -) - -func Test(t *stdtesting.T) { - if runtime.GOOS != "linux" { - t.Skip("LXC is a Linux thing") - } - gc.TestingT(t) -} - -type LxcUtilsSuite struct { - testing.BaseSuite -} - -var _ = gc.Suite(&LxcUtilsSuite{}) - -var lxcCgroupContents = `11:hugetlb:/lxc/juju-machine-1-lxc-0 -10:perf_event:/lxc/juju-machine-1-lxc-0 -9:blkio:/lxc/juju-machine-1-lxc-0 -8:freezer:/lxc/juju-machine-1-lxc-0 -7:devices:/lxc/juju-machine-1-lxc-0 -6:memory:/lxc/juju-machine-1-lxc-0 -5:cpuacct:/lxc/juju-machine-1-lxc-0 -4:cpu:/lxc/juju-machine-1-lxc-0 -3:cpuset:/lxc/juju-machine-1-lxc-0 -2:name=systemd:/lxc/juju-machine-1-lxc-0 -` - -var hostCgroupContents = `11:hugetlb:/ -10:perf_event:/ -9:blkio:/ -8:freezer:/ -7:devices:/ -6:memory:/ -5:cpuacct:/ -4:cpu:/ -3:cpuset:/ -2:name=systemd:/ -` - -var malformedCgroupFile = `some bogus content -more bogus content` - -func (s *LxcUtilsSuite) TestRunningInsideLXCOnHost(c *gc.C) { - baseDir := c.MkDir() - cgroup := filepath.Join(baseDir, "cgroup") - - ft.File{"cgroup", hostCgroupContents, 0400}.Create(c, baseDir) - - s.PatchValue(lxcutils.InitProcessCgroupFile, cgroup) - runningInLXC, err := lxcutils.RunningInsideLXC() - c.Assert(err, jc.ErrorIsNil) - c.Assert(runningInLXC, jc.IsFalse) -} - -func (s *LxcUtilsSuite) TestRunningInsideLXCOnLXCContainer(c *gc.C) { - baseDir := c.MkDir() - cgroup := filepath.Join(baseDir, "cgroup") - - ft.File{"cgroup", lxcCgroupContents, 0400}.Create(c, baseDir) - - s.PatchValue(lxcutils.InitProcessCgroupFile, cgroup) - runningInLXC, err := lxcutils.RunningInsideLXC() - c.Assert(err, jc.ErrorIsNil) - c.Assert(runningInLXC, jc.IsTrue) -} - -func (s *LxcUtilsSuite) TestRunningInsideLXCMissingCgroupFile(c *gc.C) { - s.PatchValue(lxcutils.InitProcessCgroupFile, "") - _, err := lxcutils.RunningInsideLXC() - c.Assert(err.Error(), gc.Matches, "open : no such file or directory") -} - -func (s *LxcUtilsSuite) TestRunningInsideLXCMalformedCgroupFile(c *gc.C) { - baseDir := c.MkDir() - cgroup := filepath.Join(baseDir, "cgroup") - - ft.File{"cgroup", malformedCgroupFile, 0400}.Create(c, baseDir) - - s.PatchValue(lxcutils.InitProcessCgroupFile, cgroup) - _, err := lxcutils.RunningInsideLXC() - c.Assert(err.Error(), gc.Equals, "malformed cgroup file") -} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxcutils/utils.go juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxcutils/utils.go --- juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxcutils/utils.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxcutils/utils.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package lxcutils - -var initProcessCgroupFile = "/proc/1/cgroup" - -// RunningInsideLXC reports whether or not we are running inside an -// LXC container. -func RunningInsideLXC() (bool, error) { - return runningInsideLXC() -} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxcutils/utils_linux.go juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxcutils/utils_linux.go --- juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxcutils/utils_linux.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxcutils/utils_linux.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -// Copyright 2013 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -package lxcutils - -import ( - "bufio" - "os" - "strings" - - "github.com/juju/errors" -) - -func runningInsideLXC() (bool, error) { - file, err := os.Open(initProcessCgroupFile) - if err != nil { - return false, errors.Trace(err) - } - defer file.Close() - - scanner := bufio.NewScanner(file) - for scanner.Scan() { - line := scanner.Text() - fields := strings.Split(line, ":") - if len(fields) != 3 { - return false, errors.Errorf("malformed cgroup file") - } - if fields[2] != "/" { - // When running in a container the anchor point will be - // something other than "/". - return true, nil - } - } - if err := scanner.Err(); err != nil { - return false, errors.Annotate(err, "failed to read cgroup file") - } - return false, nil -} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxcutils/utils_notlinux.go juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxcutils/utils_notlinux.go --- juju-core-1-1.25.4/src/github.com/juju/juju/container/lxc/lxcutils/utils_notlinux.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/container/lxc/lxcutils/utils_notlinux.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -// Copyright 2015 Canonical Ltd. -// Licensed under the AGPLv3, see LICENCE file for details. - -//+build !linux - -package lxcutils - -func runningInsideLXC() (bool, error) { - return false, nil -} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/container/utils.go juju-core-1-1.25.5/src/github.com/juju/juju/container/utils.go --- juju-core-1-1.25.4/src/github.com/juju/juju/container/utils.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/container/utils.go 2016-04-15 00:07:21.000000000 +0000 @@ -0,0 +1,26 @@ +// Copyright 2016 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package container + +import ( + "os/exec" + "runtime" +) + +var RunningInContainer = func() bool { + if runtime.GOOS != "linux" { + return false + } + + /* running-in-container is in init-scripts-helpers, and is smart enough + * to ask both systemd and upstart whether or not they know if the task + * is running in a container. + */ + cmd := exec.Command("running-in-container") + return cmd.Run() == nil +} + +func ContainersSupported() bool { + return !RunningInContainer() +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/container/utils_test.go juju-core-1-1.25.5/src/github.com/juju/juju/container/utils_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/container/utils_test.go 1970-01-01 00:00:00.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/container/utils_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -0,0 +1,33 @@ +// Copyright 2016 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package container + +import ( + jc "github.com/juju/testing/checkers" + gc "gopkg.in/check.v1" + + "github.com/juju/juju/testing" +) + +var _ = gc.Suite(&UtilsSuite{}) + +type UtilsSuite struct { + testing.BaseSuite +} + +func (s *UtilsSuite) TestIsLXCSupportedOnHost(c *gc.C) { + s.PatchValue(&RunningInContainer, func() bool { + return false + }) + supports := ContainersSupported() + c.Assert(supports, jc.IsTrue) +} + +func (s *UtilsSuite) TestIsLXCSupportedOnLXCContainer(c *gc.C) { + s.PatchValue(&RunningInContainer, func() bool { + return true + }) + supports := ContainersSupported() + c.Assert(supports, jc.IsFalse) +} diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/dependencies.tsv juju-core-1-1.25.5/src/github.com/juju/juju/dependencies.tsv --- juju-core-1-1.25.4/src/github.com/juju/juju/dependencies.tsv 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/dependencies.tsv 2016-04-15 00:07:21.000000000 +0000 @@ -39,7 +39,7 @@ gopkg.in/check.v1 git b3d3430320d4260e5fea99841af984b3badcea63 2015-06-26T10:50:28Z gopkg.in/errgo.v1 git 15098963088579c1cd9eb1a7da285831e548390b 2015-07-07T18:34:45Z gopkg.in/goose.v1 git e4a91e8b8323b8eef6fe41a66fd3ac6120520bb0 2015-11-13T22:25:24Z -gopkg.in/juju/charm.v5 git aece7b0e56c298641968239a7fa0b3466afa6ef5 2015-10-08T18:29:44Z +gopkg.in/juju/charm.v5 git 84c8fe478fa1b1e67eb552831de5fd2603085c51 2016-03-30T02:02:11Z gopkg.in/juju/charmstore.v4 git b90d24652753eeb1f7d209483d499f6b24dcf25e 2015-07-10T10:24:09Z gopkg.in/juju/environschema.v1 git 16cc59268c09c22870cb4de8eb6248652535f315 2015-08-24T13:22:26Z gopkg.in/macaroon-bakery.v0 git 9593b80b01ba04b519769d045dffd6abd827d2fd 2015-04-10T07:46:55Z diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/juju/arch/arch.go juju-core-1-1.25.5/src/github.com/juju/juju/juju/arch/arch.go --- juju-core-1-1.25.4/src/github.com/juju/juju/juju/arch/arch.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/juju/arch/arch.go 2016-04-15 00:07:21.000000000 +0000 @@ -16,6 +16,7 @@ ARM = "armhf" ARM64 = "arm64" PPC64EL = "ppc64el" + S390X = "s390x" // Older versions of Juju used "ppc64" instead of ppc64el LEGACY_PPC64 = "ppc64" @@ -28,6 +29,7 @@ ARM, ARM64, PPC64EL, + S390X, } // Info records the information regarding each architecture recognised by Juju. @@ -37,6 +39,7 @@ ARM: {32}, ARM64: {64}, PPC64EL: {64}, + S390X: {64}, } // ArchInfo is a struct containing information about a supported architecture. @@ -56,6 +59,7 @@ {regexp.MustCompile("(arm$)|(armv.*)"), ARM}, {regexp.MustCompile("aarch64"), ARM64}, {regexp.MustCompile("ppc64|ppc64el|ppc64le"), PPC64EL}, + {regexp.MustCompile("s390x"), S390X}, } // Override for testing. diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/juju/arch/arch_test.go juju-core-1-1.25.5/src/github.com/juju/juju/juju/arch/arch_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/juju/arch/arch_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/juju/arch/arch_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -41,6 +41,7 @@ {"ppc64el", "ppc64el"}, {"ppc64le", "ppc64el"}, {"ppc64", "ppc64el"}, + {"s390x", "s390x"}, } { arch := arch.NormaliseArch(test.raw) c.Check(arch, gc.Equals, test.arch) diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/provider/gce/disks.go juju-core-1-1.25.5/src/github.com/juju/juju/provider/gce/disks.go --- juju-core-1-1.25.4/src/github.com/juju/juju/provider/gce/disks.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/provider/gce/disks.go 2016-04-15 00:07:21.000000000 +0000 @@ -199,6 +199,7 @@ SizeHintGB: mibToGib(p.Size), Name: volumeName, PersistentDiskType: persistentType, + Description: v.envUUID, } gceDisks, err := v.gce.CreateDisks(zone, []google.DiskSpec{disk}) @@ -263,6 +264,12 @@ return zone, volumeUUID, nil } + +func isValidVolume(volumeName string) bool { + _, _, err := parseVolumeId(volumeName) + return err == nil +} + func (v *volumeSource) destroyOneVolume(volName string) error { zone, _, err := parseVolumeId(volName) if err != nil { @@ -272,7 +279,6 @@ return errors.Annotatef(err, "cannot destroy volume %q", volName) } return nil - } func (v *volumeSource) ListVolumes() ([]string, error) { @@ -289,7 +295,15 @@ continue } for _, disk := range disks { - volumes = append(volumes, disk.Name) + // Blank disk description means an older disk or a disk + // not created by storage, we should not touch it. + if disk.Description != v.envUUID && disk.Description != "" { + continue + } + // We don't want to lay hands on disks we did not create. + if isValidVolume(disk.Name) { + volumes = append(volumes, disk.Name) + } } } return volumes, nil diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/provider/gce/disks_test.go juju-core-1-1.25.5/src/github.com/juju/juju/provider/gce/disks_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/provider/gce/disks_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/provider/gce/disks_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -197,6 +197,54 @@ c.Assert(call[0].ZoneName, gc.Equals, "home-zone") } +func (s *volumeSourceSuite) TestListVolumesOnlyListsCurrentEnvUUID(c *gc.C) { + otherDisk := &google.Disk{ + Id: 1234568, + Name: "home-zone--566fe7b2-c026-4a86-a2cc-84cb7f9a4868", + Zone: "home-zone", + Status: google.StatusReady, + Size: 1024, + Description: "a-different-model-uuid", + } + s.FakeConn.GoogleDisks = []*google.Disk{s.BaseDisk, otherDisk} + s.FakeConn.Zones = []google.AvailabilityZone{google.NewZone("home-zone", "Ready", "", "")} + vols, err := s.source.ListVolumes() + c.Check(err, jc.ErrorIsNil) + c.Assert(vols, gc.HasLen, 1) +} + +func (s *volumeSourceSuite) TestListVolumesListsEmptyUUIDVolumes(c *gc.C) { + otherDisk := &google.Disk{ + Id: 1234568, + Name: "home-zone--566fe7b2-c026-4a86-a2cc-84cb7f9a4868", + Zone: "home-zone", + Status: google.StatusReady, + Size: 1024, + Description: "", + } + s.FakeConn.GoogleDisks = []*google.Disk{s.BaseDisk, otherDisk} + s.FakeConn.Zones = []google.AvailabilityZone{google.NewZone("home-zone", "Ready", "", "")} + vols, err := s.source.ListVolumes() + c.Check(err, jc.ErrorIsNil) + c.Assert(vols, gc.HasLen, 2) +} + +func (s *volumeSourceSuite) TestListVolumesIgnoresNamesFormattedDifferently(c *gc.C) { + otherDisk := &google.Disk{ + Id: 1234568, + Name: "juju-566fe7b2-c026-4a86-a2cc-84cb7f9a4868", + Zone: "home-zone", + Status: google.StatusReady, + Size: 1024, + Description: "", + } + s.FakeConn.GoogleDisks = []*google.Disk{s.BaseDisk, otherDisk} + s.FakeConn.Zones = []google.AvailabilityZone{google.NewZone("home-zone", "Ready", "", "")} + vols, err := s.source.ListVolumes() + c.Check(err, jc.ErrorIsNil) + c.Assert(vols, gc.HasLen, 1) +} + func (s *volumeSourceSuite) TestDescribeVolumes(c *gc.C) { s.FakeConn.GoogleDisk = s.BaseDisk volName := "home-zone--c930380d-8337-4bf5-b07a-9dbb5ae771e4" diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/provider/gce/google/disk.go juju-core-1-1.25.5/src/github.com/juju/juju/provider/gce/google/disk.go --- juju-core-1-1.25.4/src/github.com/juju/juju/provider/gce/google/disk.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/provider/gce/google/disk.go 2016-04-15 00:07:21.000000000 +0000 @@ -6,6 +6,8 @@ import ( "github.com/juju/errors" "google.golang.org/api/compute/v1" + + "github.com/juju/juju/version" ) // The different types of disk persistence supported by GCE. @@ -46,11 +48,24 @@ // GCE disks. // // Note: GCE does not currently have an official minimum disk size. -// However, in testing we found the minimum size to be 10 GB due to -// the image size. See gceapi messsage. +// However, in testing we found the minimum size to be 10 GB for ubuntu +// and 50 GB for windows due to the image size. See gceapi message. // // gceapi: Requested disk size cannot be smaller than the image size (10 GB) -const MinDiskSizeGB uint64 = 10 +func MinDiskSizeGB(ser string) uint64 { + // See comment below that explains why we're ignoring the error + os, _ := version.GetOSFromSeries(ser) + switch os { + case version.Ubuntu: + return 10 + case version.Windows: + return 50 + // On default we just return a "sane" default since the error + // will be propagated through the api and appear in juju status anyway + default: + return 10 + } +} // gibToMib converts gibibytes to mebibytes. func gibToMib(g int64) uint64 { @@ -61,6 +76,8 @@ // Some fields are used only for attached disks (i.e. in association // with instances). type DiskSpec struct { + // Series is the OS series on which the disk size depends + Series string // SizeHintGB is the requested disk size in Gigabytes. It must be // greater than 0. SizeHintGB uint64 @@ -89,12 +106,20 @@ // characters must be a dash, lowercase letter, or digit, except the // last character, which cannot be a dash. Name string + // Description holds a description of the disk, it currently holds + // modelUUID. + // This field is used instead of a tag or metadata because + // compute (v1) API does not support any way to add extra data + // to disks. + // Description was picked because it is not mutable (actually no field is) for disks. + // There is a metadata API but it is not supported for disks for the moment. + Description string } // TooSmall checks the spec's size hint and indicates whether or not // it is smaller than the minimum disk size. func (ds *DiskSpec) TooSmall() bool { - return ds.SizeHintGB < MinDiskSizeGB + return ds.SizeHintGB < MinDiskSizeGB(ds.Series) } // SizeGB returns the disk size to use for a new disk. The size hint @@ -103,7 +128,7 @@ func (ds *DiskSpec) SizeGB() uint64 { size := ds.SizeHintGB if ds.TooSmall() { - size = MinDiskSizeGB + size = MinDiskSizeGB(ds.Series) } return size } @@ -155,6 +180,7 @@ SizeGb: int64(ds.SizeGB()), SourceImage: ds.ImageURL, Type: string(ds.PersistentDiskType), + Description: ds.Description, }, nil } @@ -177,6 +203,8 @@ Id uint64 // Name is a unique identifier string for each disk. Name string + // Description holds the description field for a disk, we store env UUID here. + Description string // Size is the size in mbit. Size uint64 // Type is one of the available disk types supported by @@ -190,12 +218,13 @@ func NewDisk(cd *compute.Disk) *Disk { d := &Disk{ - Id: cd.Id, - Name: cd.Name, - Size: gibToMib(cd.SizeGb), - Type: DiskType(cd.Type), - Zone: cd.Zone, - Status: DiskStatus(cd.Status), + Id: cd.Id, + Name: cd.Name, + Description: cd.Description, + Size: gibToMib(cd.SizeGb), + Type: DiskType(cd.Type), + Zone: cd.Zone, + Status: DiskStatus(cd.Status), } return d } diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/provider/gce/google/disk_test.go juju-core-1-1.25.5/src/github.com/juju/juju/provider/gce/google/disk_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/provider/gce/google/disk_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/provider/gce/google/disk_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -36,11 +36,27 @@ c.Check(size, gc.Equals, uint64(15)) } -func (s *diskSuite) TestDiskSpecSizeGBMin(c *gc.C) { +func (s *diskSuite) TestDiskSpecSizeGBMinUbuntu(c *gc.C) { s.DiskSpec.SizeHintGB = 0 size := s.DiskSpec.SizeGB() c.Check(size, gc.Equals, uint64(10)) +} + +func (s *diskSuite) TestDiskSpecSizeGBMinWindows(c *gc.C) { + s.DiskSpec.SizeHintGB = 0 + s.DiskSpec.Series = "win2012r2" + size := s.DiskSpec.SizeGB() + + c.Check(size, gc.Equals, uint64(50)) +} + +func (s *diskSuite) TestDiskSpecSizeGBMinUnknown(c *gc.C) { + s.DiskSpec.SizeHintGB = 0 + s.DiskSpec.Series = "arch" + size := s.DiskSpec.SizeGB() + + c.Check(size, gc.Equals, uint64(10)) } type attachedInfo struct { diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/provider/maas/environ.go juju-core-1-1.25.5/src/github.com/juju/juju/provider/maas/environ.go --- juju-core-1-1.25.4/src/github.com/juju/juju/provider/maas/environ.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/provider/maas/environ.go 2016-04-15 00:07:21.000000000 +0000 @@ -489,7 +489,12 @@ if inst == nil { continue } - zones[i] = inst.(*maasInstance).zone() + z, err := inst.(*maasInstance).zone() + if err != nil { + logger.Errorf("could not get availability zone %v", err) + continue + } + zones[i] = z } return zones, nil } diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/provider/maas/environ_whitebox_test.go juju-core-1-1.25.5/src/github.com/juju/juju/provider/maas/environ_whitebox_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/provider/maas/environ_whitebox_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/provider/maas/environ_whitebox_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -199,7 +199,7 @@ env := suite.makeEnviron() // Create node 0: it will be used as the bootstrap node. suite.testMAASObject.TestServer.NewNode(fmt.Sprintf( - `{"system_id": "node0", "hostname": "host0", "architecture": "%s/generic", "memory": 1024, "cpu_count": 1}`, + `{"system_id": "node0", "hostname": "host0", "architecture": "%s/generic", "memory": 1024, "cpu_count": 1, "zone": {"name": "test_zone"}}`, arch.HostArch()), ) lshwXML, err := suite.generateHWTemplate(map[string]ifaceInfo{"aa:bb:cc:dd:ee:f0": {0, "eth0", false}}) @@ -225,7 +225,7 @@ // Create node 1: it will be used as instance number 1. suite.testMAASObject.TestServer.NewNode(fmt.Sprintf( - `{"system_id": "node1", "hostname": "host1", "architecture": "%s/generic", "memory": 1024, "cpu_count": 1}`, + `{"system_id": "node1", "hostname": "host1", "architecture": "%s/generic", "memory": 1024, "cpu_count": 1, "zone": {"name": "test_zone"}}`, arch.HostArch()), ) lshwXML, err = suite.generateHWTemplate(map[string]ifaceInfo{"aa:bb:cc:dd:ee:f1": {0, "eth0", false}}) @@ -235,7 +235,7 @@ c.Assert(err, jc.ErrorIsNil) c.Check(instance, gc.NotNil) c.Assert(hc, gc.NotNil) - c.Check(hc.String(), gc.Equals, fmt.Sprintf("arch=%s cpu-cores=1 mem=1024M", arch.HostArch())) + c.Check(hc.String(), gc.Equals, fmt.Sprintf("arch=%s cpu-cores=1 mem=1024M availability-zone=test_zone", arch.HostArch())) // The instance number 1 has been acquired and started. actions, found = operations["node1"] @@ -691,7 +691,7 @@ suite.setupFakeTools(c) env := suite.makeEnviron() suite.testMAASObject.TestServer.NewNode(fmt.Sprintf( - `{"system_id": "thenode", "hostname": "host", "architecture": "%s/generic", "memory": 256, "cpu_count": 8}`, + `{"system_id": "thenode", "hostname": "host", "architecture": "%s/generic", "memory": 256, "cpu_count": 8, "zone": {"name": "test_zone"}}`, arch.HostArch()), ) lshwXML, err := suite.generateHWTemplate(map[string]ifaceInfo{"aa:bb:cc:dd:ee:f0": {0, "eth0", false}}) @@ -705,7 +705,7 @@ suite.setupFakeTools(c) env := suite.makeEnviron() suite.testMAASObject.TestServer.NewNode(fmt.Sprintf( - `{"system_id": "thenode", "hostname": "host", "architecture": "%s/generic", "memory": 256, "cpu_count": 8}`, + `{"system_id": "thenode", "hostname": "host", "architecture": "%s/generic", "memory": 256, "cpu_count": 8, "zone": {"name": "test_zone"}}`, arch.HostArch()), ) lshwXML, err := suite.generateHWTemplate(map[string]ifaceInfo{"aa:bb:cc:dd:ee:f0": {0, "eth0", false}}) @@ -721,7 +721,7 @@ suite.setupFakeTools(c) env := suite.makeEnviron() suite.testMAASObject.TestServer.NewNode(fmt.Sprintf( - `{"system_id": "thenode", "hostname": "host", "architecture": "%s/generic", "memory": 256, "cpu_count": 8}`, + `{"system_id": "thenode", "hostname": "host", "architecture": "%s/generic", "memory": 256, "cpu_count": 8, "zone": {"name": "test_zone"}}`, arch.HostArch()), ) lshwXML, err := suite.generateHWTemplate(map[string]ifaceInfo{"aa:bb:cc:dd:ee:f0": {0, "eth0", false}}) @@ -1502,7 +1502,9 @@ s.testMAASObject.TestServer.AddZone("test-available", "description") inst, err := s.testStartInstanceAvailZone(c, "test-available") c.Assert(err, jc.ErrorIsNil) - c.Assert(inst.(*maasInstance).zone(), gc.Equals, "test-available") + zone, err := inst.(*maasInstance).zone() + c.Assert(err, jc.ErrorIsNil) + c.Assert(zone, gc.Equals, "test-available") } func (s *environSuite) TestStartInstanceAvailZoneUnknown(c *gc.C) { @@ -1688,6 +1690,7 @@ "architecture": fmt.Sprintf("%s/generic", arch.HostArch()), "memory": 1024, "cpu_count": 1, + "zone": map[string]string{"name": "test_zone"}, } for k, v := range attrs { allAttrs[k] = v @@ -1759,19 +1762,9 @@ s.testMAASObject.TestServer.AddZone("test-available", "description") s.newNode(c, "node1", "host1", map[string]interface{}{"zone": "test-available"}) inst, _ := testing.AssertStartInstance(c, env, "1") - c.Assert(inst.(*maasInstance).zone(), gc.Equals, "test-available") -} - -func (s *environSuite) TestStartInstanceDistributionAZNotImplemented(c *gc.C) { - env := s.bootstrap(c) - - mock := mockAvailabilityZoneAllocations{err: errors.NotImplementedf("availability zones")} - s.PatchValue(&availabilityZoneAllocations, mock.AvailabilityZoneAllocations) - - // Instance will be created without an availability zone specified. - s.newNode(c, "node1", "host1", nil) - inst, _ := testing.AssertStartInstance(c, env, "1") - c.Assert(inst.(*maasInstance).zone(), gc.Equals, "") + zone, err := inst.(*maasInstance).zone() + c.Assert(err, jc.ErrorIsNil) + c.Assert(zone, gc.Equals, "test-available") } func (s *environSuite) TestStartInstanceDistributionFailover(c *gc.C) { @@ -1791,7 +1784,9 @@ env := s.bootstrap(c) inst, _ := testing.AssertStartInstance(c, env, "1") - c.Assert(inst.(*maasInstance).zone(), gc.Equals, "zone2") + zone, err := inst.(*maasInstance).zone() + c.Assert(err, jc.ErrorIsNil) + c.Assert(zone, gc.Equals, "zone2") c.Assert(s.testMAASObject.TestServer.NodesOperations(), gc.DeepEquals, []string{ // one acquire for the bootstrap, three for StartInstance (with zone failover) "acquire", "acquire", "acquire", "acquire", diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/provider/maas/instance.go juju-core-1-1.25.5/src/github.com/juju/juju/provider/maas/instance.go --- juju-core-1-1.25.4/src/github.com/juju/juju/provider/maas/instance.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/provider/maas/instance.go 2016-04-15 00:07:21.000000000 +0000 @@ -138,9 +138,31 @@ return arch, subarch, nil } -func (mi *maasInstance) zone() string { - zone, _ := mi.getMaasObject().GetField("zone") - return zone +func (mi *maasInstance) zone() (string, error) { + // This is needed for MAAS prior to 1.9 + zone, fieldErr := mi.getMaasObject().GetField("zone") + if fieldErr == nil && zone != "" { + return zone, nil + } + + // MAAS 1.9 changes "zone" property to map + obj := mi.getMaasObject().GetMap()["zone"] + if obj.IsNil() { + return "", errors.New("zone property not set on maas") + } + zoneMap, err := obj.GetMap() + if err != nil { + return "", errors.New("zone property is not an expected type") + } + nameObj, ok := zoneMap["name"] + if !ok { + return "", errors.New("zone property is not set correctly: name is missing") + } + str, err := nameObj.GetString() + if err != nil { + return "", err + } + return str, nil } func (mi *maasInstance) cpuCount() (uint64, error) { @@ -192,7 +214,10 @@ if err != nil { return nil, errors.Annotate(err, "error determining available memory") } - zone := mi.zone() + zone, err := mi.zone() + if err != nil { + return nil, errors.Annotate(err, "error determining availability zone") + } hc := &instance.HardwareCharacteristics{ Arch: &nodeArch, CpuCores: &nodeCpuCount, diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/provider/maas/instance_test.go juju-core-1-1.25.5/src/github.com/juju/juju/provider/maas/instance_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/provider/maas/instance_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/provider/maas/instance_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -133,6 +133,7 @@ "system_id": "system_id", "architecture": "amd64/generic", "cpu_count": 6, + "zone": {"name": "tst"}, "memory": 16384 }` obj := s.testMAASObject.TestServer.NewNode(jsonValue) @@ -140,7 +141,7 @@ hc, err := inst.hardwareCharacteristics() c.Assert(err, jc.ErrorIsNil) c.Assert(hc, gc.NotNil) - c.Assert(hc.String(), gc.Equals, `arch=amd64 cpu-cores=6 mem=16384M`) + c.Assert(hc.String(), gc.Equals, `arch=amd64 cpu-cores=6 mem=16384M availability-zone=tst`) } func (s *instanceTest) TestHardwareCharacteristicsWithTags(c *gc.C) { @@ -149,6 +150,7 @@ "architecture": "amd64/generic", "cpu_count": 6, "memory": 16384, + "zone": {"name": "tst"}, "tag_names": ["a", "b"] }` obj := s.testMAASObject.TestServer.NewNode(jsonValue) @@ -156,7 +158,7 @@ hc, err := inst.hardwareCharacteristics() c.Assert(err, jc.ErrorIsNil) c.Assert(hc, gc.NotNil) - c.Assert(hc.String(), gc.Equals, `arch=amd64 cpu-cores=6 mem=16384M tags=a,b`) + c.Assert(hc.String(), gc.Equals, `arch=amd64 cpu-cores=6 mem=16384M tags=a,b availability-zone=tst`) } func (s *instanceTest) TestHardwareCharacteristicsMissing(c *gc.C) { @@ -166,7 +168,13 @@ `error determining cpu count: Requested float64, got .`) s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "architecture": "armhf", "cpu_count": 6}`, `error determining available memory: Requested float64, got .`) - s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "architecture": "armhf", "cpu_count": 6, "memory": 1, "tag_names": "wot"}`, + s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "architecture": "armhf", "cpu_count": 6, "memory": 1}`, + `error determining availability zone: zone property not set on maas`) + s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "architecture": "armhf", "cpu_count": 6, "memory": 1, "zone": ""}`, + `error determining availability zone: zone property is not an expected type`) + s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "architecture": "armhf", "cpu_count": 6, "memory": 1, "zone": {}}`, + `error determining availability zone: zone property is not set correctly: name is missing`) + s.testHardwareCharacteristicsMissing(c, `{"system_id": "id", "architecture": "armhf", "cpu_count": 6, "memory": 1, "zone": {"name": "tst"}, "tag_names": "wot"}`, `error determining tag names: Requested array, got string.`) } diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/scripts/win-installer/setup.iss juju-core-1-1.25.5/src/github.com/juju/juju/scripts/win-installer/setup.iss --- juju-core-1-1.25.4/src/github.com/juju/juju/scripts/win-installer/setup.iss 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/scripts/win-installer/setup.iss 2016-04-15 00:07:21.000000000 +0000 @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Juju" -#define MyAppVersion "1.25.4" +#define MyAppVersion "1.25.5" #define MyAppPublisher "Canonical, Ltd" #define MyAppURL "http://juju.ubuntu.com/" #define MyAppExeName "juju.exe" diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/service/windows/service.go juju-core-1-1.25.5/src/github.com/juju/juju/service/windows/service.go --- juju-core-1-1.25.4/src/github.com/juju/juju/service/windows/service.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/service/windows/service.go 2016-04-15 00:07:21.000000000 +0000 @@ -221,7 +221,7 @@ return errors.Errorf("Service %s already installed", s.Service.Name) } - logger.Infof("Installing Service %v", s.Name) + logger.Infof("Installing Service %v", s.Name()) err = s.manager.Create(s.Name(), s.Conf()) if err != nil { return errors.Trace(err) diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/version/version.go juju-core-1-1.25.5/src/github.com/juju/juju/version/version.go --- juju-core-1-1.25.4/src/github.com/juju/juju/version/version.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/version/version.go 2016-04-15 00:07:21.000000000 +0000 @@ -24,7 +24,7 @@ // The presence and format of this constant is very important. // The debian/rules build recipe uses this value for the version // number of the release package. -const version = "1.25.4" +const version = "1.25.5" // The version that we switched over from old style numbering to new style. var switchOverVersion = MustParse("1.19.9") diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/worker/provisioner/provisioner.go juju-core-1-1.25.5/src/github.com/juju/juju/worker/provisioner/provisioner.go --- juju-core-1-1.25.4/src/github.com/juju/juju/worker/provisioner/provisioner.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/worker/provisioner/provisioner.go 2016-04-15 00:07:21.000000000 +0000 @@ -128,7 +128,7 @@ tag := p.agentConfig.Tag() machineTag, ok := tag.(names.MachineTag) if !ok { - errors.Errorf("expacted names.MachineTag, got %T", tag) + errors.Errorf("expected names.MachineTag, got %T", tag) } envCfg, err := p.st.EnvironConfig() @@ -190,7 +190,9 @@ } p.broker = p.environ - harvestMode := p.environ.Config().ProvisionerHarvestMode() + modelConfig := p.environ.Config() + p.configObserver.notify(modelConfig) + harvestMode := modelConfig.ProvisionerHarvestMode() task, err := p.getStartTask(harvestMode) if err != nil { return utils.LoggedErrorStack(errors.Trace(err)) @@ -211,11 +213,10 @@ } environConfig, err := p.st.EnvironConfig() if err != nil { - logger.Errorf("cannot load environment configuration: %v", err) - return err + return errors.Annotate(err, "cannot load environment configuration") } if err := p.setConfig(environConfig); err != nil { - logger.Errorf("loaded invalid environment configuration: %v", err) + return errors.Annotate(err, "loaded invalid environment configuration") } task.SetHarvestMode(environConfig.ProvisionerHarvestMode()) } @@ -282,6 +283,7 @@ if err != nil { return err } + p.configObserver.notify(config) harvestMode := config.ProvisionerHarvestMode() task, err := p.getStartTask(harvestMode) diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/worker/proxyupdater/manifold.go juju-core-1-1.25.5/src/github.com/juju/juju/worker/proxyupdater/manifold.go --- juju-core-1-1.25.4/src/github.com/juju/juju/worker/proxyupdater/manifold.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/worker/proxyupdater/manifold.go 2016-04-15 00:07:21.000000000 +0000 @@ -5,7 +5,6 @@ import ( "github.com/juju/juju/api/base" - "github.com/juju/juju/api/environment" "github.com/juju/juju/worker" "github.com/juju/juju/worker/dependency" "github.com/juju/juju/worker/util" @@ -23,8 +22,5 @@ // newWorker is not currently tested; it should eventually replace New as the // package's exposed factory func, and then all tests should pass through it. func newWorker(apiCaller base.APICaller) (worker.Worker, error) { - // TODO(fwereade): This shouldn't be an "environment" facade, it - // should be specific to the proxyupdater, and be watching for - // *proxy settings* changes, not just watching the "environment". - return New(environment.NewFacade(apiCaller), false), nil + return New(apiCaller, false), nil } diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/worker/proxyupdater/proxyupdater.go juju-core-1-1.25.5/src/github.com/juju/juju/worker/proxyupdater/proxyupdater.go --- juju-core-1-1.25.4/src/github.com/juju/juju/worker/proxyupdater/proxyupdater.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/worker/proxyupdater/proxyupdater.go 2016-04-15 00:07:21.000000000 +0000 @@ -15,8 +15,10 @@ "github.com/juju/utils/packaging/config" proxyutils "github.com/juju/utils/proxy" - "github.com/juju/juju/api/environment" + "github.com/juju/juju/api/base" + apiproxyupdater "github.com/juju/juju/api/proxyupdater" "github.com/juju/juju/api/watcher" + "github.com/juju/juju/apiserver/params" "github.com/juju/juju/version" "github.com/juju/juju/worker" ) @@ -39,13 +41,20 @@ Started = func() {} ) +// API is an interface that is provided to New +// which can be used to fetch the API host ports +type API interface { + ProxyConfig() (params.ProxyConfigResult, error) + WatchForProxyConfigAndAPIHostPortChanges() (watcher.NotifyWatcher, error) +} + // proxyWorker is responsible for monitoring the juju environment // configuration and making changes on the physical (or virtual) machine as // necessary to match the environment changes. Examples of these types of // changes are apt proxy configuration and the juju proxies stored in the juju // proxy file. type proxyWorker struct { - api *environment.Facade + api API aptProxy proxyutils.Settings proxy proxyutils.Settings @@ -65,10 +74,11 @@ // New returns a worker.Worker that updates proxy environment variables for the // process; and, if writeSystemFiles is true, for the whole machine. -var New = func(api *environment.Facade, writeSystemFiles bool) worker.Worker { +var New = func(api base.APICaller, writeSystemFiles bool) worker.Worker { + proxyAPI := apiproxyupdater.NewAPI(api) logger.Debugf("write system files: %v", writeSystemFiles) envWorker := &proxyWorker{ - api: api, + api: proxyAPI, writeSystemFiles: writeSystemFiles, first: true, } @@ -186,16 +196,13 @@ } func (w *proxyWorker) onChange() error { - env, err := w.api.EnvironConfig() + cfg, err := w.api.ProxyConfig() if err != nil { return err } - w.handleProxyValues(env.ProxySettings()) - err = w.handleAptProxyValues(env.AptProxySettings()) - if err != nil { - return err - } - return nil + + w.handleProxyValues(cfg.ProxySettings) + return w.handleAptProxyValues(cfg.APTProxySettings) } // SetUp is defined on the worker.NotifyWatchHandler interface. @@ -208,7 +215,7 @@ } w.first = false Started() - return w.api.WatchForEnvironConfigChanges() + return w.api.WatchForProxyConfigAndAPIHostPortChanges() } // Handle is defined on the worker.NotifyWatchHandler interface. diff -Nru juju-core-1-1.25.4/src/github.com/juju/juju/worker/proxyupdater/proxyupdater_test.go juju-core-1-1.25.5/src/github.com/juju/juju/worker/proxyupdater/proxyupdater_test.go --- juju-core-1-1.25.4/src/github.com/juju/juju/worker/proxyupdater/proxyupdater_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/github.com/juju/juju/worker/proxyupdater/proxyupdater_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -52,9 +52,6 @@ func (s *ProxyUpdaterSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) s.apiRoot, s.machine = s.OpenAPIAsNewMachine(c) - // Create the environment API facade. - s.environmentAPI = s.apiRoot.Environment() - c.Assert(s.environmentAPI, gc.NotNil) proxyDir := c.MkDir() s.PatchValue(&proxyupdater.ProxyDirectory, proxyDir) @@ -80,7 +77,7 @@ case <-time.After(10 * time.Millisecond): obtained := proxy.DetectProxies() if obtained != expected { - c.Logf("proxy settings are %#v, still waiting", obtained) + c.Logf("proxy settings are \n%#v, should be \n%#v, still waiting", obtained, expected) continue } return @@ -113,7 +110,7 @@ } func (s *ProxyUpdaterSuite) TestRunStop(c *gc.C) { - updater := proxyupdater.New(s.environmentAPI, false) + updater := proxyupdater.New(s.apiRoot, false) c.Assert(worker.Stop(updater), gc.IsNil) } @@ -123,7 +120,7 @@ Http: "http proxy", Https: "https proxy", Ftp: "ftp proxy", - NoProxy: "no proxy", + NoProxy: "localhost,no proxy", } attrs := map[string]interface{}{} for k, v := range config.ProxyConfigMap(proxySettings) { @@ -152,7 +149,7 @@ func (s *ProxyUpdaterSuite) TestInitialState(c *gc.C) { proxySettings, aptProxySettings := s.updateConfig(c) - updater := proxyupdater.New(s.environmentAPI, true) + updater := proxyupdater.New(s.apiRoot, true) defer worker.Stop(updater) s.waitProxySettings(c, proxySettings) @@ -166,7 +163,7 @@ func (s *ProxyUpdaterSuite) TestWriteSystemFiles(c *gc.C) { proxySettings, aptProxySettings := s.updateConfig(c) - updater := proxyupdater.New(s.environmentAPI, true) + updater := proxyupdater.New(s.apiRoot, true) defer worker.Stop(updater) s.waitForPostSetup(c) @@ -190,7 +187,7 @@ proxySettings, _ := s.updateConfig(c) - updater := proxyupdater.New(s.environmentAPI, true) + updater := proxyupdater.New(s.apiRoot, true) defer worker.Stop(updater) s.waitForPostSetup(c) s.waitProxySettings(c, proxySettings) @@ -208,7 +205,7 @@ func (s *ProxyUpdaterSuite) TestDontWriteSystemFiles(c *gc.C) { proxySettings, _ := s.updateConfig(c) - updater := proxyupdater.New(s.environmentAPI, false) + updater := proxyupdater.New(s.apiRoot, false) defer worker.Stop(updater) s.waitForPostSetup(c) diff -Nru juju-core-1-1.25.4/src/gopkg.in/juju/charm.v5/meta.go juju-core-1-1.25.5/src/gopkg.in/juju/charm.v5/meta.go --- juju-core-1-1.25.4/src/gopkg.in/juju/charm.v5/meta.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/gopkg.in/juju/charm.v5/meta.go 2016-04-15 00:07:21.000000000 +0000 @@ -268,7 +268,14 @@ meta.OldRevision = int(m["revision"].(int64)) } if series, ok := m["series"]; ok && series != nil { - meta.Series = series.(string) + multiseries, ok := series.([]interface{}) + if ok { + if len(multiseries) > 0 { + meta.Series = multiseries[0].(string) + } + } else { + meta.Series = series.(string) + } } meta.Storage = parseStorage(m["storage"]) meta.PayloadClasses = parsePayloadClasses(m["payloads"]) @@ -672,7 +679,7 @@ "subordinate": schema.Bool(), "categories": schema.List(schema.String()), "tags": schema.List(schema.String()), - "series": schema.String(), + "series": schema.OneOf(schema.String(), schema.List(schema.String())), "storage": schema.StringMap(storageSchema), "payloads": schema.StringMap(payloadClassSchema), }, diff -Nru juju-core-1-1.25.4/src/gopkg.in/juju/charm.v5/meta_test.go juju-core-1-1.25.5/src/gopkg.in/juju/charm.v5/meta_test.go --- juju-core-1-1.25.4/src/gopkg.in/juju/charm.v5/meta_test.go 2016-04-01 00:47:26.000000000 +0000 +++ juju-core-1-1.25.5/src/gopkg.in/juju/charm.v5/meta_test.go 2016-04-15 00:07:21.000000000 +0000 @@ -278,6 +278,28 @@ } } +var multiSeriesTests = []struct{ + yamllist string + result string +}{ + {`["trusty", "precise"]`, "trusty"}, + {`["trusty", "xenial"]`, "trusty"}, + {`["wonky", "trusty", "happy"]`, "wonky"}, + {`[]`, ""}, +} + +// TestMultiSeries validates that new style multi series charms are parsed +// and pick the first listed series. +func (s *MetaSuite) TestMultiSeries(c *gc.C) { + for _, test := range multiSeriesTests { + c.Logf("test %q", test.yamllist) + meta, err := charm.ReadMeta(strings.NewReader( + fmt.Sprintf("%s\nseries: %s\n", dummyMetadata, test.yamllist))) + c.Assert(err, gc.IsNil) + c.Check(meta.Series, gc.Equals, test.result) + } +} + func (s *MetaSuite) TestCheckMismatchedRelationName(c *gc.C) { // This Check case cannot be covered by the above // TestRelationsConstraints tests.