Skip to content

Commit

Permalink
Merge branch 'release/0.2.1' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
Charles Lariviere committed Mar 23, 2022
2 parents 667c86a + 2148a0f commit 091d214
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 9 deletions.
77 changes: 74 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# metabase-manager

Manage your Metabase instance programmatically by declaring your desired state. Metabase-manager will create, update,
Manage your Metabase instance programmatically by declaring your desired state. `metabase-manager` will create, update,
and delete objects in Metabase to ensure to it matches your declared configuration.


Expand All @@ -15,14 +15,14 @@ pip install metabase-manager

Here is an example configuration for Users and Groups:
```yaml
# metabase.yaml
# metabase.yml

users:
- email: jdoe@example.com
first_name: Jane
last_name: Doe
groups:
- Admin
- Administrators
- Finance

- name: jsmith@example.com
Expand All @@ -38,11 +38,82 @@ groups:
By running the following command, `metabase-manager` will create these users and groups if they don't already exist,
update them if some attributes differ, and delete users and groups that exist in Metabase but are not declared here.
```shell
metabase-manager sync
```

```shell
[CREATE] Group(name='Finance')
[CREATE] Group(name='Marketing')
[DELETE] Group(name='Sales') # Sales is not defined in metabase.yml
[CREATE] User(first_name='Jane', last_name='Doe', email='jdoe@example.com', groups=[Group(name='Administrators'), Group(name='Finance')])
# jsmith@example.com already exists in Metabase, but some attributes or group membership differ
[UPDATE] User(first_name='John', last_name='Smith', email='jsmith@example.com', groups=[Group(name='Marketing')])
```

### Credentials

It is possible to provide credentials to your Metabase instance through the command-line as follows:

```shell
metabase-manager sync --host=https://<org>.metabaseapp.com --user <email> --password <password>
```

It is also possible to provide credentials as environment variables. `metabase-manager` will automatically use
these variables if they are set in the environment.

- `METABASE_HOST=<host>`
- `METABASE_USER=<user>`
- `METABASE_PASSWORD=<password>`


### Configuration

By default, `metabase-manager` will expect to find a `metabase.yml` file in the current directory. You can override this
default, and optionally provide more than one file, with the `--file/-f` parameter.

```shell
metabase-manager sync -f users.yml -f <directory>/groups.yml
```

### Selection

It is possible to run your sync only for certain types of objects by using the `--select/-s` or `--exclude/-e` options.

```shell
metabase-manager sync --select users # only users will be synced
```

```shell
metabase-manager sync --exclude users # everything by users will be synced
```


### Dry Run

It is possible to execute a dry run to see which objects would be created, updated, or deleted given your configuration.
This will only log the changes, but not actually execute any changes on your Metabase instance.

```shell
metabase-manager sync --dry-run
```


### Upsert Only

If you do not want `metabase-manager` to delete anything in your Metabase instance, you can use the `--no-delete` flag.
This is useful if your `metabase.yml` configuration file does not exhaustively define object that you wish to exist in
your Metabase instance.

```shell
metabase-manager sync --no-delete
```



### Supported Entities

Currently, it is possible to manage the following entities:

- Users
- Groups
18 changes: 12 additions & 6 deletions src/metabase_manager/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,16 @@ def cli():
multiple=True,
help="Don't sync certain objects.",
)
@click.option(
"--no-delete",
is_flag=True,
help="Don't run the delete step (only create/update existing objects).",
)
@click.option("--silent", is_flag=True, help="Don't print logs.")
@click.option(
"--dry-run", is_flag=True, help="Don't execute commands that mutate Metabase."
)
def sync(file, host, user, password, select, exclude, silent, dry_run):
def sync(file, host, user, password, select, exclude, no_delete, silent, dry_run):
"""
Sync your declared configuration to Metabase.
"""
Expand Down Expand Up @@ -94,10 +99,11 @@ def sync(file, host, user, password, select, exclude, silent, dry_run):
if not dry_run:
manager.update(entity)

for entity in manager.find_objects_to_delete(obj):
if not silent:
click.echo(click.style(f"[DELETE] {entity}", fg="red"))
if not dry_run:
manager.delete(entity)
if not no_delete:
for entity in manager.find_objects_to_delete(obj):
if not silent:
click.echo(click.style(f"[DELETE] {entity}", fg="red"))
if not dry_run:
manager.delete(entity)

bar()
23 changes: 23 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,26 @@ def test_sync_users(self):
self.assertEqual("User", user6.first_name)
self.assertEqual("Six", user6.last_name)
self.assertSetEqual({1}, set(user6.group_ids))

def test_sync_no_delete(self):
"""Ensure --no-delete results in no delete commands being executed on Metabase."""
group = PermissionGroup.create(using=self.metabase, name="My Group")
groups = [g.name for g in PermissionGroup.list(self.metabase)]
self.assertTrue("Developers" not in groups)
self.assertTrue("My Group" in groups)

result = self.runner.invoke(
sync, ["-f", self.config_path, "--no-delete"], env=self.env
)

self.assertEqual(0, result.exit_code)

groups = [g.name for g in PermissionGroup.list(self.metabase)]
self.assertTrue("Developers" in groups)
# My Group not in metabase.yaml, it should have been deleted if not --no-delete; assert it still exists
self.assertTrue("My Group" in groups)

# assert that without --no-delete, My Group is deleted
result = self.runner.invoke(sync, ["-f", self.config_path], env=self.env)
groups = [g.name for g in PermissionGroup.list(self.metabase)]
self.assertTrue("My Group" not in groups)

0 comments on commit 091d214

Please sign in to comment.