Skip to content

Commit

Permalink
Merge branch '2.7' into merge-2.7-20200408
Browse files Browse the repository at this point in the history
  • Loading branch information
wallyworld committed Apr 8, 2020
2 parents 7acfac6 + 57b8a04 commit e0e6fa3
Show file tree
Hide file tree
Showing 19 changed files with 332 additions and 15 deletions.
16 changes: 15 additions & 1 deletion acceptancetests/assess_model_migration.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ def ensure_api_login_redirects(source_client, dest_client):
migrated_model_client,
expected_controller=dest_client.env.controller.name)

# Release the model machines back to the cloud since there are more tests
# after this part.
migrated_model_client.destroy_model()


def assert_data_file_lists_correct_controller_for_model(
client, expected_controller):
Expand Down Expand Up @@ -321,6 +325,8 @@ def ensure_superuser_can_migrate_other_user_models(
migration_client.wait_for_started()
wait_until_model_disappears(source_client, user_qualified_model_name)

migration_client.destroy_model()


def migrate_model_to_controller(
source_client, dest_client, include_user_name=False):
Expand Down Expand Up @@ -433,6 +439,9 @@ def ensure_model_logs_are_migrated(source_client, dest_client, timeout=600):

assert_logs_appear_in_client_model(
migrated_model, before_migration_logs, timeout)
# Destroy the model, on the more resource-constrained clouds we could do
# with the machines going away.
migrated_model.destroy_model()


def assert_logs_appear_in_client_model(client, expected_logs, timeout):
Expand Down Expand Up @@ -479,6 +488,8 @@ def ensure_migration_rolls_back_on_failure(source_client, dest_client):
test_model.remove_application(application)
log.info('SUCCESS: migration rolled back.')

test_model.destroy_model()


@contextmanager
def disable_apiserver(admin_client, machine_number='0'):
Expand Down Expand Up @@ -520,6 +531,8 @@ def ensure_migrating_with_insufficient_user_permissions_fails(
user_source_client, 'user-fail')
log.info('Attempting migration process')
expect_migration_attempt_to_fail(user_new_model, user_dest_client)
# Migration fails, so destroy the source model to clean up.
user_new_model.destroy_model()


def ensure_migrating_with_superuser_user_permissions_succeeds(
Expand All @@ -533,9 +546,10 @@ def ensure_migrating_with_superuser_user_permissions_succeeds(
user_new_model = deploy_dummy_source_to_new_model(
user_source_client, 'super-permissions')
log.info('Attempting migration process')
migrate_model_to_controller(
migrated_client = migrate_model_to_controller(
user_new_model, user_dest_client, include_user_name=True)
log.info('SUCCESS: superuser migrated other user model.')
migrated_client.destroy_model()


def create_user_on_controllers(source_client, dest_client,
Expand Down
12 changes: 10 additions & 2 deletions state/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,21 @@ func (st *State) apiHostPortsForCAAS(public bool) (addresses []network.SpaceHost
logger.Debugf("getting api hostports for CAAS: public %t, addresses %v", public, addresses)
}()

controllerConfig, err := st.ControllerConfig()
// We are fetching info about the controller cloud service,
// so need the controller state.
ctrlSt, err := st.newStateNoWorkers(st.ControllerModelUUID())
if err != nil {
return nil, errors.Trace(err)
}
defer func() { _ = ctrlSt.Close() }()

controllerConfig, err := ctrlSt.ControllerConfig()
if err != nil {
return nil, errors.Trace(err)
}

apiPort := controllerConfig.APIPort()
svc, err := st.CloudService(controllerConfig.ControllerUUID())
svc, err := ctrlSt.CloudService(controllerConfig.ControllerUUID())
if err != nil {
return nil, errors.Trace(err)
}
Expand Down
27 changes: 21 additions & 6 deletions state/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -399,11 +399,16 @@ func (s *CAASAddressesSuite) TestAPIHostPortsCloudLocalOnly(c *gc.C) {
NetPort: 17777,
}}}

addrs, err := s.State.APIHostPortsForAgents()
// Make a new non-system state to ensure everything
//works from any model.
st := s.Factory.MakeCAASModel(c, nil)
defer func() { st.Close() }()

addrs, err := st.APIHostPortsForAgents()
c.Assert(err, jc.ErrorIsNil)
c.Assert(addrs, gc.DeepEquals, exp)

addrs, err = s.State.APIHostPortsForClients()
addrs, err = st.APIHostPortsForClients()
c.Assert(err, jc.ErrorIsNil)
c.Assert(addrs, gc.DeepEquals, exp)
}
Expand All @@ -427,11 +432,16 @@ func (s *CAASAddressesSuite) TestAPIHostPortsPublicOnly(c *gc.C) {
NetPort: 17777,
}}}

