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

monitor/schema: support using atlas hcl file in monitor action #299

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/ci-go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -444,9 +444,15 @@ jobs:
- run: go install ./cmd/atlas-action
env:
CGO_ENABLED: 0
- id: sanity
- name: sanity using url
uses: ./monitor/schema
with:
cloud-token: ${{ secrets.ATLAS_AGENT_TOKEN }}
url: 'mysql://root:pass@localhost:3306/dev'
slug: 'github-action'
- name: sanity using config
uses: ./monitor/schema
with:
cloud-token: ${{ secrets.ATLAS_AGENT_TOKEN }}
config: 'file://monitor/schema/atlas.hcl'
env: 'dev'
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,10 @@ Can be used periodically to [monitor](https://atlasgo.io/monitoring) changes in

* `cloud-token` - (required) The Atlas Cloud token to use for authentication. To create
a cloud token see the [docs](https://atlasgo.io/cloud/bots).
* `url` - (required) The URL of the database to monitor. For example: `mysql://root:pass@localhost:3306/prod`.
* `url` - (optional) The URL of the database to monitor. For example: `mysql://root:pass@localhost:3306/prod` (mutually exclusive with `config` and `env`).
* `config` - (optional) The URL of the Atlas configuration file. By default, Atlas will look for a file
named `atlas.hcl` in the current directory. For example, `file://config/atlas.hcl` (mutually exclusive with `url`).
* `env` - (optional) The environment to use from the Atlas configuration file. For example, `dev` (mutually exclusive with `url`).
* `slug` - (optional) Unique identifier for the database server.
* `schemas` - (optional) List of database schemas to include (by default includes all schemas). see: https://atlasgo.io/declarative/inspect#inspect-multiple-schemas.
* `exclude` - (optional) List of exclude patterns from inspection. see: https://atlasgo.io/declarative/inspect#exclude-schemas.
Expand Down
34 changes: 29 additions & 5 deletions atlasaction/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,13 @@ func (a *Actions) MonitorSchema(ctx context.Context) error {
if err != nil {
return err
}
var (
config = a.GetInput("config")
env = a.GetInput("env")
)
if (config != "" || env != "") && db.String() != "" {
return errors.New("only one of the inputs 'config' or 'url' should be provided")
ronenlu marked this conversation as resolved.
Show resolved Hide resolved
}
var (
id = cloud.ScopeIdent{
URL: db.Redacted(),
Expand All @@ -833,14 +840,27 @@ func (a *Actions) MonitorSchema(ctx context.Context) error {
}); err != nil {
return fmt.Errorf("failed to login to Atlas Cloud: %w", err)
}
res, err := a.Atlas.SchemaInspect(ctx, &atlasexec.SchemaInspectParams{
URL: db.String(),
// Get the URL on the indentifier if not provided (needed for the snapshot hash).
if id.URL == "" {
if id.URL, err = a.Atlas.SchemaInspect(ctx, &atlasexec.SchemaInspectParams{
ConfigURL: config,
Env: env,
Format: `{{ .RedactedURL }}`,
}); err != nil {
return fmt.Errorf("failed to inspect the schema: %w", err)
}
}
params := &atlasexec.SchemaInspectParams{
ronenlu marked this conversation as resolved.
Show resolved Hide resolved
Schema: id.Schemas,
Exclude: id.Schemas,
Format: `{{ printf "# %s\n%s" .Hash .MarshalHCL }}`,
})
if err != nil {
return fmt.Errorf("failed to inspect the schema: %w", err)
}
if db.String() != "" {
params.URL = db.String()
}
if config != "" {
params.ConfigURL = config
params.Env = env
ronenlu marked this conversation as resolved.
Show resolved Hide resolved
}
cc, err := a.cloudClient(ctx, "cloud-token")
if err != nil {
Expand All @@ -850,6 +870,10 @@ func (a *Actions) MonitorSchema(ctx context.Context) error {
if err != nil {
return fmt.Errorf("failed to get the schema snapshot hash: %w", err)
}
res, err := a.Atlas.SchemaInspect(ctx, params)
if err != nil {
return fmt.Errorf("failed to inspect the schema: %w", err)
}
var (
parts = strings.SplitN(res, "\n", 2)
hash = strings.TrimPrefix(parts[0], "# ")
Expand Down
76 changes: 66 additions & 10 deletions atlasaction/action_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,11 +555,13 @@ func TestMonitorSchema(t *testing.T) {
ctx = context.Background()
)
for _, tt := range []struct {
name, url, slug string
name, url, slug, config string
schemas, exclude []string
latestHash, newHash, hcl string
exScopeIdent cloud.ScopeIdent
exSnapshot *cloud.SnapshotInput
exMatch bool
wantErr bool
}{
{
name: "no latest hash",
Expand All @@ -572,6 +574,11 @@ func TestMonitorSchema(t *testing.T) {
Hash: "hash",
HCL: "hcl",
},
exScopeIdent: cloud.ScopeIdent{
URL: must(url.Parse(u)).Redacted(),
Schemas: []string{},
Exclude: []string{},
},
},
{
name: "latest hash no match",
Expand All @@ -585,6 +592,11 @@ func TestMonitorSchema(t *testing.T) {
Hash: "hash",
HCL: "hcl",
},
exScopeIdent: cloud.ScopeIdent{
URL: must(url.Parse(u)).Redacted(),
Schemas: []string{},
Exclude: []string{},
},
},
{
name: "hash match old hash func",
Expand All @@ -595,6 +607,11 @@ func TestMonitorSchema(t *testing.T) {
schemas: []string{},
exclude: []string{},
exMatch: true,
exScopeIdent: cloud.ScopeIdent{
URL: must(url.Parse(u)).Redacted(),
Schemas: []string{},
Exclude: []string{},
},
},
{
name: "hash match new hash func",
Expand All @@ -605,6 +622,11 @@ func TestMonitorSchema(t *testing.T) {
schemas: []string{},
exclude: []string{},
exMatch: true,
exScopeIdent: cloud.ScopeIdent{
URL: must(url.Parse(u)).Redacted(),
Schemas: []string{},
Exclude: []string{},
},
},
{
name: "with slug",
Expand All @@ -616,6 +638,12 @@ func TestMonitorSchema(t *testing.T) {
schemas: []string{},
exclude: []string{},
exMatch: true,
exScopeIdent: cloud.ScopeIdent{
URL: must(url.Parse(u)).Redacted(),
ExtID: "slug",
Schemas: []string{},
Exclude: []string{},
},
},
{
name: "with schema and exclude",
Expand All @@ -627,6 +655,33 @@ func TestMonitorSchema(t *testing.T) {
schemas: []string{"foo", "bar"},
exclude: []string{"foo.*", "bar.*.*"},
exMatch: true,
exScopeIdent: cloud.ScopeIdent{
URL: must(url.Parse(u)).Redacted(),
ExtID: "slug",
Schemas: []string{"foo", "bar"},
Exclude: []string{"foo.*", "bar.*.*"},
},
},
{
name: "url and config should rerurn error",
url: u,
config: "config",
wantErr: true,
},
{
name: "hash match old hash func, using config",
config: "file:/atlas.hcl",
latestHash: atlasaction.OldAgentHash("hcl"),
newHash: "hash",
hcl: "hcl",
schemas: []string{},
exclude: []string{},
exMatch: true,
exScopeIdent: cloud.ScopeIdent{
URL: "# hash\nhcl",
Schemas: []string{},
Exclude: []string{},
},
},
} {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -637,6 +692,7 @@ func TestMonitorSchema(t *testing.T) {
"cloud-token": "token",
"url": tt.url,
"slug": tt.slug,
"config": tt.config,
"schemas": strings.Join(tt.schemas, "\n"),
"exclude": strings.Join(tt.exclude, "\n"),
},
Expand All @@ -660,16 +716,16 @@ func TestMonitorSchema(t *testing.T) {
)
)
require.NoError(t, err)
require.NoError(t, as.MonitorSchema(ctx))
err = as.MonitorSchema(ctx)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, &cloud.PushSnapshotInput{
ScopeIdent: cloud.ScopeIdent{
URL: must(url.Parse(tt.url)).Redacted(),
ExtID: tt.slug,
Schemas: tt.schemas,
Exclude: tt.exclude,
},
Snapshot: tt.exSnapshot,
HashMatch: tt.exMatch,
ScopeIdent: tt.exScopeIdent,
Snapshot: tt.exSnapshot,
HashMatch: tt.exMatch,
}, cc.lastInput)
require.Equal(t, map[string]string{"url": "url"}, act.output)
})
Expand Down
4 changes: 2 additions & 2 deletions atlasaction/testdata/schema.hcl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
table "t1" {
schema = schema.public
schema = schema.main
column "c1" {
type = int
}
}

schema "public" {}
schema "main" {}
12 changes: 10 additions & 2 deletions monitor/schema/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@ inputs:
description: 'The token that is used to connect to Atlas Cloud (should be passed as a secret).'
required: true
url:
description: 'URL of the database to sync.'
required: true
description: 'URL of the database to sync (mutually exclusive with `config` and `env`).'
required: false
config:
description: 'The URL of the Atlas configuration file (mutually exclusive with `url`).
For example, `file://config/atlas.hcl`, learn more about [Atlas configuration files](https://atlasgo.io/atlas-schema/projects).'
required: false
env:
description: 'The environment to use from the Atlas configuration file. For example, `dev`
(mutually exclusive with `url`).'
required: false
slug:
description: 'Optional unique identifier for the database server.'
required: false
Expand Down
4 changes: 4 additions & 0 deletions monitor/schema/atlas.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# used in monitor github action for integration tests
env "dev" {
url = "mysql://root:pass@localhost:3306/dev"
}
Loading