From d888cdd91605297a29a50f3e874b01231b2e66a3 Mon Sep 17 00:00:00 2001 From: michaeljguarino Date: Tue, 17 Sep 2024 20:22:19 -0400 Subject: [PATCH] Implement dedicated cloud console types This provisions a plural console in its own cluster managed entirely by a plural stack. Since it's cost-inefficient, only allow enterprise accounts to use these. --- apps/core/lib/core/clients/console.ex | 63 +++++++++++++++++++ apps/core/lib/core/policies/cloud.ex | 8 +++ apps/core/lib/core/schema/console_instance.ex | 18 +++++- apps/core/lib/core/services/cloud.ex | 10 ++- .../lib/core/services/cloud/configuration.ex | 18 ++++++ apps/core/lib/core/services/cloud/poller.ex | 22 ++++++- apps/core/lib/core/services/cloud/workflow.ex | 62 ++++++++++++++++++ apps/core/lib/core/services/payments.ex | 17 +++++ .../20240917232002_add_dedicated_console.exs | 9 +++ apps/core/test/services/cloud_test.exs | 51 +++++++++++++++ apps/core/test/support/factory.ex | 4 ++ apps/graphql/lib/graphql/schema/cloud.ex | 3 + .../test/mutations/cloud_mutations_test.exs | 1 + .../test/mutations/user_mutation_test.exs | 2 +- config/config.exs | 3 + rel/config/config.exs | 3 + schema/schema.graphql | 13 ++++ .../steps/ConfigureCloudInstanceStep.tsx | 2 + www/src/generated/graphql.ts | 13 +++- 19 files changed, 313 insertions(+), 9 deletions(-) create mode 100644 apps/core/priv/repo/migrations/20240917232002_add_dedicated_console.exs diff --git a/apps/core/lib/core/clients/console.ex b/apps/core/lib/core/clients/console.ex index b3fe4f6d2..d9cdb20db 100644 --- a/apps/core/lib/core/clients/console.ex +++ b/apps/core/lib/core/clients/console.ex @@ -1,6 +1,12 @@ defmodule Core.Clients.Console do require Logger + @me_q """ + query { + me { id } + } + """ + @clusters_q """ query { clusters(first: 100) { @@ -33,6 +39,30 @@ defmodule Core.Clients.Console do } """ + @create_stack_q """ + mutation Create($attributes: StackAttributes!) { + createStack(attributes: $attributes) { + id + } + } + """ + + @update_stack_q """ + mutation update($id: ID!, $attributes: StackAttributes!) { + updateStack(id: $id, attributes: $attributes) { + id + } + } + """ + + @delete_stack_q """ + mutation delete($id: ID!) { + deleteStack(id: $id) { + id + } + } + """ + @repo_q """ query Repo($url: String!) { gitRepository(url: $url) { @@ -41,10 +71,17 @@ defmodule Core.Clients.Console do } """ + @project_q """ + query Project($name: String!) { + project(name: $name) { id } + } + """ + @stack_q """ query Stack($id: ID!) { infrastructureStack(id: $id) { id + status output { name value @@ -74,6 +111,16 @@ defmodule Core.Clients.Console do |> service_resp("gitRepository") end + def me(client) do + Req.post(client, graphql: {@me_q, %{}}) + |> service_resp("me") + end + + def project(client, name) do + Req.post(client, graphql: {@project_q, %{name: name}}) + |> service_resp("project") + end + def create_service(client, cluster_id, attrs) do Req.post(client, graphql: {@create_svc_q, %{clusterId: cluster_id, attributes: attrs}}) |> service_resp("createServiceDeployment") @@ -90,6 +137,22 @@ defmodule Core.Clients.Console do |> ignore_not_found() end + def create_stack(client, attrs) do + Req.post(client, graphql: {@create_stack_q, %{attributes: attrs}}) + |> service_resp("createInfrastructureStack") + end + + def update_stack(client, id, attrs) do + Req.post(client, graphql: {@update_stack_q, %{id: id, attributes: attrs}}) + |> service_resp("updateInfrastructureStack") + end + + def delete_stack(client, id) do + Req.post(client, graphql: {@delete_stack_q, %{id: id}}) + |> service_resp("deleteInfrastructureStack") + |> ignore_not_found() + end + def stack(client, id) do Req.post(client, graphql: {@stack_q, %{id: id}}) |> case do diff --git a/apps/core/lib/core/policies/cloud.ex b/apps/core/lib/core/policies/cloud.ex index b63f23c0e..5360ed453 100644 --- a/apps/core/lib/core/policies/cloud.ex +++ b/apps/core/lib/core/policies/cloud.ex @@ -3,6 +3,14 @@ defmodule Core.Policies.Cloud do alias Core.Schema.{User, ConsoleInstance} alias Core.Services.Payments + def can?(%User{} = user, %ConsoleInstance{type: :dedicated}, :create) do + case Payments.enterprise?(user) do + true -> :pass + _ -> + {:error, "you must be on an enterprise plan to create a dedicated Plural cluster"} + end + end + def can?(%User{} = user, %ConsoleInstance{}, :create) do case Payments.has_feature?(user, :cd) do true -> :pass diff --git a/apps/core/lib/core/schema/console_instance.ex b/apps/core/lib/core/schema/console_instance.ex index 80efe5903..04d8d669e 100644 --- a/apps/core/lib/core/schema/console_instance.ex +++ b/apps/core/lib/core/schema/console_instance.ex @@ -3,6 +3,7 @@ defmodule Core.Schema.ConsoleInstance do alias Piazza.Ecto.EncryptedString alias Core.Schema.{PostgresCluster, CloudCluster, User} + defenum Type, shared: 0, dedicated: 1 defenum Size, small: 0, medium: 1, large: 2 defenum Status, pending: 0, @@ -10,13 +11,16 @@ defmodule Core.Schema.ConsoleInstance do deployment_created: 2, provisioned: 3, deployment_deleted: 4, - database_deleted: 5 + database_deleted: 5, + stack_created: 6, + stack_deleted: 7 @region_map %{ aws: ~w(us-east-1) } schema "console_instances" do + field :type, Type, default: :shared field :name, :string field :status, Status field :subdomain, :string @@ -33,6 +37,7 @@ defmodule Core.Schema.ConsoleInstance do embeds_one :instance_status, InstanceStatus, on_replace: :update do field :db, :boolean, default: false field :svc, :boolean, default: false + field :stack, :boolean, default: false end embeds_one :configuration, Configuration, on_replace: :update do @@ -96,14 +101,14 @@ defmodule Core.Schema.ConsoleInstance do def regions(), do: @region_map - @valid ~w(name cloud size region status subdomain url external_id postgres_id cluster_id owner_id)a + @valid ~w(name type cloud size region status subdomain url external_id postgres_id cluster_id owner_id)a def changeset(model, attrs \\ %{}) do model |> cast(attrs, @valid) |> cast_embed(:configuration, with: &configuration_changeset/2) |> cast_embed(:instance_status, with: &status_changeset/2) - |> validate_required(@valid -- [:external_id]) + |> validate_required(@valid -- ~w(external_id postgres_id cluster_id)a) |> foreign_key_constraint(:cluster_id) |> foreign_key_constraint(:postgres_id) |> foreign_key_constraint(:owner_id) @@ -118,6 +123,13 @@ defmodule Core.Schema.ConsoleInstance do |> cast(attrs, @valid) |> validate_required(~w(name region status cloud size)a) |> validate_region() + |> foreign_key_constraint(:cluster_id) + |> foreign_key_constraint(:postgres_id) + |> foreign_key_constraint(:owner_id) + |> unique_constraint(:subdomain) + |> unique_constraint(:name) + |> validate_format(:name, ~r/[a-z][a-z0-9]{5,10}/, message: "must be an alphanumeric string between 5 and 11 characters") + |> validate_region() end defp validate_region(cs) do diff --git a/apps/core/lib/core/services/cloud.ex b/apps/core/lib/core/services/cloud.ex index 08f386ec5..eb5fe96fe 100644 --- a/apps/core/lib/core/services/cloud.ex +++ b/apps/core/lib/core/services/cloud.ex @@ -47,8 +47,14 @@ defmodule Core.Services.Cloud do |> allow(user, :create) |> when_ok(:insert) end) - |> add_operation(:cluster, fn _ -> select_cluster(attrs[:cloud], attrs[:region]) end) - |> add_operation(:postgres, fn _ -> select_roach(attrs[:cloud]) end) + |> add_operation(:cluster, fn + %{inst: %ConsoleInstance{type: :dedicated}} -> {:ok, %{id: nil}} + _ -> select_cluster(attrs[:cloud], attrs[:region]) + end) + |> add_operation(:postgres, fn + %{inst: %ConsoleInstance{type: :dedicated}} -> {:ok, %{id: nil}} + _ -> select_roach(attrs[:cloud]) + end) |> add_operation(:sa, fn _ -> Accounts.create_service_account(%{ name: "#{name}-cloud-sa", diff --git a/apps/core/lib/core/services/cloud/configuration.ex b/apps/core/lib/core/services/cloud/configuration.ex index b16dfa7a7..28cfec786 100644 --- a/apps/core/lib/core/services/cloud/configuration.ex +++ b/apps/core/lib/core/services/cloud/configuration.ex @@ -25,11 +25,29 @@ defmodule Core.Services.Cloud.Configuration do size: "#{size}", }) |> Map.put(:size, "#{size}") + |> Enum.filter(fn {_, v} -> is_binary(v) end) |> Enum.map(fn {k, v} -> %{name: k, value: v} end) end + def stack_attributes(%ConsoleInstance{name: name} = inst, attrs) do + Map.merge(attrs, %{ + name: "dedicated-#{name}", + cluster_id: Core.conf(:mgmt_cluster), + type: "TERRAFORM", + manageState: true, + approval: false, + configuration: %{version: "1.8"}, + git: %{ref: "main", folder: "terraform/modules/dedicated/#{inst.cloud}"}, + variables: Jason.encode!(%{ + development: String.contains?(Core.conf(:dedicated_project), "dev"), + service_secrets: build(inst) |> Map.new(fn %{name: n, value: v} -> {n, v} end) + }) + }) + end + # defp certificate(%ConsoleInstance{postgres: %PostgresCluster{certificate: cert}}), do: cert + defp build_pg_url(%ConsoleInstance{type: :dedicated}), do: nil defp build_pg_url(%ConsoleInstance{ configuration: %{dbuser: u, dbpassword: p, database: database}, postgres: %PostgresCluster{host: host} diff --git a/apps/core/lib/core/services/cloud/poller.ex b/apps/core/lib/core/services/cloud/poller.ex index 396a0fca0..d9b46bf1b 100644 --- a/apps/core/lib/core/services/cloud/poller.ex +++ b/apps/core/lib/core/services/cloud/poller.ex @@ -6,7 +6,7 @@ defmodule Core.Services.Cloud.Poller do @poll :timer.minutes(2) - defmodule State, do: defstruct [:client, :repo] + defmodule State, do: defstruct [:client, :dedicated_client, :repo, :project] def start_link(_) do GenServer.start_link(__MODULE__, :ok, name: __MODULE__) @@ -16,15 +16,24 @@ defmodule Core.Services.Cloud.Poller do :timer.send_interval(@poll, :clusters) :timer.send_interval(@poll, :pgs) send self(), :repo - {:ok, %State{client: Console.new(Core.conf(:console_url), Core.conf(:console_token))}} + send self(), :project + client = Console.new(Core.conf(:console_url), Core.conf(:console_token)) + dedicated_client = Console.new(Core.conf(:console_url), Core.conf(:dedicated_console_token)) + {:ok, %State{client: client, dedicated_client: dedicated_client}} end def repository(), do: GenServer.call(__MODULE__, :repo) + def project(), do: GenServer.call(__MODULE__, :project) + def handle_call(:repo, _, %{repo: id} = state) when is_binary(id), do: {:reply, {:ok, id}, state} def handle_call(:repo, _, state), do: {:reply, {:error, "repo not pulled"}, state} + def handle_call(:project, _, %{project: id} = state) when is_binary(id), + do: {:reply, {:ok, id}, state} + def handle_call(:project, _, state), do: {:reply, {:error, "project not pulled"}, state} + def handle_info(:repo, %{client: client} = state) do case Console.repo(client, Core.conf(:mgmt_repo)) do {:ok, id} -> {:noreply, %{state | repo: id}} @@ -34,6 +43,15 @@ defmodule Core.Services.Cloud.Poller do end end + def handle_info(:project, %{dedicated_client: client} = state) do + case Console.project(client, Core.conf(:dedicated_project)) do + {:ok, id} -> {:noreply, %{state | project: id}} + err -> + Logger.warn "failed to find dedicated project: #{inspect(err)}" + {:noreply, state} + end + end + def handle_info(:clusters, %{client: client} = state) do with {:ok, clusters} <- Console.clusters(client) do Enum.each(clusters, &upsert_cluster/1) diff --git a/apps/core/lib/core/services/cloud/workflow.ex b/apps/core/lib/core/services/cloud/workflow.ex index 98e6cd63c..e638b7515 100644 --- a/apps/core/lib/core/services/cloud/workflow.ex +++ b/apps/core/lib/core/services/cloud/workflow.ex @@ -8,6 +8,14 @@ defmodule Core.Services.Cloud.Workflow do require Logger + def sync(%ConsoleInstance{type: :dedicated, external_id: id} = inst) when is_binary(id) do + with {:ok, project_id} <- Poller.project(), + {:ok, repo_id} <- Poller.repository(), + {:ok, actor} <- Console.me(dedicated_console()), + attrs = %{actor_id: actor, project_id: project_id, repository_id: repo_id}, + do: Console.update_stack(dedicated_console(), id, Configuration.stack_attributes(inst, attrs)) + end + def sync(%ConsoleInstance{external_id: id} = instance) when is_binary(id) do instance = Repo.preload(instance, [:cluster, :postgres]) Console.update_service(console(), id, %{ @@ -39,6 +47,7 @@ defmodule Core.Services.Cloud.Workflow do case down(acc) do {:ok, %ConsoleInstance{status: :pending} = inst} -> {:halt, inst} {:ok, %ConsoleInstance{status: :database_deleted} = inst} -> {:halt, inst} + {:ok, %ConsoleInstance{status: :stack_deleted} = inst} -> {:halt, inst} {:ok, inst} -> {:cont, inst} err -> :timer.sleep(:timer.seconds(10)) @@ -49,6 +58,38 @@ defmodule Core.Services.Cloud.Workflow do |> finalize(:down) end + defp up(%ConsoleInstance{status: :pending, type: :dedicated} = inst) do + with {:ok, id} <- Poller.project(), + {:ok, repo_id} <- Poller.repository(), + {:ok, actor} <- Console.me(dedicated_console()), + attrs = %{actor_id: actor, project_id: id, repository_id: repo_id}, + {:ok, stack_id} <- Console.create_stack(dedicated_console(), Configuration.stack_attributes(inst, attrs)) do + ConsoleInstance.changeset(inst, %{ + instance_status: %{stack: true}, + status: :stack_created, + external_id: stack_id + }) + |> Repo.update() + end + end + + defp up(%ConsoleInstance{type: :dedicated, status: :stack_created, external_id: id} = inst) do + Enum.reduce_while(0..120, inst, fn _, inst -> + dedicated_console() + |> Console.stack(id) + |> case do + %{"status" => "SUCCESSFUL"} -> + {:ok, inst} = ConsoleInstance.changeset(inst, %{status: :provisioned}) + |> Repo.update() + {:halt, inst} + status -> + Logger.info "stack not ready yet, sleeping: #{inspect(status)}" + :timer.sleep(:timer.minutes(1)) + {:cont, inst} + end + end) + end + defp up(%ConsoleInstance{status: :deployment_created, url: url} = inst) do case {DNS.resolve(url), DNS.resolve(url, :cname)} do {{:ok, [_ | _]}, _} -> mark_provisioned(inst) @@ -97,6 +138,13 @@ defmodule Core.Services.Cloud.Workflow do end end + defp down(%ConsoleInstance{type: :dedicated, instance_status: %{stack: true}, external_id: id} = inst) do + with {:ok, _} <- Console.delete_stack(dedicated_console(), id) do + ConsoleInstance.changeset(inst, %{status: :stack_deleted}) + |> Repo.update() + end + end + defp down(%ConsoleInstance{instance_status: %{svc: false, db: true}, configuration: conf, postgres: pg} = inst) do with {:ok, pid} <- connect(pg), {:ok, _} <- Postgrex.query(pid, "DROP DATABASE IF EXISTS #{conf.database}", []), @@ -139,6 +187,18 @@ defmodule Core.Services.Cloud.Workflow do |> execute(extract: :inst) end + defp finalize(%ConsoleInstance{type: :dedicated} = inst, :down) do + start_transaction() + |> add_operation(:inst, fn _ -> Repo.delete(inst) end) + |> add_operation(:sa, fn %{inst: %{name: name}} -> + case Users.get_user_by_email("#{name}-cloud-sa@srv.plural.sh") do + %User{} = u -> Repo.delete(u) + _ -> {:ok, nil} + end + end) + |> execute(extract: :inst) + end + defp finalize(inst, _) do Logger.warn "failed to finalize console instance: #{inst.id}" {:ok, inst} @@ -172,4 +232,6 @@ defmodule Core.Services.Cloud.Workflow do end defp console(), do: Console.new(Core.conf(:console_url), Core.conf(:console_token)) + + defp dedicated_console(), do: Console.new(Core.conf(:console_url), Core.conf(:dedicated_console_token)) end diff --git a/apps/core/lib/core/services/payments.ex b/apps/core/lib/core/services/payments.ex index 01e60e257..204723d81 100644 --- a/apps/core/lib/core/services/payments.ex +++ b/apps/core/lib/core/services/payments.ex @@ -262,6 +262,23 @@ defmodule Core.Services.Payments do end def limited?(_, _), do: false + @doc """ + Determines if an account is on an enterprise plan + """ + @spec enterprise?(Account.t | User.t) :: boolean + def enterprise?(%Account{} = account) do + case Core.Repo.preload(account, [subscription: :plan]) do + %Account{subscription: %PlatformSubscription{plan: %PlatformPlan{enterprise: ent}}} -> ent + _ -> false + end + end + + def enterprise?(%User{} = user) do + preload(user) + |> Map.get(:account) + |> enterprise?() + end + @doc """ Determine's if a user's account has access to the given feature. Returns `true` if enforcement is not enabled yet. """ diff --git a/apps/core/priv/repo/migrations/20240917232002_add_dedicated_console.exs b/apps/core/priv/repo/migrations/20240917232002_add_dedicated_console.exs new file mode 100644 index 000000000..a38bebe80 --- /dev/null +++ b/apps/core/priv/repo/migrations/20240917232002_add_dedicated_console.exs @@ -0,0 +1,9 @@ +defmodule Core.Repo.Migrations.AddDedicatedConsole do + use Ecto.Migration + + def change do + alter table(:console_instances) do + add :type, :integer, default: 0 + end + end +end diff --git a/apps/core/test/services/cloud_test.exs b/apps/core/test/services/cloud_test.exs index 8393264bc..6c9fbfffb 100644 --- a/apps/core/test/services/cloud_test.exs +++ b/apps/core/test/services/cloud_test.exs @@ -39,6 +39,57 @@ defmodule Core.Services.CloudTest do assert_receive {:event, %PubSub.ConsoleInstanceCreated{item: ^instance}} end + test "enterprise accounts can create dedicated console instances" do + account = insert(:account) + enterprise_plan(account) + user = admin_user(account) + insert(:repository, name: "console") + + expect(HTTPoison, :post, fn _, _, _ -> + {:ok, %{status_code: 200, body: Jason.encode!(%{client_id: "123", client_secret: "secret"})}} + end) + + {:ok, instance} = Cloud.create_instance(%{ + type: :dedicated, + name: "plrltest", + cloud: :aws, + region: "us-east-1", + size: :small + }, user) + + assert instance.name == "plrltest" + assert instance.cloud == :aws + assert instance.region == "us-east-1" + assert instance.size == :small + refute instance.postgres_id + refute instance.cluster_id + + sa = Core.Services.Users.get_user_by_email("plrltest-cloud-sa@srv.plural.sh") + %{impersonation_policy: %{bindings: [binding]}} = Core.Repo.preload(sa, [impersonation_policy: :bindings]) + assert binding.user_id == user.id + + assert_receive {:event, %PubSub.ConsoleInstanceCreated{item: ^instance}} + end + + test "nonenterprise plans cannot create a dedicated cloud console instance" do + account = insert(:account) + enable_features(account, [:cd]) + user = admin_user(account) + insert(:cloud_cluster) + insert(:postgres_cluster) + insert(:repository, name: "console") + + {:error, err} = Cloud.create_instance(%{ + type: :dedicated, + name: "plrltest", + cloud: :aws, + region: "us-east-1", + size: :small + }, user) + + assert err =~ "enterprise" + end + test "unpaid users cannot create instances" do account = insert(:account) user = admin_user(account) diff --git a/apps/core/test/support/factory.ex b/apps/core/test/support/factory.ex index ce872362e..1ac413155 100644 --- a/apps/core/test/support/factory.ex +++ b/apps/core/test/support/factory.ex @@ -687,6 +687,10 @@ defmodule Core.Factory do insert(:platform_subscription, account: account, plan: build(:platform_plan, features: features)) end + def enterprise_plan(account) do + insert(:platform_subscription, account: account, plan: build(:platform_plan, enterprise: true)) + end + def bound_service_account(user, args \\ []) do sa = insert(:user, args ++ [service_account: true, account: user.account]) insert(:impersonation_policy_binding, diff --git a/apps/graphql/lib/graphql/schema/cloud.ex b/apps/graphql/lib/graphql/schema/cloud.ex index 6f49f4c21..9ae2661c1 100644 --- a/apps/graphql/lib/graphql/schema/cloud.ex +++ b/apps/graphql/lib/graphql/schema/cloud.ex @@ -6,8 +6,10 @@ defmodule GraphQl.Schema.Cloud do ecto_enum :cloud_provider, CloudCluster.Cloud ecto_enum :console_instance_status, ConsoleInstance.Status ecto_enum :console_size, ConsoleInstance.Size + ecto_enum :console_instance_type, ConsoleInstance.Type input_object :console_instance_attributes do + field :type, non_null(:console_instance_type), description: "the type of console instance" field :name, non_null(:string), description: "the name of this instance (globally unique)" field :size, non_null(:console_size), description: "a heuristic size of this instance" field :cloud, non_null(:cloud_provider), description: "the cloud provider to deploy to" @@ -25,6 +27,7 @@ defmodule GraphQl.Schema.Cloud do object :console_instance do field :id, non_null(:id) + field :type, non_null(:console_instance_type), description: "whether this is a shared or dedicated console" field :name, non_null(:string), description: "the name of this instance (globally unique)" field :subdomain, non_null(:string), description: "the subdomain this instance lives under" field :url, non_null(:string), description: "full console url of this instance" diff --git a/apps/graphql/test/mutations/cloud_mutations_test.exs b/apps/graphql/test/mutations/cloud_mutations_test.exs index ee718507a..2000861e0 100644 --- a/apps/graphql/test/mutations/cloud_mutations_test.exs +++ b/apps/graphql/test/mutations/cloud_mutations_test.exs @@ -27,6 +27,7 @@ defmodule GraphQl.CloudMutationsTest do } } """, %{"attrs" => %{ + "type" => "SHARED", "name" => "plrltest", "cloud" => "AWS", "size" => "SMALL", diff --git a/apps/graphql/test/mutations/user_mutation_test.exs b/apps/graphql/test/mutations/user_mutation_test.exs index 13a85faf9..66f0dcb43 100644 --- a/apps/graphql/test/mutations/user_mutation_test.exs +++ b/apps/graphql/test/mutations/user_mutation_test.exs @@ -50,7 +50,7 @@ defmodule GraphQl.UserMutationTest do end test "it will fail on invalid captchas" do - {:ok, user} = Users.create_user(%{ + {:ok, _} = Users.create_user(%{ name: "Michael Guarino", email: "mjg@plural.sh", password: "super strong password" diff --git a/config/config.exs b/config/config.exs index 2de4589a6..d8dc3b7a2 100644 --- a/config/config.exs +++ b/config/config.exs @@ -143,6 +143,9 @@ config :core, trial_plan: "Pro Trial", console_token: "bogus", console_url: "https://console.example.com", + dedicated_console_token: "bogus", + mgmt_cluster: "dummy", + dedicated_project: "dedicated", sysbox_emails: [], mgmt_repo: "https://github.com/pluralsh/plural.git", cockroach_parameters: [], diff --git a/rel/config/config.exs b/rel/config/config.exs index 591ce374a..65aaa07c1 100644 --- a/rel/config/config.exs +++ b/rel/config/config.exs @@ -92,6 +92,9 @@ config :core, stripe_webhook_secret: get_env("STRIPE_WEBHOOK_SECRET"), github_demo_token: get_env("GITHUB_DEMO_TOKEN"), console_token: get_env("CONSOLE_SA_TOKEN"), + dedicated_console_token: get_env("CONSOLE_DEDICATED_SA_TOKEN"), + dedicated_project: get_env("CONSOLE_DEDICATED_PROJECT"), + mgmt_cluster: get_env("MGMT_CLUSTER_ID"), console_url: get_env("CONSOLE_URL"), mgmt_repo: get_env("CONSOLE_MGMT_REPO"), stack_id: get_env("CONSOLE_CLOUD_STACK_ID"), diff --git a/schema/schema.graphql b/schema/schema.graphql index d5239d105..06a0a8f2b 100644 --- a/schema/schema.graphql +++ b/schema/schema.graphql @@ -564,6 +564,8 @@ enum ConsoleInstanceStatus { PROVISIONED DEPLOYMENT_DELETED DATABASE_DELETED + STACK_CREATED + STACK_DELETED } enum ConsoleSize { @@ -572,7 +574,15 @@ enum ConsoleSize { LARGE } +enum ConsoleInstanceType { + SHARED + DEDICATED +} + input ConsoleInstanceAttributes { + "the type of console instance" + type: ConsoleInstanceType! + "the name of this instance (globally unique)" name: String! @@ -598,6 +608,9 @@ input ConsoleConfigurationUpdateAttributes { type ConsoleInstance { id: ID! + "whether this is a shared or dedicated console" + type: ConsoleInstanceType! + "the name of this instance (globally unique)" name: String! diff --git a/www/src/components/create-cluster/steps/ConfigureCloudInstanceStep.tsx b/www/src/components/create-cluster/steps/ConfigureCloudInstanceStep.tsx index 33e04ba77..285dcc48d 100644 --- a/www/src/components/create-cluster/steps/ConfigureCloudInstanceStep.tsx +++ b/www/src/components/create-cluster/steps/ConfigureCloudInstanceStep.tsx @@ -9,6 +9,7 @@ import { } from '@pluralsh/design-system' import { CloudProvider, + ConsoleInstanceType, ConsoleSize, useCreateConsoleInstanceMutation, } from 'generated/graphql' @@ -48,6 +49,7 @@ export function ConfigureCloudInstanceStep() { size, cloud, region, + type: ConsoleInstanceType.Shared, }, }, onCompleted: (data) => { diff --git a/www/src/generated/graphql.ts b/www/src/generated/graphql.ts index 6efc433a9..c84619a57 100644 --- a/www/src/generated/graphql.ts +++ b/www/src/generated/graphql.ts @@ -584,6 +584,8 @@ export type ConsoleInstance = { status: ConsoleInstanceStatus; /** the subdomain this instance lives under */ subdomain: Scalars['String']['output']; + /** whether this is a shared or dedicated console */ + type: ConsoleInstanceType; updatedAt?: Maybe; /** full console url of this instance */ url: Scalars['String']['output']; @@ -598,6 +600,8 @@ export type ConsoleInstanceAttributes = { region: Scalars['String']['input']; /** a heuristic size of this instance */ size: ConsoleSize; + /** the type of console instance */ + type: ConsoleInstanceType; }; export type ConsoleInstanceConnection = { @@ -618,7 +622,14 @@ export enum ConsoleInstanceStatus { DeploymentCreated = 'DEPLOYMENT_CREATED', DeploymentDeleted = 'DEPLOYMENT_DELETED', Pending = 'PENDING', - Provisioned = 'PROVISIONED' + Provisioned = 'PROVISIONED', + StackCreated = 'STACK_CREATED', + StackDeleted = 'STACK_DELETED' +} + +export enum ConsoleInstanceType { + Dedicated = 'DEDICATED', + Shared = 'SHARED' } export type ConsoleInstanceUpdateAttributes = {