addrs, err := s.State.APIHostPortsForAgents()
// Make a new non-system state to ensure everything
//works from any model.
st := s.Factory.MakeCAASModel(c, nil)
defer func() { st.Close() }()

addrs, err := st.APIHostPortsForAgents()
c.Assert(err, jc.ErrorIsNil)
c.Assert(addrs, gc.DeepEquals, exp)

addrs, err = s.State.APIHostPortsForClients()
addrs, err = st.APIHostPortsForClients()
c.Assert(err, jc.ErrorIsNil)
c.Assert(addrs, gc.DeepEquals, exp)
}
Expand Down Expand Up @@ -470,7 +480,12 @@ func (s *CAASAddressesSuite) TestAPIHostPortsMultiple(c *gc.C) {
})
c.Assert(err, jc.ErrorIsNil)

addrs, err := s.State.APIHostPortsForAgents()
// Make a new non-system state to ensure everything
//works from any model.
st := s.Factory.MakeCAASModel(c, nil)
defer func() { st.Close() }()

addrs, err := st.APIHostPortsForAgents()
c.Assert(err, jc.ErrorIsNil)

// Local-cloud addresses must come first.
Expand Down Expand Up @@ -500,7 +515,7 @@ func (s *CAASAddressesSuite) TestAPIHostPortsMultiple(c *gc.C) {
c.Assert(addrs[0][2:], jc.SameContents, exp)

// Only the public ones should be returned.
addrs, err = s.State.APIHostPortsForClients()
addrs, err = st.APIHostPortsForClients()
c.Assert(err, jc.ErrorIsNil)
c.Assert(addrs, gc.DeepEquals, []network.SpaceHostPorts{exp})
}
8 changes: 4 additions & 4 deletions state/migration_export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,12 @@ func (s *MigrationBaseSuite) primeStatusHistory(c *gc.C, entity statusSetter, st
}, 0, "")
}

