From 094fed733858a6fb3f75bfca18b4b4780f70b229 Mon Sep 17 00:00:00 2001 From: Joshua Sierles Date: Mon, 9 Oct 2023 23:02:42 +0200 Subject: [PATCH 1/6] Fixes for Supabase commands (#2892) --- .gitignore | 1 + gql/generated.go | 4 +- gql/schema.graphql | 67 +++++++++++++--- internal/command/extensions/extensions.go | 3 +- .../command/extensions/supabase/create.go | 64 +++++++++++++++ .../command/extensions/supabase/dashboard.go | 42 ++++++++++ .../command/extensions/supabase/destroy.go | 80 +++++++++++++++++++ internal/command/extensions/supabase/list.go | 56 +++++++++++++ .../command/extensions/supabase/status.go | 70 ++++++++++++++++ .../command/extensions/supabase/supabase.go | 19 +++++ 10 files changed, 391 insertions(+), 15 deletions(-) create mode 100644 internal/command/extensions/supabase/create.go create mode 100644 internal/command/extensions/supabase/dashboard.go create mode 100644 internal/command/extensions/supabase/destroy.go create mode 100644 internal/command/extensions/supabase/list.go create mode 100644 internal/command/extensions/supabase/status.go create mode 100644 internal/command/extensions/supabase/supabase.go diff --git a/.gitignore b/.gitignore index 7917bbb3d7..007f614b8d 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ out .direnv/ /ci-preflight-test-results.njson +.tool-versions diff --git a/gql/generated.go b/gql/generated.go index c2cdd5a355..ba72cdd35d 100644 --- a/gql/generated.go +++ b/gql/generated.go @@ -36,14 +36,14 @@ func (v *AddOnData) GetStatus() string { return v.Status } type AddOnType string const ( - // A Logtail log receiver - AddOnTypeLogtail AddOnType = "logtail" // A PlanetScale database AddOnTypePlanetscale AddOnType = "planetscale" // An Upstash Redis database AddOnTypeRedis AddOnType = "redis" // A Sentry project endpoint AddOnTypeSentry AddOnType = "sentry" + // A Supabase database + AddOnTypeSupabase AddOnType = "supabase" // An Upstash Redis database AddOnTypeUpstashRedis AddOnType = "upstash_redis" ) diff --git a/gql/schema.graphql b/gql/schema.graphql index 673e9a1fc1..02922f49f9 100644 --- a/gql/schema.graphql +++ b/gql/schema.graphql @@ -68,6 +68,16 @@ enum AccessTokenType { """ pat + """ + used for Sentry + """ + sentry + + """ + access token + """ + token + """ token generated for our UI frontend """ @@ -190,11 +200,6 @@ type AddOn implements Node { Status of the add-on """ status: String - - """ - Token for the add-on - """ - token: String } """ @@ -298,16 +303,11 @@ type AddOnProvider { selectName: Boolean! selectRegion: Boolean! selectReplicaRegions: Boolean! - tosAgreement: String! + tosAgreement: String tosUrl: String } enum AddOnType { - """ - A Logtail log receiver - """ - logtail - """ A PlanetScale database """ @@ -323,6 +323,11 @@ enum AddOnType { """ sentry + """ + A Supabase database + """ + supabase + """ An Upstash Redis database """ @@ -435,6 +440,11 @@ input AllocateIPAddressInput { """ region: String + """ + The name of the associated service + """ + serviceName: String + """ The type of IP address to allocate (v4, v6, or private_v6) """ @@ -1664,6 +1674,11 @@ input BuildVolumeInput { """ clientMutationId: String + """ + compute requirements for volume placement (cpu, mem, gpu, ...) + """ + computeRequirements: JSON + """ id of host dedication """ @@ -5872,6 +5887,7 @@ type LimitedAccessToken implements Node { id: ID! name: String! organization: Organization! + profileParams: JSON token: String! tokenHeader: String user: User! @@ -5957,6 +5973,27 @@ type LogEntry { timestamp: ISO8601DateTime! } +""" +Autogenerated input type of LogOut +""" +input LogOutInput { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String +} + +""" +Autogenerated return type of LogOut. +""" +type LogOutPayload { + """ + A unique identifier for the client performing the mutation. + """ + clientMutationId: String + ok: Boolean! +} + type LoggedCertificate implements Node { cert: String! id: ID! @@ -6905,6 +6942,12 @@ type Mutations { """ input: LockAppInput! ): LockAppPayload + logOut( + """ + Parameters for LogOut + """ + input: LogOutInput! + ): LogOutPayload migrateVolume( """ Parameters for MigrateVolume @@ -7299,7 +7342,7 @@ type Organization implements Node { """ Single sign-on link for the given integration type """ - addOnSsoLink: String! + addOnSsoLink: String """ List third party integrations associated with an organization diff --git a/internal/command/extensions/extensions.go b/internal/command/extensions/extensions.go index 4bedfba419..b3c6b943c3 100644 --- a/internal/command/extensions/extensions.go +++ b/internal/command/extensions/extensions.go @@ -7,6 +7,7 @@ import ( "github.com/superfly/flyctl/internal/command" "github.com/superfly/flyctl/internal/command/extensions/planetscale" sentry_ext "github.com/superfly/flyctl/internal/command/extensions/sentry" + "github.com/superfly/flyctl/internal/command/extensions/supabase" ) func New() (cmd *cobra.Command) { @@ -19,6 +20,6 @@ func New() (cmd *cobra.Command) { cmd.Args = cobra.NoArgs - cmd.AddCommand(sentry_ext.New(), planetscale.New()) + cmd.AddCommand(sentry_ext.New(), planetscale.New(), supabase.New()) return } diff --git a/internal/command/extensions/supabase/create.go b/internal/command/extensions/supabase/create.go new file mode 100644 index 0000000000..c2e320119d --- /dev/null +++ b/internal/command/extensions/supabase/create.go @@ -0,0 +1,64 @@ +package supabase + +import ( + "context" + + "github.com/spf13/cobra" + "github.com/superfly/flyctl/gql" + "github.com/superfly/flyctl/internal/appconfig" + "github.com/superfly/flyctl/internal/command" + extensions_core "github.com/superfly/flyctl/internal/command/extensions/core" + "github.com/superfly/flyctl/internal/command/orgs" + "github.com/superfly/flyctl/internal/command/secrets" + "github.com/superfly/flyctl/internal/flag" +) + +func create() (cmd *cobra.Command) { + + const ( + short = "Provision a Supabase PostgreSQL database" + long = short + "\n" + ) + + cmd = command.New("create", short, long, runCreate, command.RequireSession, command.LoadAppNameIfPresent) + flag.Add(cmd, + flag.App(), + flag.AppConfig(), + flag.Org(), + flag.Region(), + flag.String{ + Name: "name", + Shorthand: "n", + Description: "The name of your database", + }, + ) + return cmd +} + +func runCreate(ctx context.Context) (err error) { + appName := appconfig.NameFromContext(ctx) + params := extensions_core.ExtensionParams{} + + if appName != "" { + params.AppName = appName + } else { + org, err := orgs.OrgFromFlagOrSelect(ctx) + + if err != nil { + return err + } + + params.Organization = org + } + + params.Provider = "supabase" + extension, err := extensions_core.ProvisionExtension(ctx, params) + + if err != nil { + return err + } + + secrets.DeploySecrets(ctx, gql.ToAppCompact(extension.App), false, false) + + return +} diff --git a/internal/command/extensions/supabase/dashboard.go b/internal/command/extensions/supabase/dashboard.go new file mode 100644 index 0000000000..a6072ba7fa --- /dev/null +++ b/internal/command/extensions/supabase/dashboard.go @@ -0,0 +1,42 @@ +package supabase + +import ( + "context" + + "github.com/spf13/cobra" + + "github.com/superfly/flyctl/gql" + "github.com/superfly/flyctl/internal/command" + extensions_core "github.com/superfly/flyctl/internal/command/extensions/core" + "github.com/superfly/flyctl/internal/flag" +) + +func dashboard() (cmd *cobra.Command) { + const ( + long = `Visit the Supabase database dashboard` + + short = long + usage = "dashboard [database_name]" + ) + + cmd = command.New(usage, short, long, runDashboard, command.RequireSession, command.LoadAppNameIfPresent) + + flag.Add(cmd, + flag.App(), + flag.AppConfig(), + ) + cmd.Args = cobra.MaximumNArgs(1) + return cmd +} + +func runDashboard(ctx context.Context) (err error) { + + extension, _, err := extensions_core.Discover(ctx, gql.AddOnTypeSupabase) + + if err != nil { + return err + } + + err = extensions_core.OpenDashboard(ctx, extension.Name) + return +} diff --git a/internal/command/extensions/supabase/destroy.go b/internal/command/extensions/supabase/destroy.go new file mode 100644 index 0000000000..0d295e6226 --- /dev/null +++ b/internal/command/extensions/supabase/destroy.go @@ -0,0 +1,80 @@ +package supabase + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/superfly/flyctl/gql" + "github.com/superfly/flyctl/iostreams" + + "github.com/superfly/flyctl/client" + "github.com/superfly/flyctl/internal/command" + extensions_core "github.com/superfly/flyctl/internal/command/extensions/core" + "github.com/superfly/flyctl/internal/flag" + "github.com/superfly/flyctl/internal/prompt" +) + +func destroy() (cmd *cobra.Command) { + const ( + long = `Permanently destroy a Supabase database` + + short = long + usage = "destroy [name]" + ) + + cmd = command.New(usage, short, long, runDestroy, command.RequireSession, command.LoadAppNameIfPresent) + + cmd.Args = cobra.MaximumNArgs(1) + + flag.Add(cmd, + flag.App(), + flag.AppConfig(), + flag.Yes(), + ) + + return cmd +} + +func runDestroy(ctx context.Context) (err error) { + io := iostreams.FromContext(ctx) + colorize := io.ColorScheme() + + extension, _, err := extensions_core.Discover(ctx, gql.AddOnTypeSupabase) + + if err != nil { + return err + } + + if !flag.GetYes(ctx) { + const msg = "Destroying a Supabase database is not reversible." + fmt.Fprintln(io.ErrOut, colorize.Red(msg)) + + switch confirmed, err := prompt.Confirmf(ctx, "Destroy Supabase database %s?", extension.Name); { + case err == nil: + if !confirmed { + return nil + } + case prompt.IsNonInteractive(err): + return prompt.NonInteractiveError("yes flag must be specified when not running interactively") + default: + return err + } + } + + var ( + out = iostreams.FromContext(ctx).Out + client = client.FromContext(ctx).API().GenqClient + ) + + _, err = gql.DeleteAddOn(ctx, client, extension.Name) + + if err != nil { + return + } + + fmt.Fprintf(out, "Your Supabase database %s was destroyed\n", extension.Name) + + return +} diff --git a/internal/command/extensions/supabase/list.go b/internal/command/extensions/supabase/list.go new file mode 100644 index 0000000000..e7882e4a27 --- /dev/null +++ b/internal/command/extensions/supabase/list.go @@ -0,0 +1,56 @@ +package supabase + +import ( + "context" + + "github.com/spf13/cobra" + + "github.com/superfly/flyctl/gql" + "github.com/superfly/flyctl/iostreams" + + "github.com/superfly/flyctl/client" + "github.com/superfly/flyctl/internal/command" + "github.com/superfly/flyctl/internal/flag" + "github.com/superfly/flyctl/internal/render" +) + +func list() (cmd *cobra.Command) { + const ( + long = `List your Supabase databases` + short = long + usage = "list" + ) + + cmd = command.New(usage, short, long, runList, command.RequireSession) + + cmd.Aliases = []string{"ls"} + + flag.Add(cmd, + flag.Org(), + ) + + return cmd +} + +func runList(ctx context.Context) (err error) { + var ( + out = iostreams.FromContext(ctx).Out + client = client.FromContext(ctx).API().GenqClient + ) + + response, err := gql.ListAddOns(ctx, client, "supabase") + + var rows [][]string + + for _, addon := range response.AddOns.Nodes { + rows = append(rows, []string{ + addon.Name, + addon.Organization.Slug, + addon.PrimaryRegion, + }) + } + + _ = render.Table(out, "", rows, "Name", "Org", "Primary Region") + + return +} diff --git a/internal/command/extensions/supabase/status.go b/internal/command/extensions/supabase/status.go new file mode 100644 index 0000000000..8b34463db7 --- /dev/null +++ b/internal/command/extensions/supabase/status.go @@ -0,0 +1,70 @@ +package supabase + +import ( + "context" + + "github.com/spf13/cobra" + "github.com/superfly/flyctl/gql" + "github.com/superfly/flyctl/internal/command" + extensions_core "github.com/superfly/flyctl/internal/command/extensions/core" + "github.com/superfly/flyctl/internal/flag" + "github.com/superfly/flyctl/internal/render" + "github.com/superfly/flyctl/iostreams" +) + +func status() *cobra.Command { + const ( + short = "Show details about a Supabase database" + long = short + "\n" + + usage = "status [name]" + ) + + cmd := command.New(usage, short, long, runStatus, + command.RequireSession, command.LoadAppNameIfPresent, + ) + + cmd.Args = cobra.MaximumNArgs(1) + + flag.Add(cmd, + flag.App(), + flag.AppConfig(), + ) + + return cmd +} + +func runStatus(ctx context.Context) (err error) { + var ( + io = iostreams.FromContext(ctx) + ) + + extension, app, err := extensions_core.Discover(ctx, gql.AddOnTypeSupabase) + + if err != nil { + return err + } + + var appName string + + if app != nil { + appName = app.Name + } + + obj := [][]string{ + { + extension.Name, + extension.PrimaryRegion, + extension.Status, + appName, + }, + } + + var cols []string = []string{"Name", "Primary Region", "Status", "App"} + + if err = render.VerticalTable(io.Out, "Status", obj, cols...); err != nil { + return + } + + return +} diff --git a/internal/command/extensions/supabase/supabase.go b/internal/command/extensions/supabase/supabase.go new file mode 100644 index 0000000000..ec3fffb1d0 --- /dev/null +++ b/internal/command/extensions/supabase/supabase.go @@ -0,0 +1,19 @@ +package supabase + +import ( + "github.com/spf13/cobra" + "github.com/superfly/flyctl/internal/command" +) + +func New() (cmd *cobra.Command) { + + const ( + short = "Provision and manage Supabase Postgresql databases" + long = short + "\n" + ) + + cmd = command.New("supabase", short, long, nil) + cmd.AddCommand(create(), destroy(), dashboard(), list(), status()) + + return cmd +} From 11d4e6fffac2b45f11bc46c4ab826bf416f8c5cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Tue, 10 Oct 2023 12:37:22 -0300 Subject: [PATCH 2/6] Avoid agent restarts on each run by fixing the buildtime for dev (#2884) --- internal/buildinfo/env_dev.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/internal/buildinfo/env_dev.go b/internal/buildinfo/env_dev.go index 39dbe6599d..d7e4200986 100644 --- a/internal/buildinfo/env_dev.go +++ b/internal/buildinfo/env_dev.go @@ -8,14 +8,25 @@ import ( "github.com/superfly/flyctl/internal/version" ) -var environment = "development" +var ( + buildDate = "" + environment = "development" +) -func loadBuildTime() error { - cachedBuildTime = time.Now() - return nil +func loadBuildTime() (err error) { + // Makefile sets proper values for buildDate but bare `go run .` doesn't + if buildDate == "" { + buildDate = time.Now().Format(time.RFC3339) + } + cachedBuildTime, err = time.Parse(time.RFC3339, buildDate) + return } func loadVersion() error { - cachedVersion = version.New(cachedBuildTime, "dev", int(cachedBuildTime.Unix())) + // Makefile sets proper values for branchName but bare `go run .` doesn't + if branchName == "" { + branchName = "dev" + } + cachedVersion = version.New(cachedBuildTime, branchName, int(cachedBuildTime.Unix())) return nil } From 9e9dd09e0a39afa4cb45ff5c3cf68e0f04b89503 Mon Sep 17 00:00:00 2001 From: Allison Pierson Date: Wed, 11 Oct 2023 12:40:25 -0500 Subject: [PATCH 3/6] `migrate-to-v2`: finish `--force-standard-migration` for PG (#2895) --- internal/command/migrate_to_v2/machines.go | 4 +++- .../command/migrate_to_v2/migrate_to_v2.go | 24 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/internal/command/migrate_to_v2/machines.go b/internal/command/migrate_to_v2/machines.go index 3c3bd8baec..1e4a7b38dd 100644 --- a/internal/command/migrate_to_v2/machines.go +++ b/internal/command/migrate_to_v2/machines.go @@ -45,7 +45,9 @@ func (m *v2PlatformMigrator) createLaunchMachineInput(oldAllocID string, skipLau mConfig.Metadata[api.MachineConfigMetadataKeyFlyPreviousAlloc] = oldAllocID } - if m.isPostgres { + // Intentionally not checking m.isPostgres here because we need to support + // standard "app"-style migrations for PG apps + if m.pgConsulUrl != "" { mConfig.Env["FLY_CONSUL_URL"] = m.pgConsulUrl mConfig.Metadata[api.MachineConfigMetadataKeyFlyManagedPostgres] = "true" } diff --git a/internal/command/migrate_to_v2/migrate_to_v2.go b/internal/command/migrate_to_v2/migrate_to_v2.go index 29e821f6e3..e28002304f 100644 --- a/internal/command/migrate_to_v2/migrate_to_v2.go +++ b/internal/command/migrate_to_v2/migrate_to_v2.go @@ -315,8 +315,20 @@ func NewV2PlatformMigrator(ctx context.Context, appName string) (V2PlatformMigra leaseDelayBetween := (leaseTimeout - 1*time.Second) / 3 isPostgres := appCompact.IsPostgresApp() && - appFull.ImageDetails.Repository == "flyio/postgres" && - !flag.GetBool(ctx, "force-standard-migration") + appFull.ImageDetails.Repository == "flyio/postgres" + + pgConsulUrl := "" + if isPostgres { + consul, err := apiClient.EnablePostgresConsul(ctx, appCompact.Name) + if err != nil { + return nil, err + } + pgConsulUrl = consul.ConsulURL + } + + if flag.GetBool(ctx, "force-standard-migration") { + isPostgres = false + } migrator := &v2PlatformMigrator{ apiClient: apiClient, @@ -343,13 +355,7 @@ func NewV2PlatformMigrator(ctx context.Context, appName string) (V2PlatformMigra backupMachines: map[string]int{}, machineWaitTimeout: flag.GetDuration(ctx, "wait-timeout"), skipHealthChecks: flag.GetBool(ctx, "skip-health-checks"), - } - if migrator.isPostgres { - consul, err := apiClient.EnablePostgresConsul(ctx, appCompact.Name) - if err != nil { - return nil, err - } - migrator.pgConsulUrl = consul.ConsulURL + pgConsulUrl: pgConsulUrl, } migrator.applyHacks(ctx) From cc672fb4232ddbf99c52db3fc82f0fb2e13f6f1c Mon Sep 17 00:00:00 2001 From: William Batista Date: Thu, 12 Oct 2023 02:38:10 +0800 Subject: [PATCH 4/6] Add CONTRIBUTING.md (#2894) * Add CONTRIBUTING.md People new to the flyctl repo struggle to get around sometimes, so adding a file that explains basic things like building and runnning flyctl, as well as testing, creating a new release, etc. is something useful to share. Co-authored-by: Michael Dwan Co-authored-by: Allison Pierson * Make the preflight making real apps warning bold Hopefully no contributors will be confused * Remove duplication README info Also link to CONTRIBUTING.md * Remove trailing whitespace * Mention pre-commit in CONTRIBUTING.md * Fix small formatting error for example commmand * Apply lillian's suggestions * Fix some more typos + command examples I swear I can write * Apply md's suggestions --------- Co-authored-by: Michael Dwan Co-authored-by: Allison Pierson --- CONTRIBUTING.md | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 41 ++----------------------- 2 files changed, 84 insertions(+), 39 deletions(-) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..ebea82cc10 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,82 @@ +# Development flow + + +## Building + +To build `flyctl`, all you need to do is to run `make build` from the root directory. This will build a binary in the `bin/` directory. Alternatively, you can run `go build .` + +To run `flyctl`, you can just run the binary you built using `make build`: `./bin/flyctl`. So for example, to update a machine, you can run `go run . m update -a `. Alternatively, you can build and run in the same command by running `go run .`, followed by whatever sub-command you want to run. Just note that this will have a slower startup. + + +## Testing + +We have two different kinds of tests in `flyctl`, unit tests and integration tests (preflight). It's recommend to write a test for any features added or bug fixes, in order to prevent regressions in the future. + + +### Integration tests + +Unit tests are stored in individual files next to the functionality they're testing. For example + +`internal/command/secrets/parser_test.go` +is a test for the secrets parsing code + +`internal/command/secrets/parser_test.go`. +You can run these tests by running `make test` from the root directory. + + +### Preflight + +The integration tests, called preflight, are different. They exist to test flyctl functionality on production infra, in order to make sure that entire commands and workflows don't break. Those are located in the `test/preflight` directory. + +For outside contributors, **please be warned that running preflight tests creates real apps and machines and will cost real money**. We already run preflight by default on all pull requests, so we recommend just opening up a draft PR instead. If you work at Fly and want to work on preflight tests, go ahead and continue reading. + +Before running any preflight test, you must first set some specific environment variables. It's recommended to set them up using [direnv](https://direnv.net/docs/installation.html). First, copy the `.direnv/preflight-example` file to `.direnv/preflight`. Next, modify `FLY_PREFLIGHT_TEST_FLY_ORG` to an organization you make specifically for testing. Don't use your `personal` org. Modify `FLY_PREFLIGHT_TEST_FLY_REGIONS` to have two regions, ideally ones not the closest ones. For example, `"iad sin"`. Finally, set `FLY_PREFLIGHT_TEST_ACCESS_TOKEN` to whatever `fly auth token` outputs. + +To run preflight tests, you can just run `make preflight-test`. If you want to run a specific preflight test, run `make preflight-test T=` + +If you're trying to decide whether to write a unit test, or an integration test for your change, I recommend just writing a preflight test. They're usually simpler to write, and there's a lot more examples of how to write them. + + + +## Linting + +With the trifecta of the development process nearly complete, let's talk about linting. The linter we run is [golangci-lint](https://golangci-lint.run/). It helps with finding potential bugs in programs, as well as helping you follow standard go conventions. To run it locally, just run `golangci-lint --path-prefix=. run`. If you'd like to run all of our [pre-commit lints](https://pre-commit.com/), then run `pre-commit run --all-files` + +# Generating the GraphQL Schema + +As of writing this, we host our GraphQL schema on `web`, an internal repo that hosts our GQL based API. Unfortunately, that means that outside contributors can't updated the GraphQL schema used by `flyctl`. While there isn't much of a reason why you may want to do so, we're working on automating update the GQL schema in `flyctl`. + +Updating the GraphQL schema from web is a manual process at the moment. To do so, `cd` into `web/`, and run `bundle exec rails graphql:schema:idl && cp ./schema.graphql ../flyctl/gql/schema.graphql`, assuming that `flyctl/` is in the same directory as `web/` + + +# Cutting a release + +If you have write access to this repo, you can ship a prerelease with: + +`scripts/bump_version.sh prerel` + +or a full release with: + +`scripts/bump_version.sh` + + +# Committing to flyctl + +When committing to `flyctl`, there are a few important things to keep in mind: + +- Keep commits small and focused, it helps greatly when reviewing larger PRs +- Make sure to use descriptive messages in your commit messages, it also helps future people understand why a change was made. +- PRs are squash merged, so please make sure to use descriptive titles + + +## Examples + +[This is a bad example of a commit](https://github.com/superfly/flyctl/pull/1809/commits/6f167c858dbd7ae1324632dda9e29072ddde8ad7), it has a large diff and no explanation as to why this change is being made. [This is a great one](https://github.com/superfly/flyctl/commit/2636f47fe91cbe37018926cb0d7d2227a6887086), since it's a small commit, and it's reasoning as well as the context behind the change. Good commit messages also help contributors in the future to understand *why* we did something a certain way. + + +# Further Go reading + +Go is a weird language full of a million different pitfalls. If you haven't already, I strongly recommend reading through these articles: + +- (just a generally great resource) +- (error wrapping specifically is useful for a lot of the functionality we use) diff --git a/README.md b/README.md index 2331fe6a7a..f70cf288b0 100644 --- a/README.md +++ b/README.md @@ -98,42 +98,5 @@ There is a simple Powershell script, `winbuild.ps1`, which will run the code gen Run `scripts/build-dfly` to build a Docker image from the current branch. Then, use `scripts/dfly` to run it. This assumes you are already authenticated to Fly in your local environment. -## Cutting a release - -If you have write access to this repo, you can ship a prerelease or full release with: - -`scripts/bump_version.sh prerel` - -or - -`scripts/bump_version.sh` - -## Running preflight tests - -A preflight suite of integration tests is located under the test/preflight/ directory. It uses a flyctl binary and runs real user scenarios, including deploying apps and dbs, and validates expected behavior. - -**Warning**: Real apps will be deployed that cost real money. The test fixture does its best to destroy resources it creates, but sometimes it may fail to delete a resource. - -The easiest way to run the preflight tests is: - -Copy `.direnv/preflight-example` to `.direnv/preflight` and edit following these guidelines: - -* Grab your auth token from `~/.fly/config.yml` -* Do not use your "personal" org, create an new org (i.e. `flyctl-tests-YOURNAME`) -* Set 2 regions, ideally not your closest region because it leads - to false positives when --region or primary region handling is buggy. - Run `fly platform regions` for valid ids. - -Finally run the tests: - - make preflight-test - -That builds a flyctl binary (just like running `make`), then runs the preflight tests against that binary. - -To run a single test: - -``` -make preflight-test T=TestAppsV2Example -``` - -Oh, add more preflight tests at `tests/preflight/*` +## Contributing guide +See [CONTRIBUTING.md](./CONTRIBUTING.mdl) From 31d06138257c50b77db4b45b3a38e81869d0dfc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gra=C3=B1a?= Date: Wed, 11 Oct 2023 16:33:38 -0300 Subject: [PATCH 5/6] Add --volume-initial-size flag to set volume sizes on first fly deploy (#2896) --- internal/command/deploy/deploy.go | 6 ++++++ internal/command/deploy/deploy_first.go | 9 +++++++-- internal/command/deploy/machines.go | 7 +++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/internal/command/deploy/deploy.go b/internal/command/deploy/deploy.go index c898a3d7fd..570ce58ad5 100644 --- a/internal/command/deploy/deploy.go +++ b/internal/command/deploy/deploy.go @@ -131,6 +131,11 @@ var CommonFlags = flag.Set{ Description: "Maximum number of machines to update concurrently when using the immediate deployment strategy.", Default: 16, }, + flag.Int{ + Name: "volume-initial-size", + Description: "The initial size in GB for volumes created on first deploy", + Default: 1, + }, flag.VMSizeFlags, } @@ -324,6 +329,7 @@ func deployToMachines( ExcludeRegions: excludeRegions, OnlyRegions: onlyRegions, ImmediateMaxConcurrent: flag.GetInt(ctx, "immediate-max-concurrent"), + VolumeInitialSize: flag.GetInt(ctx, "volume-initial-size"), }) if err != nil { sentry.CaptureExceptionWithAppInfo(err, "deploy", appCompact) diff --git a/internal/command/deploy/deploy_first.go b/internal/command/deploy/deploy_first.go index 9eacd26f65..9bcbf6c4a9 100644 --- a/internal/command/deploy/deploy_first.go +++ b/internal/command/deploy/deploy_first.go @@ -107,12 +107,17 @@ func (md *machineDeployment) provisionVolumesOnFirstDeploy(ctx context.Context) continue } - fmt.Fprintf(md.io.Out, "Creating 1GB volume '%s' for process group '%s'. Use 'fly vol extend' to increase its size\n", m.Source, groupName) + fmt.Fprintf( + md.io.Out, + "Creating a %d GB volume named '%s' for process group '%s'. "+ + "Use 'fly vol extend' to increase its size\n", + md.volumeInitialSize, m.Source, groupName, + ) input := api.CreateVolumeRequest{ Name: m.Source, Region: groupConfig.PrimaryRegion, - SizeGb: api.Pointer(1), + SizeGb: api.Pointer(md.volumeInitialSize), Encrypted: api.Pointer(true), HostDedicationId: md.appConfig.HostDedicationID, ComputeRequirements: md.machineGuest, diff --git a/internal/command/deploy/machines.go b/internal/command/deploy/machines.go index 99565ec2fc..2d04d2dbbe 100644 --- a/internal/command/deploy/machines.go +++ b/internal/command/deploy/machines.go @@ -54,6 +54,7 @@ type MachineDeploymentArgs struct { ExcludeRegions map[string]interface{} OnlyRegions map[string]interface{} ImmediateMaxConcurrent int + VolumeInitialSize int } type machineDeployment struct { @@ -87,6 +88,7 @@ type machineDeployment struct { excludeRegions map[string]interface{} onlyRegions map[string]interface{} immediateMaxConcurrent int + volumeInitialSize int } func NewMachineDeployment(ctx context.Context, args MachineDeploymentArgs) (MachineDeployment, error) { @@ -147,6 +149,10 @@ func NewMachineDeployment(ctx context.Context, args MachineDeploymentArgs) (Mach if immedateMaxConcurrent < 1 { immedateMaxConcurrent = 1 } + volumeInitialSize := 1 + if args.VolumeInitialSize > 0 { + volumeInitialSize = args.VolumeInitialSize + } md := &machineDeployment{ apiClient: apiClient, @@ -172,6 +178,7 @@ func NewMachineDeployment(ctx context.Context, args MachineDeploymentArgs) (Mach excludeRegions: args.ExcludeRegions, onlyRegions: args.OnlyRegions, immediateMaxConcurrent: immedateMaxConcurrent, + volumeInitialSize: volumeInitialSize, } if err := md.setStrategy(); err != nil { return nil, err From 08aa3a4c4a1c2df53723c8c56cf151739f365820 Mon Sep 17 00:00:00 2001 From: Allison Pierson Date: Wed, 11 Oct 2023 17:11:51 -0500 Subject: [PATCH 6/6] `migrate-to-v2`: run GetVolume for each vol to get attached alloc (#2897) --- internal/command/migrate_to_v2/volumes.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/command/migrate_to_v2/volumes.go b/internal/command/migrate_to_v2/volumes.go index 88d033047e..2ffaadcaba 100644 --- a/internal/command/migrate_to_v2/volumes.go +++ b/internal/command/migrate_to_v2/volumes.go @@ -134,6 +134,14 @@ func (m *v2PlatformMigrator) resolveOldVolumes(ctx context.Context) error { if err != nil { return err } + // GetVolumes doesn't return attached allocations or machines. + for i := range vols { + fullVol, err := m.flapsClient.GetVolume(ctx, vols[i].ID) + if err != nil { + return err + } + vols[i] = *fullVol + } m.oldAttachedVolumes = lo.Filter(vols, func(v api.Volume, _ int) bool { if v.AttachedAllocation != nil { for _, a := range m.oldAllocs {