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

Add "auto approve" flag to "schema apply" command #526

Merged
merged 4 commits into from
Jan 31, 2022

Conversation

ericyd
Copy link
Contributor

@ericyd ericyd commented Jan 28, 2022

Closes #519

Summary

  • Adds --approve boolean flag to schema apply command. Default: false. When true, applies schema changes to given DSN without prompting user for approval. Useful for CI environments

Notes

  1. Please feel free to request any/all changes. I know nothing about Go standards, or about naming preferences within this project. I chose --approve instead of --apply because I felt like running atlas schema apply --apply would feel kind of strange, but I'm totally open to other naming options.
  2. I built and tested this locally with a docker-compose setup. I've included my steps below in case it helps in eventually building out some community contributing guidelines.

Docker Compose config:

version: "3"
services:
  go:
    image: golang:1.18beta1-bullseye
    volumes:
      - .:/go/app
    command: bash
    depends_on:
      - db
    environment:
      DB_URL: postgres://postgres:postgres@db:5432/dev?sslmode=disable

  db:
    image: postgres:12.9-alpine
    ports:
      - "5432:5432"
    environment:
      POSTGRES_PASSWORD: postgres
      POSTGRES_USER: postgres
      POSTGRES_DB: development
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:

Steps

  1. Run the Go container: docker-compose run --rm --service-ports go bash
  2. Attach to the Postgres container in a separate terminal tab: docker-compose exec db bash
    1. Connect to the "dev" database: psql dev -U postgres
    2. Create a sample table: create table testing (id text, name text); \q
    3. Create a new database to test the applied changes: createdb testing -U postgres
  3. In the Go container, inspect the schema: cd app && go run ./cmd/atlas schema inspect -d "$DB_URL" > atlas.hcl
  4. Apply the schema to the testing table: go run ./cmd/atlas schema apply -d "postgres://postgres:postgres@db:5432/testing?sslmode=disable" -f atlas.hcl --approve
  5. Verify that the changes are applied in the Postgres container:
psql testing -U postgres
select * from testing;
=> should print the column names even though the table is empty

To run the integration tests, I followed the general idea from .github/workflows/ci.yml:

docker-compose up
go test -race -count=2 ./...

Copy link
Member

@masseelch masseelch left a comment

Choose a reason for hiding this comment

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

Hey @ericyd, thanks for the contribution. Great work. Some minor comments / suggestions :-)

Furthermore, we have some integration tests set up, that you can extend to test the new flag. You can take some inspiration from this test.

