Skip to content

Commit

Permalink
Merge branch '2.4' into merge-2.4-20180824
Browse files Browse the repository at this point in the history
  • Loading branch information
wallyworld committed Aug 23, 2018
2 parents 719b1b9 + fde9276 commit c4c1ab7
Show file tree
Hide file tree
Showing 37 changed files with 397 additions and 109 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ TAGS
parts/
prime/
stage/
vendor/
*.snap
.emacs.desktop
.emacs.desktop.lock
Expand All @@ -20,4 +21,3 @@ stage/
.idea/
*.pyc
.vscode/
vendor/
4 changes: 4 additions & 0 deletions acceptancetests/assess_constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@ def deploy_charm_constraint(client, constraints, charm_name, charm_series,
constraints_charm = Charm(charm_name,
'Test charm for constraints',
series=[charm_series])
# Valid charms require at least one hook.
# Add a dummy install hook.
install = '#!/bin/sh\necho install'
constraints_charm.add_hook_script('install', install)
charm_root = constraints_charm.to_repo_dir(charm_dir)
platform = 'ubuntu'
charm = local_charm_path(charm=charm_name,
Expand Down
8 changes: 5 additions & 3 deletions acceptancetests/assess_container_networking.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def assess_network_traffic(client, targets):
ssh(client, dest,
'echo "{msg}" | nc {addr} 6778'.format(msg=msg, addr=address))
result = ssh(client, source, 'more nc_listen.out')
if result.rstrip() != msg:
if msg not in result:
raise ValueError("Wrong or missing message: %r" % result.rstrip())
log.info('SUCCESS.')

Expand Down Expand Up @@ -334,15 +334,17 @@ def assess_container_networking(client, types):
for host in hosts:
log.info("Restarting hosted machine: {}".format(host))
client.juju(
'run', ('--machine', host, 'sudo shutdown -r now'))
'run', ('--machine', host, 'sudo shutdown -r +1'))
client.juju('show-action-status', ('--name', 'juju-run'))

log.info("Restarting controller machine 0")
controller_client = client.get_controller_client()
controller_status = controller_client.get_status()
controller_host = controller_status.status['machines']['0']['dns-name']
first_uptime = get_uptime(controller_client, '0')
ssh(controller_client, '0', 'sudo shutdown -r now')
ssh(controller_client, '0', 'sudo shutdown -r +1')
# Ensure the reboots have started.
time.sleep(70)
except subprocess.CalledProcessError as e:
logging.info(
"Error running shutdown:\nstdout: %s\nstderr: %s",
Expand Down
4 changes: 4 additions & 0 deletions acceptancetests/assess_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,10 @@ def storage_pool_list(client):
def create_storage_charm(charm_dir, name, summary, storage):
"""Manually create a temporary charm to test with storage."""
storage_charm = Charm(name, summary, storage=storage, series=['trusty'])
# Valid charms require at least one hook.
# Add a dummy install hook.
install = '#!/bin/sh\necho install'
storage_charm.add_hook_script('install', install)
charm_root = storage_charm.to_repo_dir(charm_dir)
return charm_root

Expand Down
6 changes: 3 additions & 3 deletions acceptancetests/jujupy/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2194,11 +2194,11 @@ def register_user_interactively(client, token, controller_name):
"""
try:
child = client.expect('register', (token), include_e=False)
child.expect('(?i)password')
child.expect('Enter a new password:')
child.sendline(client.env.user_name + '_password')
child.expect('(?i)password')
child.expect('Confirm password:')
child.sendline(client.env.user_name + '_password')
child.expect('(?i)name')
child.expect('Enter a name for this controller \[.*\]:')
child.sendline(controller_name)
client._end_pexpect_session(child)
except pexpect.TIMEOUT:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ func writeSizes(glob string) error {
// but it's a lot more convenient for parsing if it's first in the output.
for i, j := len(paths)-1, 0; i >= 0; i-- {
path := paths[i]
// Skip any log files that start with machine-lock as these aren't interesting
// for fill logs.
if strings.HasPrefix(filepath.Base(path), "machine-lock") {
continue
}
info, err := os.Stat(path)
if err != nil {
return fmt.Errorf("error stating log %q: %s", path, err)
Expand Down
4 changes: 2 additions & 2 deletions acceptancetests/substrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,8 @@ def convert_to_azure_ids(self, client, instance_ids):
self.arm_client, glob=resource_group, recursive=True)
vm_ids = []
for machine_name in instance_ids:
rgd, vm = winazurearm.find_vm_instance(
resources, machine_name, resource_group)
rgd, vm = winazurearm.find_vm_deployment(
resource_group, machine_name)
vm_ids.append(vm.vm_id)
return vm_ids

Expand Down
9 changes: 7 additions & 2 deletions api/apiclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -840,6 +840,7 @@ type dialer struct {
// when appropriate.
func (d dialer) dial(_ <-chan struct{}) (io.Closer, error) {
a := retry.StartWithCancel(d.openAttempt, d.opts.Clock, d.ctx.Done())
var lastErr error = nil
for a.Next() {
conn, tlsConfig, err := d.dial1()
if err == nil {
Expand All @@ -853,11 +854,15 @@ func (d dialer) dial(_ <-chan struct{}) (io.Closer, error) {
}
if isX509Error(err) || !a.More() {
// certificate errors don't improve with retries.
logger.Debugf("error dialing websocket: %v", err)
return nil, errors.Annotatef(err, "unable to connect to API")
}
lastErr = err
}
return nil, parallel.ErrStopped
if lastErr == nil {
logger.Debugf("no error, but not connected, probably cancelled before we started")
return nil, parallel.ErrStopped
}
return nil, errors.Trace(lastErr)
}

// dial1 makes a single dial attempt.
Expand Down
81 changes: 81 additions & 0 deletions api/apiclient_whitebox_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright 2018 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package api

import (
"context"
"fmt"
"net"
"regexp"
"time"

"github.com/juju/testing"
jc "github.com/juju/testing/checkers"
gc "gopkg.in/check.v1"

jtesting "github.com/juju/juju/testing"
)

type apiclientWhiteboxSuite struct {
testing.IsolationSuite
}

var _ = gc.Suite(&apiclientWhiteboxSuite{})

func (s *apiclientWhiteboxSuite) TestDialWebsocketMultiCancelled(c *gc.C) {
ctx := context.TODO()
ctx, cancel := context.WithCancel(ctx)
started := make(chan struct{})
go func() {
select {
case <-started:
case <-time.After(jtesting.LongWait):
c.Fatalf("timed out waiting %s for started", jtesting.LongWait)
}
<-time.After(10 * time.Millisecond)
if cancel != nil {
c.Logf("cancelling")
cancel()
}
}()
listen, err := net.Listen("tcp4", ":0")
c.Assert(err, jc.ErrorIsNil)
addr := listen.Addr().String()
c.Logf("listening at: %s", addr)
// Note that we Listen, but we never Accept
close(started)
info := &Info{
Addrs: []string{addr},
}
opts := DialOpts{
DialAddressInterval: 50 * time.Millisecond,
RetryDelay: 40 * time.Millisecond,
Timeout: 100 * time.Millisecond,
DialTimeout: 100 * time.Millisecond,
}
// Close before we connect
listen.Close()
_, err = dialAPI(ctx, info, opts)
c.Check(err, gc.ErrorMatches, fmt.Sprintf("dial tcp %s:.*", regexp.QuoteMeta(addr)))
}

func (s *apiclientWhiteboxSuite) TestDialWebsocketMultiClosed(c *gc.C) {
listen, err := net.Listen("tcp4", ":0")
c.Assert(err, jc.ErrorIsNil)
addr := listen.Addr().String()
c.Logf("listening at: %s", addr)
// Note that we Listen, but we never Accept
info := &Info{
Addrs: []string{addr},
}
opts := DialOpts{
DialAddressInterval: 1 * time.Second,
RetryDelay: 1 * time.Second,
Timeout: 2 * time.Second,
DialTimeout: 3 * time.Second,
}
listen.Close()
_, _, err = DialAPI(info, opts)
c.Check(err, gc.ErrorMatches, fmt.Sprintf("unable to connect to API: dial tcp %s:.*", regexp.QuoteMeta(addr)))
}
1 change: 1 addition & 0 deletions api/applicationoffers/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func (c *Client) ListOffers(filters ...crossmodel.ApplicationOfferFilter) ([]*cr
var paramsFilter params.OfferFilters
for _, f := range filters {
filterTerm := params.OfferFilter{
OwnerName: f.OwnerName,
ModelName: f.ModelName,
OfferName: f.OfferName,
ApplicationName: f.ApplicationName,
Expand Down
4 changes: 4 additions & 0 deletions api/applicationoffers/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ func (s *crossmodelMockSuite) TestList(c *gc.C) {
relations := []jujucrossmodel.EndpointFilterTerm{{Name: "endPointA", Interface: "http"}}

filter := jujucrossmodel.ApplicationOfferFilter{
OwnerName: "fred",
ModelName: "prod",
OfferName: offerName,
Endpoints: relations,
ApplicationName: "mysql",
Expand All @@ -128,6 +130,8 @@ func (s *crossmodelMockSuite) TestList(c *gc.C) {
c.Assert(ok, jc.IsTrue)
c.Assert(args.Filters, gc.HasLen, 1)
c.Assert(args.Filters[0], jc.DeepEquals, params.OfferFilter{
OwnerName: "fred",
ModelName: "prod",
OfferName: filter.OfferName,
ApplicationName: filter.ApplicationName,
Endpoints: []params.EndpointFilterAttributes{{
Expand Down
2 changes: 1 addition & 1 deletion api/bundle/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func NewClient(st base.APICallCloser) *Client {
func (c *Client) ExportBundle() (string, error) {
var result params.StringResult
if bestVer := c.BestAPIVersion(); bestVer < 2 {
return "", errors.Errorf("ExportBundle() on v%d is not supported", bestVer)
return "", errors.Errorf("this controller version does not support bundle export feature.")
}

if err := c.facade.FacadeCall("ExportBundle", nil, &result); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion api/bundle/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (s *bundleMockSuite) TestFailExportBundlev1(c *gc.C) {
)
result, err := client.ExportBundle()
c.Assert(err, gc.NotNil)
c.Assert(err.Error(), gc.Equals, "ExportBundle() on v1 is not supported")
c.Assert(err.Error(), gc.Equals, "this controller version does not support bundle export feature.")
c.Assert(result, jc.DeepEquals, "")
}

Expand Down
5 changes: 2 additions & 3 deletions api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
"strings"

Expand Down Expand Up @@ -361,8 +360,8 @@ func hasHooksFolder(name string) (bool, error) {
}
defer zipr.Close()
count := 0
// Cater for file separator differences between operating systems.
hooksPath := filepath.FromSlash("hooks/")
// zip file spec 4.4.17.1 says that separators are always "/" even on Windows.
hooksPath := "hooks/"
for _, f := range zipr.File {
if strings.Contains(f.Name, hooksPath) {
count++
Expand Down
11 changes: 11 additions & 0 deletions cmd/juju/application/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
"github.com/juju/juju/core/devices"
"github.com/juju/juju/core/model"
"github.com/juju/juju/environs/config"
"github.com/juju/juju/instance"
"github.com/juju/juju/resource/resourceadapters"
"github.com/juju/juju/storage"
)
Expand Down Expand Up @@ -817,6 +818,15 @@ func (c *DeployCommand) deployCharm(
return errors.New("this juju controller does not support --attach-storage")
}

// Storage cannot be added to a container.
if len(c.Storage) > 0 || len(c.AttachStorage) > 0 {
for _, placement := range c.Placement {
if t, err := instance.ParseContainerType(placement.Scope); err == nil {
return errors.NotSupportedf("adding storage to %s container", string(t))
}
}
}

numUnits := c.NumUnits
if charmInfo.Meta.Subordinate {
if !constraints.IsEmpty(&c.Constraints) {
Expand Down Expand Up @@ -948,6 +958,7 @@ func (c *DeployCommand) deployCharm(
if len(appConfig) == 0 {
appConfig = nil
}

args := application.DeployArgs{
CharmID: id,
Cons: c.Constraints,
Expand Down
33 changes: 33 additions & 0 deletions cmd/juju/application/deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,15 @@ func (s *CAASDeploySuite) TestDevices(c *gc.C) {
})
}

func (s *DeploySuite) TestDeployStorageFailContainer(c *gc.C) {
ch := testcharms.Repo.ClonedDirPath(s.CharmsPath, "dummy")
machine, err := s.State.AddMachine(version.SupportedLTS(), state.JobHostUnits)
c.Assert(err, jc.ErrorIsNil)
container := "lxd:" + machine.Id()
err = runDeploy(c, ch, "--to", container, "--storage", "data=machinescoped,1G")
c.Assert(err, gc.ErrorMatches, "adding storage to lxd container not supported")
}

func (s *DeploySuite) TestPlacement(c *gc.C) {
ch := testcharms.Repo.ClonedDirPath(s.CharmsPath, "dummy")
// Add a machine that will be ignored due to placement directive.
Expand Down Expand Up @@ -1736,6 +1745,30 @@ func (s *DeployUnitTestSuite) TestDeployAttachStorage(c *gc.C) {
c.Assert(err, jc.ErrorIsNil)
}

func (s *DeployUnitTestSuite) TestDeployAttachStorageFailContainer(c *gc.C) {
charmsPath := c.MkDir()
charmDir := testcharms.Repo.ClonedDir(charmsPath, "dummy")

fakeAPI := vanillaFakeModelAPI(map[string]interface{}{
"name": "name",
"uuid": "deadbeef-0bad-400d-8000-4b1d0d06f00d",
"type": "foo",
})

dummyURL := charm.MustParseURL("local:trusty/dummy-0")
withLocalCharmDeployable(fakeAPI, dummyURL, charmDir)
withCharmDeployable(
fakeAPI, dummyURL, "trusty", charmDir.Meta(), charmDir.Metrics(), false, 1, []string{"foo/0", "bar/1", "baz/2"}, nil,
)

cmd := NewDeployCommandForTest(func() (DeployAPI, error) { return fakeAPI, nil }, nil)
cmd.SetClientStore(jujuclienttesting.MinimalStore())
_, err := cmdtesting.RunCommand(c, cmd, dummyURL.String(),
"--attach-storage", "foo/0", "--to", "lxd",
)
c.Assert(err, gc.ErrorMatches, "adding storage to lxd container not supported")
}

func (s *DeployUnitTestSuite) TestDeployAttachStorageNotSupported(c *gc.C) {
charmsPath := c.MkDir()
charmDir := testcharms.Repo.ClonedDir(charmsPath, "dummy")
Expand Down
Loading

0 comments on commit c4c1ab7

Please sign in to comment.