Skip to content

Commit

Permalink
Merge pull request juju#17068 from SimonRichardson/bootstrap-options
Browse files Browse the repository at this point in the history
juju#17068

During bootstrap we need to access both the controller (global db) and the model (controller model db). To prevent the need for hacks to get that information, this revisits the bootstrap concerns and moves back to bootstrap options. The options take the controller and the model. Thus you can access any part of each model during bootstrap.

<!-- Why this change is needed and what it does. -->

## Checklist

<!-- If an item is not applicable, use `~strikethrough~`. -->

- [x] Code style: imports ordered, good names, simple structure, etc
- [x] Comments saying why design decisions were made
- [x] Go unit tests, with comments saying what you're testing

## QA steps

```sh
$ juju bootstrap lxd test
$ juju add-model default
```

## Links

**Jira card:** JUJU-5645
  • Loading branch information
jujubot authored Mar 21, 2024
2 parents 1ec1a03 + b98e191 commit 0381ce4
Show file tree
Hide file tree
Showing 27 changed files with 278 additions and 250 deletions.
39 changes: 18 additions & 21 deletions agent/agentbootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ import (
type DqliteInitializerFunc func(
ctx stdcontext.Context,
mgr database.BootstrapNodeManager,
modelUUID model.UUID,
logger database.Logger,
concerns ...database.BootstrapConcern,
options ...database.BootstrapOpt,
) error

// Logger describes methods for emitting log output.
Expand Down Expand Up @@ -250,29 +251,24 @@ func (b *AgentBootstrap) Initialize(ctx stdcontext.Context) (_ *state.Controller
stateParams.ControllerInheritedConfig,
stateParams.RegionInheritedConfig[stateParams.ControllerCloudRegion])

databaseBootstrapConcerns := []database.BootstrapConcern{
database.BootstrapControllerConcern(
// The admin user needs to be added before everything else that
// requires being owned by a Juju user.
addAdminUser,
ccbootstrap.InsertInitialControllerConfig(stateParams.ControllerConfig),
cloudbootstrap.InsertCloud(stateParams.ControllerCloud),
credbootstrap.InsertCredential(credential.IdFromTag(cloudCredTag), cloudCred),
cloudbootstrap.SetCloudDefaults(stateParams.ControllerCloud.Name, stateParams.ControllerInheritedConfig),
controllerModelCreateFunc,
),
database.BootstrapModelConcern(controllerModelUUID,
modelbootstrap.CreateReadOnlyModel(controllerModelArgs.AsReadOnly()),
modelconfigbootstrap.SetModelConfig(stateParams.ControllerModelConfig, controllerModelDefaults),
),
databaseBootstrapOptions := []database.BootstrapOpt{
// The admin user needs to be added before everything else that
// requires being owned by a Juju user.
addAdminUser,
ccbootstrap.InsertInitialControllerConfig(stateParams.ControllerConfig),
cloudbootstrap.InsertCloud(stateParams.ControllerCloud),
credbootstrap.InsertCredential(credential.IdFromTag(cloudCredTag), cloudCred),
cloudbootstrap.SetCloudDefaults(stateParams.ControllerCloud.Name, stateParams.ControllerInheritedConfig),
controllerModelCreateFunc,
modelbootstrap.CreateReadOnlyModel(controllerModelArgs.AsReadOnly()),
modelconfigbootstrap.SetModelConfig(stateParams.ControllerModelConfig, controllerModelDefaults),
}
isCAAS := cloud.CloudIsCAAS(stateParams.ControllerCloud)
if !isCAAS {
// TODO(wallyworld) - this is just a placeholder for now
databaseBootstrapConcerns = append(databaseBootstrapConcerns,
database.BootstrapModelConcern(controllerModelUUID,
machinebootstrap.InsertMachine(agent.BootstrapControllerId),
))
databaseBootstrapOptions = append(databaseBootstrapOptions,
machinebootstrap.InsertMachine(agent.BootstrapControllerId),
)
}

// If we're running caas, we need to bind to the loopback address
Expand All @@ -285,8 +281,9 @@ func (b *AgentBootstrap) Initialize(ctx stdcontext.Context) (_ *state.Controller
if err := b.bootstrapDqlite(
ctx,
database.NewNodeManager(b.agentConfig, isLoopbackPreferred, b.logger, coredatabase.NoopSlowQueryLogger{}),
controllerModelUUID,
b.logger,
databaseBootstrapConcerns...,
databaseBootstrapOptions...,
); err != nil {
return nil, errors.Trace(err)
}
Expand Down
16 changes: 11 additions & 5 deletions agent/agentbootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,11 +580,17 @@ func (e *fakeEnviron) Provider() environs.EnvironProvider {
return e.provider
}

func bootstrapDqliteWithDummyCloudType(ctx context.Context, mgr database.BootstrapNodeManager, logger database.Logger, concerns ...database.BootstrapConcern) error {
func bootstrapDqliteWithDummyCloudType(
ctx context.Context,
mgr database.BootstrapNodeManager,
modelUUID model.UUID,
logger database.Logger,
opts ...database.BootstrapOpt,
) error {
// The dummy cloud type needs to be inserted before the other operations.
concerns = append([]database.BootstrapConcern{
database.BootstrapControllerInitConcern(database.EmptyInit, jujujujutesting.InsertDummyCloudType),
}, concerns...)
opts = append([]database.BootstrapOpt{
jujujujutesting.InsertDummyCloudType,
}, opts...)

return database.BootstrapDqlite(ctx, mgr, logger, concerns...)
return database.BootstrapDqlite(ctx, mgr, modelUUID, logger, opts...)
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (s *CAASApplicationSuite) SetUpTest(c *gc.C) {
s.ServiceFactorySuite.SetUpTest(c)

controllerConfig := coretesting.FakeControllerConfig()
err := controllerconfigbootstrap.InsertInitialControllerConfig(controllerConfig)(context.Background(), s.TxnRunner())
err := controllerconfigbootstrap.InsertInitialControllerConfig(controllerConfig)(context.Background(), s.TxnRunner(), s.NoopTxnRunner())
c.Assert(err, jc.ErrorIsNil)

s.clock = testclock.NewClock(time.Now())
Expand Down
2 changes: 2 additions & 0 deletions cmd/jujud-controller/agent/agenttest/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
cmdutil "github.com/juju/juju/cmd/jujud-controller/util"
"github.com/juju/juju/controller"
coredatabase "github.com/juju/juju/core/database"
modeltesting "github.com/juju/juju/core/model/testing"
"github.com/juju/juju/core/network"
"github.com/juju/juju/environs"
"github.com/juju/juju/environs/filestorage"
Expand Down Expand Up @@ -236,6 +237,7 @@ func (s *AgentSuite) PrimeStateAgentVersion(c *gc.C, tag names.Tag, password str
err = database.BootstrapDqlite(
context.Background(),
database.NewNodeManager(conf, true, logger, coredatabase.NoopSlowQueryLogger{}),
modeltesting.GenModelUUID(c),
logger,
)
c.Assert(err, jc.ErrorIsNil)
Expand Down
11 changes: 6 additions & 5 deletions cmd/jujud-controller/agent/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -600,13 +600,14 @@ func nullContext() environs.BootstrapContext {
func bootstrapDqliteWithDummyCloudType(
ctx context.Context,
mgr database.BootstrapNodeManager,
modelUUID model.UUID,
logger database.Logger,
concerns ...database.BootstrapConcern,
opts ...database.BootstrapOpt,
) error {
// The dummy cloud type needs to be inserted before the other operations.
concerns = append([]database.BootstrapConcern{
database.BootstrapControllerInitConcern(database.EmptyInit, jujutesting.InsertDummyCloudType),
}, concerns...)
opts = append([]database.BootstrapOpt{
jujutesting.InsertDummyCloudType,
}, opts...)

return database.BootstrapDqlite(ctx, mgr, logger, concerns...)
return database.BootstrapDqlite(ctx, mgr, modelUUID, logger, opts...)
}
2 changes: 2 additions & 0 deletions cmd/jujud/agent/agenttest/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
agenttools "github.com/juju/juju/agent/tools"
"github.com/juju/juju/controller"
coredatabase "github.com/juju/juju/core/database"
modeltesting "github.com/juju/juju/core/model/testing"
"github.com/juju/juju/core/network"
"github.com/juju/juju/environs"
"github.com/juju/juju/environs/filestorage"
Expand Down Expand Up @@ -170,6 +171,7 @@ func (s *AgentSuite) PrimeStateAgentVersion(c *gc.C, tag names.Tag, password str
err = database.BootstrapDqlite(
context.Background(),
database.NewNodeManager(conf, true, logger, coredatabase.NoopSlowQueryLogger{}),
modeltesting.GenModelUUID(c),
logger,
)
c.Assert(err, jc.ErrorIsNil)
Expand Down
13 changes: 7 additions & 6 deletions domain/cloud/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@ import (
"github.com/juju/juju/core/database"
"github.com/juju/juju/domain/cloud/state"
modelconfigservice "github.com/juju/juju/domain/modelconfig/service"
internaldatabase "github.com/juju/juju/internal/database"
"github.com/juju/juju/internal/uuid"
)

// InsertCloud inserts the initial cloud during bootstrap.
func InsertCloud(cloud cloud.Cloud) func(context.Context, database.TxnRunner) error {
return func(ctx context.Context, db database.TxnRunner) error {
func InsertCloud(cloud cloud.Cloud) internaldatabase.BootstrapOpt {
return func(ctx context.Context, controller, model database.TxnRunner) error {
cloudUUID, err := uuid.NewUUID()
if err != nil {
return errors.Trace(err)
}
return errors.Trace(db.Txn(ctx, func(ctx context.Context, tx *sqlair.TX) error {
return errors.Trace(controller.Txn(ctx, func(ctx context.Context, tx *sqlair.TX) error {
if err := state.CreateCloud(ctx, tx, cloudUUID.String(), cloud); err != nil {
return errors.Annotate(err, "creating bootstrap cloud")
}
Expand All @@ -42,14 +43,14 @@ func InsertCloud(cloud cloud.Cloud) func(context.Context, database.TxnRunner) er
func SetCloudDefaults(
cloudName string,
defaults map[string]any,
) func(context.Context, database.TxnRunner) error {
return func(ctx context.Context, db database.TxnRunner) error {
) internaldatabase.BootstrapOpt {
return func(ctx context.Context, controller, model database.TxnRunner) error {
strDefaults, err := modelconfigservice.CoerceConfigForStorage(defaults)
if err != nil {
return fmt.Errorf("coercing cloud %q default values for storage: %w", cloudName, err)
}

err = db.StdTxn(ctx, func(ctx context.Context, tx *sql.Tx) error {
err = controller.StdTxn(ctx, func(ctx context.Context, tx *sql.Tx) error {
return state.SetCloudDefaults(ctx, tx, cloudName, strDefaults)
})

Expand Down
14 changes: 7 additions & 7 deletions domain/cloud/bootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var _ = gc.Suite(&bootstrapSuite{})

func (s *bootstrapSuite) TestInsertCloud(c *gc.C) {
cld := cloud.Cloud{Name: "cirrus", Type: "ec2", AuthTypes: cloud.AuthTypes{cloud.UserPassAuthType}}
err := InsertCloud(cld)(context.Background(), s.TxnRunner())
err := InsertCloud(cld)(context.Background(), s.TxnRunner(), s.NoopTxnRunner())
c.Assert(err, jc.ErrorIsNil)

var name string
Expand All @@ -39,7 +39,7 @@ func (s *bootstrapSuite) TestSetCloudDefaultsNoExist(c *gc.C) {
"HTTP_PROXY": "[2001:0DB8::1]:80",
})

err := set(context.Background(), s.TxnRunner())
err := set(context.Background(), s.TxnRunner(), s.NoopTxnRunner())
c.Check(err, jc.ErrorIs, clouderrors.NotFound)

var count int
Expand All @@ -56,14 +56,14 @@ func (s *bootstrapSuite) TestSetCloudDefaults(c *gc.C) {
Type: "ec2",
AuthTypes: cloud.AuthTypes{cloud.UserPassAuthType},
}
err := InsertCloud(cld)(context.Background(), s.TxnRunner())
err := InsertCloud(cld)(context.Background(), s.TxnRunner(), s.NoopTxnRunner())
c.Check(err, jc.ErrorIsNil)

set := SetCloudDefaults("cirrus", map[string]any{
"HTTP_PROXY": "[2001:0DB8::1]:80",
})

err = set(context.Background(), s.TxnRunner())
err = set(context.Background(), s.TxnRunner(), s.NoopTxnRunner())
c.Check(err, jc.ErrorIsNil)

st := state.NewState(s.TxnRunnerFactory())
Expand All @@ -82,14 +82,14 @@ func (s *bootstrapSuite) TestSetCloudDefaultsOverides(c *gc.C) {
Type: "ec2",
AuthTypes: cloud.AuthTypes{cloud.UserPassAuthType},
}
err := InsertCloud(cld)(context.Background(), s.TxnRunner())
err := InsertCloud(cld)(context.Background(), s.TxnRunner(), s.NoopTxnRunner())
c.Check(err, jc.ErrorIsNil)

set := SetCloudDefaults("cirrus", map[string]any{
"HTTP_PROXY": "[2001:0DB8::1]:80",
})

err = set(context.Background(), s.TxnRunner())
err = set(context.Background(), s.TxnRunner(), s.NoopTxnRunner())
c.Check(err, jc.ErrorIsNil)

st := state.NewState(s.TxnRunnerFactory())
Expand All @@ -105,7 +105,7 @@ func (s *bootstrapSuite) TestSetCloudDefaultsOverides(c *gc.C) {
"foo": "bar",
})

err = set(context.Background(), s.TxnRunner())
err = set(context.Background(), s.TxnRunner(), s.NoopTxnRunner())
c.Check(err, jc.ErrorIsNil)

st = state.NewState(s.TxnRunnerFactory())
Expand Down
16 changes: 8 additions & 8 deletions domain/controllerconfig/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ import (

"github.com/juju/errors"

"github.com/juju/juju/controller"
jujucontroller "github.com/juju/juju/controller"
"github.com/juju/juju/core/database"
jujudatabase "github.com/juju/juju/internal/database"
internaldatabase "github.com/juju/juju/internal/database"
)

// InsertInitialControllerConfig inserts the initial controller configuration
// into the database.
func InsertInitialControllerConfig(cfg controller.Config) func(context.Context, database.TxnRunner) error {
return func(ctx context.Context, db database.TxnRunner) error {
values, err := controller.EncodeToString(cfg)
func InsertInitialControllerConfig(cfg jujucontroller.Config) internaldatabase.BootstrapOpt {
return func(ctx context.Context, controller, model database.TxnRunner) error {
values, err := jujucontroller.EncodeToString(cfg)
if err != nil {
return errors.Trace(err)
}

fields, _, err := controller.ConfigSchema.ValidationSchema()
fields, _, err := jujucontroller.ConfigSchema.ValidationSchema()
if err != nil {
return errors.Trace(err)
}
Expand All @@ -39,10 +39,10 @@ func InsertInitialControllerConfig(cfg controller.Config) func(context.Context,

query := "INSERT INTO controller_config (key, value) VALUES (?, ?)"

return errors.Trace(db.StdTxn(ctx, func(ctx context.Context, tx *sql.Tx) error {
return errors.Trace(controller.StdTxn(ctx, func(ctx context.Context, tx *sql.Tx) error {
for k, v := range values {
if _, err := tx.ExecContext(ctx, query, k, v); err != nil {
if jujudatabase.IsErrConstraintPrimaryKey(errors.Cause(err)) {
if internaldatabase.IsErrConstraintPrimaryKey(errors.Cause(err)) {
return errors.AlreadyExistsf("controller configuration key %q", k)
}
return errors.Annotatef(err, "inserting controller configuration %q, %v", k, v)
Expand Down
2 changes: 1 addition & 1 deletion domain/controllerconfig/bootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var _ = gc.Suite(&bootstrapSuite{})

func (s *bootstrapSuite) TestInsertInitialControllerConfig(c *gc.C) {
cfg := controller.Config{controller.CACertKey: testing.CACert}
err := InsertInitialControllerConfig(cfg)(context.Background(), s.TxnRunner())
err := InsertInitialControllerConfig(cfg)(context.Background(), s.TxnRunner(), s.NoopTxnRunner())
c.Assert(err, jc.ErrorIsNil)

var cert string
Expand Down
2 changes: 1 addition & 1 deletion domain/controllerconfig/controllerconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (s *controllerconfigSuite) TestControllerConfigRoundTrips(c *gc.C) {
)
c.Assert(err, jc.ErrorIsNil)

err = bootstrap.InsertInitialControllerConfig(cfgIn)(ctx.Background(), s.TxnRunner())
err = bootstrap.InsertInitialControllerConfig(cfgIn)(ctx.Background(), s.TxnRunner(), s.NoopTxnRunner())
c.Assert(err, jc.ErrorIsNil)

cfgOut, err := srv.ControllerConfig(ctx.Background())
Expand Down
7 changes: 4 additions & 3 deletions domain/credential/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ import (
"github.com/juju/juju/core/database"
"github.com/juju/juju/domain/credential"
"github.com/juju/juju/domain/credential/state"
internaldatabase "github.com/juju/juju/internal/database"
"github.com/juju/juju/internal/uuid"
)

// InsertCredential inserts a cloud credential into dqlite.
func InsertCredential(id corecredential.ID, cred cloud.Credential) func(context.Context, database.TxnRunner) error {
return func(ctx context.Context, db database.TxnRunner) error {
func InsertCredential(id corecredential.ID, cred cloud.Credential) internaldatabase.BootstrapOpt {
return func(ctx context.Context, controller, model database.TxnRunner) error {
if id.IsZero() {
return nil
}
Expand All @@ -28,7 +29,7 @@ func InsertCredential(id corecredential.ID, cred cloud.Credential) func(context.
if err != nil {
return errors.Trace(err)
}
return errors.Trace(db.Txn(ctx, func(ctx context.Context, tx *sqlair.TX) error {
return errors.Trace(controller.Txn(ctx, func(ctx context.Context, tx *sqlair.TX) error {
if err := state.CreateCredential(ctx, tx, credentialUUID.String(), id, credential.CloudCredentialInfo{
AuthType: string(cred.AuthType()),
Attributes: cred.Attributes(),
Expand Down
4 changes: 2 additions & 2 deletions domain/credential/bootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var _ = gc.Suite(&bootstrapSuite{})
func (s *bootstrapSuite) TestInsertInitialControllerConfig(c *gc.C) {
ctx := context.Background()
cld := cloud.Cloud{Name: "cirrus", Type: "ec2", AuthTypes: cloud.AuthTypes{cloud.UserPassAuthType}}
err := cloudbootstrap.InsertCloud(cld)(ctx, s.TxnRunner())
err := cloudbootstrap.InsertCloud(cld)(ctx, s.TxnRunner(), s.NoopTxnRunner())
c.Assert(err, jc.ErrorIsNil)

userUUID, err := user.NewUUID()
Expand All @@ -50,7 +50,7 @@ func (s *bootstrapSuite) TestInsertInitialControllerConfig(c *gc.C) {
Name: "foo",
}

err = InsertCredential(id, cred)(ctx, s.TxnRunner())
err = InsertCredential(id, cred)(ctx, s.TxnRunner(), s.NoopTxnRunner())
c.Assert(err, jc.ErrorIsNil)

var owner, cloudName string
Expand Down
7 changes: 4 additions & 3 deletions domain/machine/bootstrap/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import (

"github.com/juju/juju/core/database"
"github.com/juju/juju/domain/life"
internaldatabase "github.com/juju/juju/internal/database"
"github.com/juju/juju/internal/uuid"
)

// InsertMachine inserts a machine during bootstrap.
// TODO - this just creates a minimal row for now.
func InsertMachine(machineId string) func(context.Context, database.TxnRunner) error {
return func(ctx context.Context, db database.TxnRunner) error {
func InsertMachine(machineId string) internaldatabase.BootstrapOpt {
return func(ctx context.Context, controller, model database.TxnRunner) error {

createMachine := `
INSERT INTO machine (uuid, net_node_uuid, machine_id, life_id)
Expand All @@ -43,7 +44,7 @@ VALUES ($M.machine_uuid, $M.net_node_uuid, $M.machine_id, $M.life_id)
return errors.Trace(err)
}

return errors.Trace(db.Txn(ctx, func(ctx context.Context, tx *sqlair.TX) error {
return errors.Trace(model.Txn(ctx, func(ctx context.Context, tx *sqlair.TX) error {
createParams := sqlair.M{
"machine_uuid": machineUUID.String(),
"net_node_uuid": nodeUUID.String(),
Expand Down
2 changes: 1 addition & 1 deletion domain/machine/bootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type bootstrapSuite struct {
var _ = gc.Suite(&bootstrapSuite{})

func (s *bootstrapSuite) TestInsertBootstrapMachine(c *gc.C) {
err := InsertMachine("666")(context.Background(), s.TxnRunner())
err := InsertMachine("666")(context.Background(), s.NoopTxnRunner(), s.TxnRunner())
c.Assert(err, jc.ErrorIsNil)

var machineId string
Expand Down
Loading

0 comments on commit 0381ce4

Please sign in to comment.