@@ -53,6 +54,7 @@ func init() {
ApplyCmd.Flags().BoolVarP(&ApplyFlags.DryRun, "dry-run", "", false, "Dry-run. Print SQL plan without prompting for execution")
ApplyCmd.Flags().StringVarP(&ApplyFlags.Addr, "addr", "", "127.0.0.1:5800", "used with -w, local address to bind the server to")
ApplyCmd.Flags().StringSliceVarP(&ApplyFlags.Schema, "schema", "s", nil, "Set schema name")
ApplyCmd.Flags().BoolVarP(&ApplyFlags.AutoApprove, "approve", "", false, "Auto approve. Apply the schema changes without prompting for approval")
Copy link
Member

Choose a reason for hiding this comment

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

I'd try to be more in line what flags like this are usually called. So I propose for --force / -f or --no-interaction or something similar.

Copy link
Member

Choose a reason for hiding this comment

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

Tbh I think for the apply command we can stick to the terraform lingo, since users coming from there will feel at home we can do :

image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I went with --auto-approve because I felt like --force / -f would conflict with the existing -f argument. Open to other ideas too

Comment on lines 120 to 126
if autoApprove {
schemaCmd.Println("Applying changes (automatically approved with --approve flag)")
err = d.ApplyChanges(ctx, changes)
cobra.CheckErr(err)
return
}
Copy link
Member

Choose a reason for hiding this comment

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

Maybe put the prompt in an extra func and only do this:

if autoApprove || promptUser() {
  cobra.CheckErr(d.ApplyChanges(ctx, changes))
}

@ericyd
Copy link
Contributor Author

ericyd commented Jan 29, 2022

When running the integration tests, some of the tests failed. But the new one I added appeared to pass. Let me know if you'd like me to change anything there. I've included details below for how I ran the tests in case I did something incorrectly

cd internal/integration
docker-compose up
go test -race -count=2 ./...

And here are the failures I got

❯ go test -race -count=2 ./...
--- FAIL: TestMySQL_AddDropTable (0.69s)
    --- FAIL: TestMySQL_AddDropTable/Maria103 (0.11s)
        integration_test.go:402: 
                Error Trace:    integration_test.go:402
                                                        integration_test.go:57
                                                        mysql_test.go:62
                                                        mysql_test.go:55
                Error:          Not equal: 
                                expected: 4
                                actual  : 3
                Test:           TestMySQL_AddDropTable/Maria103
        mysql_test.go:1137: 
                Error Trace:    mysql_test.go:1137
                                                        testing.go:912
                                                        testing.go:1049
                                                        testing.go:1253
                                                        panic.go:661
                                                        testing.go:756
                                                        integration_test.go:402
                                                        integration_test.go:57
                                                        mysql_test.go:62
                                                        mysql_test.go:55
                Error:          Received unexpected error:
                                Error 1451: Cannot delete or update a parent row: a foreign key constraint fails
                Test:           TestMySQL_AddDropTable/Maria103
                Messages:       drop tables ["posts" "users" "pets"]
--- FAIL: TestMySQL_Relation (0.34s)
    --- FAIL: TestMySQL_Relation/Maria103 (0.00s)
        mysql_test.go:1131: 
                Error Trace:    mysql_test.go:1131
                                                        integration_test.go:66
                                                        mysql_test.go:68
                                                        mysql_test.go:55
                Error:          Received unexpected error:
                                create "users" table: Error 1050: Table 'users' already exists
                Test:           TestMySQL_Relation/Maria103
--- FAIL: TestMySQL_AddIndexedColumns (1.91s)
    --- FAIL: TestMySQL_AddIndexedColumns/Maria103 (0.11s)
        integration_test.go:402: 
                Error Trace:    integration_test.go:402
                                                        mysql_test.go:102
                                                        mysql_test.go:55
                Error:          Not equal: 
                                expected: 2
                                actual  : 1
                Test:           TestMySQL_AddIndexedColumns/Maria103
--- FAIL: TestMySQL_AddColumns (0.45s)
    --- FAIL: TestMySQL_AddColumns/Maria103 (0.10s)
        integration_test.go:402: 
                Error Trace:    integration_test.go:402
                                                        mysql_test.go:184
                                                        mysql_test.go:55
                Error:          Not equal: 
                                expected: 2
                                actual  : 1
                Test:           TestMySQL_AddColumns/Maria103
--- FAIL: TestMySQL_ColumnInt (2.21s)
    --- FAIL: TestMySQL_ColumnInt/ChangeType (1.33s)
        --- FAIL: TestMySQL_ColumnInt/ChangeType/Maria103 (0.12s)
            integration_test.go:402: 
                        Error Trace:    integration_test.go:402
                                                                mysql_test.go:202
                                                                mysql_test.go:55
                        Error:          Not equal: 
                                        expected: 2
                                        actual  : 1
                        Test:           TestMySQL_ColumnInt/ChangeType/Maria103
    --- FAIL: TestMySQL_ColumnInt/ChangeDefault (0.88s)
        --- FAIL: TestMySQL_ColumnInt/ChangeDefault/Maria103 (0.06s)
            integration_test.go:402: 
                        Error Trace:    integration_test.go:402
                                                                mysql_test.go:215
                                                                mysql_test.go:55
                        Error:          Not equal: 
                                        expected: 2
                                        actual  : 1
                        Test:           TestMySQL_ColumnInt/ChangeDefault/Maria103
--- FAIL: TestMySQL_ColumnString (2.90s)
    --- FAIL: TestMySQL_ColumnString/ChangeType (1.56s)
        --- FAIL: TestMySQL_ColumnString/ChangeType/Maria103 (0.11s)
            integration_test.go:402: 
                        Error Trace:    integration_test.go:402
                                                                mysql_test.go:241
                                                                mysql_test.go:55
                        Error:          Not equal: 
                                        expected: 2
                                        actual  : 1
                        Test:           TestMySQL_ColumnString/ChangeType/Maria103
    --- FAIL: TestMySQL_ColumnString/AddWithDefault (0.24s)
        --- FAIL: TestMySQL_ColumnString/AddWithDefault/Maria103 (0.06s)
            integration_test.go:402: 
                        Error Trace:    integration_test.go:402
                                                                mysql_test.go:257
                                                                mysql_test.go:55
                        Error:          Not equal: 
                                        expected: 2
                                        actual  : 1
                        Test:           TestMySQL_ColumnString/AddWithDefault/Maria103
    --- FAIL: TestMySQL_ColumnString/ChangeDefault (1.10s)
        --- FAIL: TestMySQL_ColumnString/ChangeDefault/Maria103 (0.06s)
            integration_test.go:402: 
                        Error Trace:    integration_test.go:402
                                                                mysql_test.go:269
                                                                mysql_test.go:55
                        Error:          Not equal: 
                                        expected: 2
                                        actual  : 1
                        Test:           TestMySQL_ColumnString/ChangeDefault/Maria103
--- FAIL: TestMySQL_ColumnBool (1.69s)
    --- FAIL: TestMySQL_ColumnBool/Add (0.23s)
        --- FAIL: TestMySQL_ColumnBool/Add/Maria103 (0.05s)
            integration_test.go:402: 
                        Error Trace:    integration_test.go:402
                                                                mysql_test.go:295
                                                                mysql_test.go:55
                        Error:          Not equal: 
                                        expected: 2
                                        actual  : 1
                        Test:           TestMySQL_ColumnBool/Add/Maria103
    --- FAIL: TestMySQL_ColumnBool/AddWithDefault (0.23s)
        --- FAIL: TestMySQL_ColumnBool/AddWithDefault/Maria103 (0.06s)
            integration_test.go:402: 
                        Error Trace:    integration_test.go:402
                                                                mysql_test.go:316
                                                                mysql_test.go:55
                        Error:          Not equal: 
                                        expected: 2
                                        actual  : 1
                        Test:           TestMySQL_ColumnBool/AddWithDefault/Maria103
    --- FAIL: TestMySQL_ColumnBool/ChangeDefault (0.71s)
        --- FAIL: TestMySQL_ColumnBool/ChangeDefault/Maria103 (0.05s)
            integration_test.go:402: 
                        Error Trace:    integration_test.go:402
                                                                mysql_test.go:330
                                                                mysql_test.go:55
                        Error:          Not equal: 
                                        expected: 2
                                        actual  : 1
                        Test:           TestMySQL_ColumnBool/ChangeDefault/Maria103
    --- FAIL: TestMySQL_ColumnBool/ChangeNull (0.53s)
        --- FAIL: TestMySQL_ColumnBool/ChangeNull/Maria103 (0.05s)
            integration_test.go:402: 
                        Error Trace:    integration_test.go:402
                                                                mysql_test.go:352
                                                                mysql_test.go:55
                        Error:          Not equal: 
                                        expected: 2
                                        actual  : 1
                        Test:           TestMySQL_ColumnBool/ChangeNull/Maria103
--- FAIL: TestMySQL_ForeignKey (3.31s)
    --- FAIL: TestMySQL_ForeignKey/ChangeAction (1.24s)
        --- FAIL: TestMySQL_ForeignKey/ChangeAction/Maria103 (0.09s)
            integration_test.go:402: 
                        Error Trace:    integration_test.go:402
                                                                mysql_test.go:368
                                                                mysql_test.go:55
                        Error:          Not equal: 
                                        expected: 3
                                        actual  : 2
                        Test:           TestMySQL_ForeignKey/ChangeAction/Maria103
    --- FAIL: TestMySQL_ForeignKey/UnsetNull (1.26s)
        --- FAIL: TestMySQL_ForeignKey/UnsetNull/Maria103 (0.09s)
            integration_test.go:402: 
                        Error Trace:    integration_test.go:402
                                                                mysql_test.go:395
                                                                mysql_test.go:55
                        Error:          Not equal: 
                                        expected: 3
                                        actual  : 2
                        Test:           TestMySQL_ForeignKey/UnsetNull/Maria103
    --- FAIL: TestMySQL_ForeignKey/AddDrop (0.81s)
        --- FAIL: TestMySQL_ForeignKey/AddDrop/Maria103 (0.06s)
            integration_test.go:402: 
                        Error Trace:    integration_test.go:402
                                                                mysql_test.go:424
                                                                mysql_test.go:55
                        Error:          Not equal: 
                                        expected: 2
                                        actual  : 1
                        Test:           TestMySQL_ForeignKey/AddDrop/Maria103
FAIL
FAIL    ariga.io/atlas/internal/integration     509.146s
ok      ariga.io/atlas/internal/integration/hclsqlspec  0.200s
FAIL

Copy link
Member

@masseelch masseelch left a comment

Choose a reason for hiding this comment

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

💯

Regarding your local tests: are you sure the docker containers were clean? Maybe there have been some left over data. This happens sometimes if you stop the test mid run. They don't get cleaned properly then.

}

func applyRun(d *Driver, dsn string, file string, dryRun bool) {
func applyRun(d *Driver, dsn string, file string, dryRun bool, autoApprove bool) {
Copy link
Member

Choose a reason for hiding this comment

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

@rotemtam should we introduce a small config struct here?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, but can be done in additional PR.

Copy link
Member

@rotemtam rotemtam left a comment

Choose a reason for hiding this comment

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

well done @ericyd ,
notice that CI is red, please run go generate ./... to update the CLI documentation after your flag addition. thanks!

Copy link
Member

@rotemtam rotemtam left a comment

Choose a reason for hiding this comment

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

Awesome job @ericyd, thanks for your contribution!

@rotemtam rotemtam merged commit a037e20 into ariga:master Jan 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add "auto approve" option to schema apply command
4 participants