Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding update-series command by application. #7677

Merged
merged 2 commits into from
Aug 16, 2017

Conversation

hmlanigan
Copy link
Member

@hmlanigan hmlanigan commented Jul 28, 2017

Description of change

The update-series command will allow a user to juju of a new series for an application or a machine. This initial commit covers the application case where updating the series of an application allows for new units of the application to use the new series.

The new series must be in the supported series list of the related charm. As well, charms subordinate
to the one specified must also support the new series. A --force flag allows this behavior to be over ridden.

QA steps

  1. Deploy two charms, one a subordinate. It's recommended to have 1 series supported by the principal charm not supported by the subordinate.
  2. juju update-series ; where the series is supported by both the principal and subordinates. This should succeed.
  3. juju add-unit ; the new unit should be provisioned with the series used in step 2.
  4. juju update-series ; where the series is supported by the principal and not the subordinate. This should fail.
  5. juju update-series --force; where the series is supported by the principal and not the subordinate. This should succeed.
  6. juju update-series ; where the series is not supported by the principal. This should fail.
  7. juju update-series --force; where the series is not supported by the principal. This should succeed.

At any time, the series to be used by an application can be found in the output of "juju status --format yaml"

Documentation changes

Yes, the online list of commands needs to be updated.

Bug reference

This is a planned project, however a feature request bug was filed as well:
https://bugs.launchpad.net/juju/+bug/1704135

Copy link
Member

@wallyworld wallyworld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some initial comments....

