From 4b41228b83ef8ae836ffcea95239b7c9f783c4f0 Mon Sep 17 00:00:00 2001 From: Simon Richardson Date: Thu, 28 Mar 2024 10:23:20 +0000 Subject: [PATCH] Model life Adds the life concept to the model metadata. This will be used as the basis to trigger the model watcher. Any changes to the model metadata allows us to trigger the model watcher. --- core/model/model.go | 7 +++++++ core/model/model_test.go | 6 +++--- domain/credential/state/state_test.go | 4 ++-- domain/model/state/state.go | 10 +++++++--- domain/model/state/state_test.go | 2 ++ domain/schema/controller.go | 16 +++++++++++++--- domain/schema/life.go | 20 ++++++++++++++++++++ domain/schema/model.go | 14 -------------- domain/schema/schema_test.go | 7 +++++++ 9 files changed, 61 insertions(+), 25 deletions(-) create mode 100644 domain/schema/life.go diff --git a/core/model/model.go b/core/model/model.go index cbe1b0810ad..3cbd1d922a7 100644 --- a/core/model/model.go +++ b/core/model/model.go @@ -10,6 +10,7 @@ import ( "github.com/juju/version/v2" "github.com/juju/juju/core/credential" + "github.com/juju/juju/core/life" "github.com/juju/juju/core/user" "github.com/juju/juju/internal/uuid" ) @@ -47,6 +48,12 @@ type Model struct { // Name returns the human friendly name of the model. Name string + // Life is the current state of the model. + // Options are alive, dying, dead. Every model starts as alive, only + // during the destruction of the model it transitions to dying and then + // dead. + Life life.Value + // UUID is the universally unique identifier of the model. UUID UUID diff --git a/core/model/model_test.go b/core/model/model_test.go index 60fc92371fb..63fc4f9886c 100644 --- a/core/model/model_test.go +++ b/core/model/model_test.go @@ -23,9 +23,9 @@ func (*ModelSuite) TestValidateBranchName(c *gc.C) { branchName string valid bool }{ - {"", false}, - {GenerationMaster, false}, - {"something else", true}, + {branchName: "", valid: false}, + {branchName: GenerationMaster, valid: false}, + {branchName: "something else", valid: true}, } { err := ValidateBranchName(t.branchName) if t.valid { diff --git a/domain/credential/state/state_test.go b/domain/credential/state/state_test.go index ec368e3ffc8..abd53c8115a 100644 --- a/domain/credential/state/state_test.go +++ b/domain/credential/state/state_test.go @@ -573,8 +573,8 @@ func (s *credentialSuite) TestModelsUsingCloudCredential(c *gc.C) { return err } result, err := tx.ExecContext(ctx, fmt.Sprintf(` - INSERT INTO model_metadata (model_uuid, name, owner_uuid, model_type_id, cloud_uuid, cloud_credential_uuid) - SELECT %q, %q, %q, 0, + INSERT INTO model_metadata (model_uuid, name, owner_uuid, life_id, model_type_id, cloud_uuid, cloud_credential_uuid) + SELECT %q, %q, %q, 0, 0, (SELECT uuid FROM cloud WHERE cloud.name="stratus"), (SELECT uuid FROM cloud_credential cc WHERE cc.name="foobar")`, modelUUID, name, s.userUUID), diff --git a/domain/model/state/state.go b/domain/model/state/state.go index 5f136d66640..db3d66962b6 100644 --- a/domain/model/state/state.go +++ b/domain/model/state/state.go @@ -18,6 +18,7 @@ import ( "github.com/juju/juju/domain" usererrors "github.com/juju/juju/domain/access/errors" clouderrors "github.com/juju/juju/domain/cloud/errors" + "github.com/juju/juju/domain/life" "github.com/juju/juju/domain/model" modelerrors "github.com/juju/juju/domain/model/errors" jujudb "github.com/juju/juju/internal/database" @@ -152,7 +153,7 @@ func Get( uuid coremodel.UUID, ) (coremodel.Model, error) { modelStmt := ` -SELECT md.name, cl.name, cr.name, mt.type, u.uuid, cc.cloud_uuid, cc.name, o.name, ccn.name +SELECT md.name, cl.name, cr.name, mt.type, u.uuid, cc.cloud_uuid, cc.name, o.name, ccn.name, l.value FROM model_metadata AS md LEFT JOIN model_list ml ON ml.uuid = md.model_uuid LEFT JOIN cloud cl ON cl.uuid = md.cloud_uuid @@ -162,6 +163,7 @@ LEFT JOIN model_type mt ON mt.id = md.model_type_id LEFT JOIN user u ON u.uuid = md.owner_uuid LEFT JOIN user o ON o.uuid = cc.owner_uuid LEFT JOIN cloud ccn ON ccn.uuid = cc.cloud_uuid +LEFT JOIN life l ON l.id = md.life_id WHERE md.model_uuid = ? ` @@ -186,6 +188,7 @@ WHERE md.model_uuid = ? &credKey.Name, &credKey.Owner, &credKey.Cloud, + &model.Life, ) if errors.Is(err, sql.ErrNoRows) { @@ -370,15 +373,16 @@ AND removed = false INSERT INTO model_metadata (model_uuid, cloud_uuid, model_type_id, + life_id, name, owner_uuid) -SELECT ?, ?, model_type.id, ?, ? +SELECT ?, ?, model_type.id, ?, ?, ? FROM model_type WHERE model_type.type = ? ` res, err := tx.ExecContext(ctx, stmt, - uuid, cloudUUID, input.Name, input.Owner, modelType, + uuid, cloudUUID, life.Alive, input.Name, input.Owner, modelType, ) if jujudb.IsErrConstraintPrimaryKey(err) { return fmt.Errorf("%w for uuid %q", modelerrors.AlreadyExists, uuid) diff --git a/domain/model/state/state_test.go b/domain/model/state/state_test.go index d972e03599a..af3c10c6d04 100644 --- a/domain/model/state/state_test.go +++ b/domain/model/state/state_test.go @@ -14,6 +14,7 @@ import ( "github.com/juju/juju/cloud" corecredential "github.com/juju/juju/core/credential" + "github.com/juju/juju/core/life" coremodel "github.com/juju/juju/core/model" modeltesting "github.com/juju/juju/core/model/testing" "github.com/juju/juju/core/permission" @@ -132,6 +133,7 @@ func (m *stateSuite) TestGetModel(c *gc.C) { Name: "my-test-model", Owner: m.userUUID, ModelType: coremodel.IAAS, + Life: life.Alive, }) } diff --git a/domain/schema/controller.go b/domain/schema/controller.go index fd0b8839399..bbcc7097f6f 100644 --- a/domain/schema/controller.go +++ b/domain/schema/controller.go @@ -22,11 +22,13 @@ const ( tableUpgradeInfoControllerNode tableObjectStoreMetadata tableSecretBackendRotation + tableModelMetadata ) // ControllerDDL is used to create the controller database schema at bootstrap. func ControllerDDL() *schema.Schema { patches := []func() schema.Patch{ + lifeSchema, leaseSchema, changeLogSchema, changeLogControllerNamespacesSchema, @@ -59,6 +61,7 @@ func ControllerDDL() *schema.Schema { changeLogTriggersForTable("upgrade_info_controller_node", "upgrade_info_uuid", tableUpgradeInfoControllerNode), changeLogTriggersForTable("object_store_metadata_path", "path", tableObjectStoreMetadata), changeLogTriggersForTableOnColumn("secret_backend_rotation", "backend_uuid", "next_rotation_time", tableSecretBackendRotation), + changeLogTriggersForTable("model_metadata", "model_uuid", tableModelMetadata), // We need to ensure that the internal and kubernetes backends are immutable after // they are created by the controller during bootstrap time. @@ -142,7 +145,8 @@ INSERT INTO change_log_namespace VALUES (8, 'autocert_cache', 'autocert cache changes based on the UUID'), (9, 'upgrade_info_controller_node', 'upgrade info controller node changes based on the upgrade info UUID'), (10, 'object_store_metadata_path', 'object store metadata path changes based on the path'), - (11, 'secret_backend_rotation', 'secret backend rotation changes based on the backend UUID and next rotation time'); + (11, 'secret_backend_rotation', 'secret backend rotation changes based on the backend UUID and next rotation time'), + (12, 'model_metadata', 'model metadata changes based on the model UUID'); `) } @@ -390,6 +394,7 @@ CREATE TABLE model_metadata ( cloud_region_uuid TEXT, cloud_credential_uuid TEXT, model_type_id INT NOT NULL, + life_id INT NOT NULL, name TEXT NOT NULL, owner_uuid TEXT NOT NULL, CONSTRAINT fk_model_metadata_model @@ -410,6 +415,9 @@ CREATE TABLE model_metadata ( CONSTRAINT fk_model_metadata_owner_uuid FOREIGN KEY (owner_uuid) REFERENCES user(uuid) + CONSTRAINT fk_model_metadata_life_id + FOREIGN KEY (life_id) + REFERENCES life(id) ); CREATE UNIQUE INDEX idx_model_metadata_name_owner @@ -431,7 +439,8 @@ SELECT m.uuid, mt.type AS model_type_type, mm.name, mm.owner_uuid, - u.name AS owner_name + u.name AS owner_name, + l.value AS life FROM model_list m INNER JOIN model_metadata mm ON m.uuid = mm.model_uuid INNER JOIN cloud c ON mm.cloud_uuid = c.uuid @@ -440,7 +449,8 @@ LEFT JOIN cloud_credential cc ON mm.cloud_credential_uuid = cc.uuid INNER JOIN user cco ON cc.owner_uuid = cco.uuid LEFT JOIN cloud ccn ON cc.cloud_uuid = ccn.uuid INNER JOIN model_type mt ON mm.model_type_id = mt.id -INNER JOIN user u ON mm.owner_uuid = u.uuid; +INNER JOIN user u ON mm.owner_uuid = u.uuid +INNER JOIN life l ON mm.life_id = l.id; `) } diff --git a/domain/schema/life.go b/domain/schema/life.go new file mode 100644 index 00000000000..4482961cf6f --- /dev/null +++ b/domain/schema/life.go @@ -0,0 +1,20 @@ +// Copyright 2024 Canonical Ltd. +// Licensed under the AGPLv3, see LICENCE file for details. + +package schema + +import "github.com/juju/juju/core/database/schema" + +func lifeSchema() schema.Patch { + return schema.MakePatch(` +CREATE TABLE life ( + id INT PRIMARY KEY, + value TEXT NOT NULL +); + +INSERT INTO life VALUES + (0, 'alive'), + (1, 'dying'), + (2, 'dead'); +`) +} diff --git a/domain/schema/model.go b/domain/schema/model.go index 9be6f86c8a7..954be48db62 100644 --- a/domain/schema/model.go +++ b/domain/schema/model.go @@ -122,20 +122,6 @@ CREATE TABLE annotation_%[1]s ( } } -func lifeSchema() schema.Patch { - return schema.MakePatch(` -CREATE TABLE life ( - id INT PRIMARY KEY, - value TEXT NOT NULL -); - -INSERT INTO life VALUES - (0, 'alive'), - (1, 'dying'), - (2, 'dead'); -`) -} - func changeLogModelNamespaceSchema() schema.Patch { // Note: These should match exactly the values of the tableNamespaceID // constants above. diff --git a/domain/schema/schema_test.go b/domain/schema/schema_test.go index 1bce8a6da0a..bad3fa470f0 100644 --- a/domain/schema/schema_test.go +++ b/domain/schema/schema_test.go @@ -125,6 +125,9 @@ func (s *schemaSuite) TestControllerTables(c *gc.C) { "model_metadata", "model_type", + // Life + "life", + // Controller config "controller_config", @@ -350,6 +353,10 @@ func (s *schemaSuite) TestControllerTriggers(c *gc.C) { "trg_log_secret_backend_rotation_next_rotation_time_insert", "trg_log_secret_backend_rotation_next_rotation_time_update", "trg_log_secret_backend_rotation_next_rotation_time_delete", + + "trg_log_model_metadata_insert", + "trg_log_model_metadata_update", + "trg_log_model_metadata_delete", ) // These are additional triggers that are not change log triggers, but