func (s *MigrationBaseSuite) makeApplicationWithUnits(c *gc.C, applicationname string, count int) {
func (s *MigrationBaseSuite) makeApplicationWithUnits(c *gc.C, applicationName string, count int) {
units := make([]*state.Unit, count)
application := s.Factory.MakeApplication(c, &factory.ApplicationParams{
Name: applicationname,
Name: applicationName,
Charm: s.Factory.MakeCharm(c, &factory.CharmParams{
Name: applicationname,
Name: applicationName,
}),
})
for i := 0; i < count; i++ {
Expand All @@ -111,7 +111,7 @@ func (s *MigrationBaseSuite) makeUnitApplicationLeader(c *gc.C, unitName, applic
loggo.GetLogger("migration_export_test"),
)
target.Claimed(
lease.Key{"application-leadership", s.State.ModelUUID(), applicationName},
lease.Key{Namespace: "application-leadership", ModelUUID: s.State.ModelUUID(), Lease: applicationName},
unitName,
)
}
Expand Down
5 changes: 5 additions & 0 deletions state/migration_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,11 @@ func (i *importer) parseBindings(bindingsMap map[string]string) (*Bindings, erro

// 2.6 controllers only populate the default space key if set to the
// non-default space whereas 2.7 controllers always set it.
// The application implementation in the description package has
// `omitempty` for bindings, so we need to create it if nil.
if bindingsMap == nil {
bindingsMap = make(map[string]string, 1)
}
if _, exists := bindingsMap[defaultEndpointName]; !exists {
bindingsMap[defaultEndpointName] = network.AlphaSpaceName
}
Expand Down
20 changes: 19 additions & 1 deletion state/migration_import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1305,7 +1305,7 @@ func (s *MigrationImportSuite) TestRelationsMissingStatusNoUnits(c *gc.C) {
func (s *MigrationImportSuite) TestEndpointBindings(c *gc.C) {
// Endpoint bindings need both valid charms, applications, and spaces.
space := s.Factory.MakeSpace(c, &factory.SpaceParams{
Name: "one", ProviderID: network.Id("provider"), IsPublic: true})
Name: "one", ProviderID: "provider", IsPublic: true})
state.AddTestingApplicationWithBindings(
c, s.State, "wordpress", state.AddTestingCharm(c, s.State, "wordpress"),
map[string]string{"db": space.Id()})
Expand All @@ -1323,6 +1323,24 @@ func (s *MigrationImportSuite) TestEndpointBindings(c *gc.C) {
c.Assert(bindings.Map()[""], gc.Equals, network.AlphaSpaceId)
}

func (s *MigrationImportSuite) TestNilEndpointBindings(c *gc.C) {
app := state.AddTestingApplicationWithEmptyBindings(
c, s.State, "dummy", state.AddTestingCharm(c, s.State, "dummy"))

bindings, err := app.EndpointBindings()
c.Assert(err, jc.ErrorIsNil)
c.Assert(bindings.Map(), gc.HasLen, 0)

_, newSt := s.importModel(c, s.State)

newApp, err := newSt.Application("dummy")
c.Assert(err, jc.ErrorIsNil)

newBindings, err := newApp.EndpointBindings()
c.Assert(err, jc.ErrorIsNil)
c.Assert(newBindings.Map()[""], gc.Equals, network.AlphaSpaceId)
}

func (s *MigrationImportSuite) TestUnitsOpenPorts(c *gc.C) {
unit := s.Factory.MakeUnit(c, nil)
s.AssertOpenUnitPorts(c, unit, "", "tcp", 1234, 2345)
Expand Down
16 changes: 16 additions & 0 deletions state/package_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ func TestPackage(t *testing.T) {
coretesting.MgoTestPackage(t)
}

// SetModelTypeToCAAS can be called after SetUpTest for state suites.
// It crudely just sets the model type to CAAS so that certain functionality
// relying on the model type can be tested.
func SetModelTypeToCAAS(c *gc.C, st *State, m *Model) {
ops := []txn.Op{{
C: modelsC,
Expand All @@ -44,3 +47,16 @@ func SetModelTypeToCAAS(c *gc.C, st *State, m *Model) {
c.Assert(st.db().RunTransaction(ops), jc.ErrorIsNil)
c.Assert(m.refresh(m.UUID()), jc.ErrorIsNil)
}

// AddTestingApplicationWithEmptyBindings mimics an application
// from an old version of Juju, with no bindings entry.
func AddTestingApplicationWithEmptyBindings(c *gc.C, st *State, name string, ch *Charm) *Application {
app := addTestingApplication(c, addTestingApplicationParams{
st: st,
name: name,
ch: ch,
})

c.Assert(st.db().RunTransaction([]txn.Op{removeEndpointBindingsOp(app.globalKey())}), jc.ErrorIsNil)
return app
}
4 changes: 4 additions & 0 deletions testcharms/charm-repo/kubernetes/ubuntu/hooks/install
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
date=`date`
status-set waiting "Hello from install, it is $date."
juju-log -l INFO "Hello from install."
3 changes: 3 additions & 0 deletions testcharms/charm-repo/kubernetes/ubuntu/hooks/start
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash
application-version-set $(grep DISTRIB_RELEASE /etc/lsb-release | cut -d= -sf2)
juju-log -l INFO "Hello from start."
4 changes: 4 additions & 0 deletions testcharms/charm-repo/kubernetes/ubuntu/hooks/update-status
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
date=`date`
status-set active "Hello from update-status, it is $date."
juju-log -l INFO "Hello from update-status."
12 changes: 12 additions & 0 deletions testcharms/charm-repo/kubernetes/ubuntu/metadata.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: ubuntu-k8s
summary: "ubuntu operating system"
description: "A popular operating system"
provides:
ubuntu:
interface: ubuntu
resources:
ubuntu_image:
type: "oci-image"
description: "Image used for ubuntu pod."
series:
- kubernetes
1 change: 1 addition & 0 deletions upgrades/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ var upgradeOperations = func() []Operation {
upgradeToVersion{version.MustParse("2.6.3"), stepsFor263()},
upgradeToVersion{version.MustParse("2.7.0"), stepsFor27()},
upgradeToVersion{version.MustParse("2.7.2"), stepsFor272()},
upgradeToVersion{version.MustParse("2.7.6"), stepsFor276()},
upgradeToVersion{version.MustParse("2.8.0"), stepsFor28()},
}
return steps
Expand Down
106 changes: 106 additions & 0 deletions upgrades/steps_276.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2020 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package upgrades

import (
"path/filepath"

"github.com/juju/errors"
"github.com/juju/utils"
"gopkg.in/juju/names.v3"

"github.com/juju/juju/worker/uniter/operation"
)

// stateStepsFor276 returns upgrade steps for Juju 2.7.6.
func stepsFor276() []Step {
return []Step{
&upgradeStep{
description: "add remote-application key to hooks in uniter state files",
targets: []Target{HostMachine},
run: AddRemoteApplicationToRunningHooks(uniterStateGlob),
},
}
}

const uniterStateGlob = `/var/lib/juju/agents/unit-*/state/uniter`

// AddRemoteApplicationToRunningHooks finds any uniter state files on
// the machine with running hooks, and makes sure that they contain a
// remote-application key.
func AddRemoteApplicationToRunningHooks(pattern string) func(Context) error {
return func(_ Context) error {
matches, err := filepath.Glob(pattern)
if err != nil {
return errors.Annotate(err, "finding uniter state files")
}
for _, path := range matches {
// First, check whether the file needs rewriting.
stateFile := operation.NewStateFile(path)
_, err := stateFile.Read()
if err == nil {
// This one's fine, leave it alone.
logger.Debugf("state file valid: %q", path)
continue
}

err = AddRemoteApplicationToHook(path)
if err != nil {
return errors.Annotatef(err, "fixing %q", path)
}
}
return nil
}
}

// AddRemoteApplicationToHook takes a the path to a uniter state file
// that doesn't validate, and sets hook.remote-application to the
// remote application so that it does. (If it doesn't validate for
// some other reason we won't change the file.)
func AddRemoteApplicationToHook(path string) error {
var uniterState map[string]interface{}
err := utils.ReadYaml(path, &uniterState)
if err != nil {
return errors.Trace(err)
}

hookUnconverted, found := uniterState["hook"]
if !found {
logger.Warningf("no hook found in %q, unable to fix", path)
return nil
}

hook, ok := hookUnconverted.(map[interface{}]interface{})
if !ok {
logger.Warningf("fixing %q: expected hook to be a map[interface{}]interface{}, got %T", path, hookUnconverted)
return nil
}

if val, found := hook["remote-application"]; found {
logger.Debugf("remote-application in %q set to %v already", path, val)
return nil
}

unitUnconverted, found := hook["remote-unit"]
if !found {
logger.Warningf("fixing %q: remote-unit not found")
return nil
}

unit, ok := unitUnconverted.(string)
if !ok {
logger.Warningf("fixing %q: expected remote-unit to be string, got %T", path, unitUnconverted)
return nil
}

appName, err := names.UnitApplication(unit)
if err != nil {
return errors.Trace(err)
}

logger.Debugf("setting remote-application to %q in %q", appName, path)
hook["remote-application"] = appName
return errors.Annotatef(utils.WriteYaml(path, uniterState),
"writing updated state to %q", path)
}
Loading

0 comments on commit e0e6fa3

Please sign in to comment.