@@ -123,6 +123,7 @@ func AllFacades() *facade.Registry {
reg("Application", 2, application.NewFacade)
reg("Application", 3, application.NewFacade)
reg("Application", 4, application.NewFacade)
reg("Application", 5, application.NewFacade) // UpdateSeries
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the preferred way to do it - the fact that it was done this way previously was a mistake. See StorageProvisionerAPIv3 and StorageProvisionerAPIv4 for the right way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

There is a side question on this. Version 5 is already used in develop. What is the correct version to use here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

if !app.IsPrincipal() {
return errors.New(fmt.Sprintf("%s is a subordinate application, series update is not supported.", args.ApplicationName))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Errorf

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

if err != nil {
return errors.Trace(err)
}
// all units will have the same subordinates, therefore we only
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

// all units will have the same subordinates, therefore we only
// need to check one.
unit := units[0]
if unit != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When would unit ever be nil? I don't think we need this check. For safety, we should error though if len(units) == 0

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
_, seriesSupportedErr := charm.SeriesForCharm(args.Series, subordinateCharm.Meta().Series)
if seriesSupportedErr != nil && !args.Force {
return errors.New(fmt.Sprintf("series %s is not supported by the subordinates of the application in use. Use --force to override.", args.Series))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Errorf.
Also, the apiserver layer is not the place to put error messages. You need to define a new error type and have the caller react to that. The API is called by more than just the CLI. eg Juju Python library. There's no such thing as --force there.
See apiserver/params/apierror.go

@@ -1480,6 +1480,91 @@ func (s *applicationSuite) TestApplicationUpdateInvalidApplication(c *gc.C) {
c.Assert(err, gc.ErrorMatches, `application "no-such-application" not found`)
}

func (s *applicationSuite) TestApplicationUpdateSeries(c *gc.C) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New tests should be added to application_unit_test.go
The tests in application_test.go use JujuConnSuite which we are trying to move away from

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed changes to application_test.go and added unit tests to application_unit_tests.go

}

var updateSeriesDoc = `
It is recommended to update the series on existing units before running this
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to necessarily recommend this? There's no real harm in having existing units on trusty and new ones on xenial. It's really outside of Juju's domain and more of a charm thing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking in terms of when updating the series of a machine is requested, not a the application data. e.g.: ..."running this command to update a machines' series.

command.

By updating the application series, future units of the application to use
the new series. The series for subordinate applications is also updated.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not mention subordinates here. There's no real point as people don't use or pay attention to the series there. They don't care - they are just interested in the principal.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

the new series. The series for subordinate applications is also updated.

The update is disallowed unless the --force flag is used if the series is not
explicitly supported:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"... if the requested series is not explicitly supported by the application's charm and any subordinates, as well as any other charms which may be deployed to the same machine."

You can add a comment that the user may wish to upgrade the charm to a later revision that does support their preferred series.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.


Examples:
juju update-series <application> <series>
juju update-series <machine> <series>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include a --force example

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@hmlanigan hmlanigan force-pushed the update-series branch 5 times, most recently from b3e8cb7 to bc32899 Compare August 4, 2017 18:45
@hmlanigan hmlanigan changed the title WIP: Adding update-series command by application. Adding update-series command by application. Aug 4, 2017
Copy link
Member

@wallyworld wallyworld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A good iteration on the first commit with some good progress towards how things need to be. There's a few issues with the apiserver layer to sort out. In the state layer, we need tests for the concurrency checks, and there's also logic and asserts missing.

@@ -219,6 +219,11 @@ func (c *Client) Update(args params.ApplicationUpdate) error {
return c.facade.FacadeCall("Update", args, nil)
}

// UpdateApplicationSeries updates the application series.
func (c *Client) UpdateApplicationSeries(args params.ApplicationUpdateSeries) error {
return c.facade.FacadeCall("UpdateApplicationSeries", args, nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a bulk call. The api layer typically takes a singular argument like here. But what is passed over the wire to the apiserver facade is a set of bulk args. There's lot of examples to cargo cult, but it would be something like:

type UpdateApplicationSeriesArg struct {
ApplicationName string
Force bool
etc
}

type UpateApplicationSeriesArgs struct {
Args []UpdateApplicationSeriesArg
}

and then

args := params.UpateApplicationSeriesArgs {
Args: []params.UpdateApplicationSeriesArg{arg}
}
var results params.ErrorResults
err := c.facade.FacadeCall("UpdateApplicationSeries", args, results)
if err != nil {
return err
}
return results.OneError()

As I said, lots of examples to copy off, but the above pseudo code should get you started.
On the server side, the pattern is to define a func to do one arg at a time, and loop over the supplied args in the args parameter, setting the results.Error for each one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

if err := api.checkCanWrite(); err != nil {
return err
}
if !args.Force {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if force is true, if changes are blocked, it should still error. Force is only to bypass incompatible series, not change blocks

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rechecked the examples I was using. Removed the Force part.

if err != nil {
return errors.Trace(err)
}
if args.Series == "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be checked first as it is cheap to check. We should not waste cpu cycles, db access etc loading the application if a simple arg check would fail

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved

}
if err = verifySupportedSeries(app, args.Series, args.Force); err != nil {
if params.IsCodeIncompatibleSeries(err) {
err = errors.Annotatef(err, "series %s is not supported by the application in use. Use --force to override.", args.Series)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No! No CLI related messages at the apiserver layer. These facades are called by external client libraries as well, eg juju python lib. You don't need the params.IsCodeIncompatibleSeries(err) check here at all.

As well, all errors returned from the apiserver facade methods need to be wrapped like so:
return common.ServerError(err)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrapped appropriate errors in common.ServerError. Removed checks highlighted in comment.

return err
}

if !app.IsPrincipal() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check should be further up - why go to all the trouble of checking for supported series if we won't update anything anyway because of the app being a subordinate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved.

}

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

func (s *ApplicationSuite) TearDownTest(c *gc.C) {
for _, a := range s.applications {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implies that we are leaking applications between tests. s.application should start out empty or with newly cnstructed mocks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

Assert: bson.D{{"charmurl", chURL}},
}

var ops []txn.Op
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ops here should also include the ones to update any subordinates, instead of doing that in the apiserver layer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved.

if err != nil {
return errors.Trace(err)
}
var unitOps []txn.Op
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These ops all need to be built up inside a buildTxn() function, with matching business logic to perform the same checks as what the asserts do.
(as an aside, there's no model is alive check and there needs to be).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildTxn() function added

@@ -922,6 +922,16 @@ func (s *ApplicationSuite) TestUpdateConfigSettings(c *gc.C) {
}
}

func (s *ApplicationSuite) TestUpdateApplicationSeries(c *gc.C) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need tests that check the behaviour when things change concurrently (eg adding subordinates). This is done using the various txn hook helpers See various uses of state.SetBeforeHooks, state.SetAfterHooks, state.SetRetryHooks

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

starting to add these tests.

unitOps = append(unitOps, txn.Op{
C: unitsC,
Id: u.doc.DocID,
Assert: bson.D{{"subordinates", subNames}},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this sufficient? Maybe it is! I thought maybe it would have been a bit more complicated, but I am probably wrong. A test to check all this is needed to be sure (using the txn hook helpers).

@hmlanigan hmlanigan force-pushed the update-series branch 4 times, most recently from 0718e0d to 2a576fd Compare August 10, 2017 20:41
@hmlanigan
Copy link
Member Author

!!build!!

@hmlanigan
Copy link
Member Author

!!build!!

1 similar comment
@hmlanigan
Copy link
Member Author

!!build!!

Copy link
Member

@wallyworld wallyworld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good progress, not quite done yet, some more comments

if err != nil {
return errors.Trace(err)
}
return errors.Trace(results.OneError())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typically we don't need trace here. Don't hurt but not really needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

@@ -78,6 +94,11 @@ func NewFacade(ctx facade.Context) (*API, error) {
)
}

// NewAPIv5 creates a new server-side API v5 facade.
func NewAPIv5(v4 *API) *APIv5 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't really needed - just use "return &APIv5{v4}" directly in the NewFacadeV5() method

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

}
for i, arg := range args.Args {
err := api.updateOneApplicationSeries(arg)
results.Results[i].Error = err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typically the pattern is not to assume the "doOne" method will return a params.Error and so use common.ServerError(err) here. If err is already a params.Error, it will just work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.


// Find our subordinate applications, to verify their series as well,
for _, subUnitName := range unit.SubordinateNames() {
subordinateApplicationName := strings.Split(subUnitName, "/")[0]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There could be a helper for this - might pay to check

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found one.

charm: &ch,
}

s.applications["foo"] = &app
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need both s.applications and s.backend.applications (and charms)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests were originally setup that way, however it appears that neither s.applications nor s.charms are not needed, removed. Looking at the other mock structs in the ApplicationSuite, endpoints and relation are not needed either, removed those as well.

@@ -1005,6 +1005,108 @@ func (a *Application) SetCharm(cfg SetCharmConfig) (err error) {
return nil
}

// UpdateApplicationSeries updates the series for the Application.
func (a *Application) UpdateApplicationSeries(series string, force bool) (err error) {
acopy := &Application{a.st, a.doc}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why we need a copy here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed


var ops []txn.Op

appOps, err := a.addApplicationUpdateSeriesOps(series, force)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just make this first one ops and subsequent things append to that

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

ops = append(ops, txn.Op{
C: unitsC,
Id: u.doc.DocID,
Assert: txn.DocExists,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this is a separate txn.Op? just include with the assert above

buildTxn := func(attempt int) ([]txn.Op, error) {
a := acopy
if attempt > 0 {
// If we've tried once already and failed, re-evaluate the criteria.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On attempt > 0, we need to check that someone hasn't already updated the series to what we want; in that case we return ErrNoOperations. And there needs to be a test for that.
And we need to check for that in business logic outside the buildTxn func

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done?


func (a *Application) addApplicationUpdateSeriesOps(series string, force bool) ([]txn.Op, error) {
var ops []txn.Op
err := a.VerifySupportedSeries(series, force)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is done outside the ops funcs. And then the ops func build up asserts to match.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

app, subApp := s.setupMultiSeriesUnitWithSubordinate(c)
err := app.UpdateApplicationSeries("trusty", false)
c.Assert(err, jc.ErrorIsNil)
app.Refresh()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to check error, here and elsewhere

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

defer state.SetTestHooks(c, s.State,
jujutxn.TestHook{
Before: func() {
ops := []txn.Op{{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We typically just manipulate state directly via state.State api calls. Using the txn runner works but should be a last resort. eg could just use app.SetCharm(blah)

c.Assert(subApp.Series(), gc.Equals, "xenial")
}

func (s *ApplicationSuite) TestUpdateApplicationSeriesURLChange(c *gc.C) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't quite look right.
There should be 2 variations.

  1. the charm changes to one where the series is not supported and so an incompatible series error occurs
  2. the charm changes but the series is still supported so all good

I'm not sure why the after hooks are needed. They are typically used to verify that a particular txn run attempt has done to the db what is expected, ie read only checks. We don't need that here because we check via api calls that things are as expected.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After hooks removed

c.Assert(app.Series(), gc.Equals, "trusty")
}

func (s *ApplicationSuite) TestUpdateApplicationSeriesUnitCountChange(c *gc.C) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment applies here etc - as per other test

@hmlanigan hmlanigan force-pushed the update-series branch 3 times, most recently from 5d5898d to b9b89e9 Compare August 14, 2017 23:00
@@ -339,6 +354,71 @@ func (api *API) Update(args params.ApplicationUpdate) error {
return nil
}

// UpdateApplicationSeries updates the application series. Series for
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't use double spaces after a full stop. There's at least a couple of places.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

Copy link
Member

@wallyworld wallyworld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a few things to fix before landing but otherwise everything looks ok.

}
app, err := api.backend.Application(arg.ApplicationName)
if err != nil {
return err
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In most cases, we use errors.Trace(err) for standard err returns

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
}
if arg.Series == app.Series() {
// HML do something and add test
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd just return nil here? I think that will suffice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

Series: "zesty",
}},
}
results, err := s.api.UpdateApplicationSeries(args)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that below we verify that UpdateApplicationSeries with the passed in series is called so that's ok. Ideally the test would use different application names rather than "foo" for both args though.

results, err := s.api.UpdateApplicationSeries(args)
c.Assert(err, jc.ErrorIsNil)
c.Assert(len(results.Results), gc.Equals, 1)
c.Assert(results.Results[0], jc.DeepEquals, params.ErrorResult{})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant the value in params.ErrorResult.
It's nil but we expect a fail?

}
c.series = args[1]
case 1:
if names.IsValidMachine(args[0]) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if names.IsValidMachine(args[0]) || names.IsValidApplication(args[0]) {
}

?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that if IsValidMachine is true with only 1 arg, then the series is missing. Otherwise it's unknown if the series or application name is missing because IsValidApplication doesn't seem to know the difference between an application and a series.

Assert: isAliveDoc,
Update: bson.D{{"$set", bson.D{{"series", series}}}},
}}
ops = append(ops, txn.Op{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for all these appends, just to it as a slice with many elements

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

combined into 1 txn and removed the function.

ops = append(ops, txn.Op{
C: applicationsC,
Id: a.doc.DocID,
Assert: bson.D{{"unitcount", a.doc.UnitCount}},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with this asset - add it to the first txn.Op.
So in fact we only need the one txn.Op in the return result. That op will have 3 assert values appended together

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

ch := state.AddTestingCharmMultiSeries(c, s.State, "multi-series")
app := state.AddTestingServiceForSeries(c, s.State, "precise", "multi-series", ch)
err := app.UpdateApplicationSeries("precise", false)
c.Assert(err, gc.ErrorMatches, "no transaction operations are available")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "no transaction operations are available" error should not get exposed - the txn runner sees it and returns nil.
If this is required here then there's something wrong elsewhere.

Copy link
Member Author

@hmlanigan hmlanigan Aug 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test was to see an error when the series on the app matches the requested change before even trying to build the txn. However now the underlying code has been changed per other comments. The test now doesn't expect and error.

err := app.SetCharm(cfg)
c.Assert(err, jc.ErrorIsNil)
},
After: nil,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just drop the After: nil

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

assertApplicationSeriesUpdate(c, subApp, "trusty")
}

func (s *ApplicationSuite) TestUpdateApplicationSeriesSecondSubordinate(c *gc.C) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we still need a test where an added subordinate is not compatible

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added.

@hmlanigan hmlanigan force-pushed the update-series branch 3 times, most recently from 0f28886 to 5c0b46c Compare August 15, 2017 23:24
@hmlanigan hmlanigan changed the base branch from 2.2 to develop August 16, 2017 14:30
@hmlanigan
Copy link
Member Author

!!build!!

1 similar comment
@hmlanigan
Copy link
Member Author

!!build!!

@hmlanigan
Copy link
Member Author

!!build!!

1 similar comment
@hmlanigan
Copy link
Member Author

!!build!!

@hmlanigan
Copy link
Member Author

$$merge$$

@jujubot
Copy link
Collaborator

jujubot commented Aug 16, 2017

Status: merge request accepted. Url: http://juju-ci.vapour.ws:8080/job/github-merge-juju

@jujubot jujubot merged commit 725c0fc into juju:develop Aug 16, 2017
@hmlanigan hmlanigan deleted the update-series branch September 6, 2017 00:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants