diff --git a/assets/src/generated/graphql.ts b/assets/src/generated/graphql.ts index e26306b39..1a77bf26c 100644 --- a/assets/src/generated/graphql.ts +++ b/assets/src/generated/graphql.ts @@ -8804,6 +8804,7 @@ export type ServiceUpdateAttributes = { dryRun?: InputMaybe; git?: InputMaybe; helm?: InputMaybe; + imports?: InputMaybe>>; interval?: InputMaybe; kustomize?: InputMaybe; parentId?: InputMaybe; diff --git a/go/client/models_gen.go b/go/client/models_gen.go index 551bb80cb..3a380d996 100644 --- a/go/client/models_gen.go +++ b/go/client/models_gen.go @@ -5261,6 +5261,7 @@ type ServiceUpdateAttributes struct { ReadBindings []*PolicyBindingAttributes `json:"readBindings,omitempty"` WriteBindings []*PolicyBindingAttributes `json:"writeBindings,omitempty"` ContextBindings []*ContextBindingAttributes `json:"contextBindings,omitempty"` + Imports []*ServiceImportAttributes `json:"imports,omitempty"` } type ServiceVuln struct { diff --git a/go/controller/internal/controller/servicedeployment_controller.go b/go/controller/internal/controller/servicedeployment_controller.go index 4da364d95..79a737af3 100644 --- a/go/controller/internal/controller/servicedeployment_controller.go +++ b/go/controller/internal/controller/servicedeployment_controller.go @@ -182,6 +182,7 @@ func (r *ServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ SyncConfig: attr.SyncConfig, Dependencies: attr.Dependencies, ParentID: attr.ParentID, + Imports: attr.Imports, } sha, err := utils.HashObject(updater) diff --git a/lib/console/cost/ingester.ex b/lib/console/cost/ingester.ex index efc23a08c..b821a1215 100644 --- a/lib/console/cost/ingester.ex +++ b/lib/console/cost/ingester.ex @@ -1,15 +1,18 @@ defmodule Console.Cost.Ingester do alias Console.Repo import Console.Services.Base - import Console.Cost.Utils, only: [batch_insert: 3] + import Console.Cost.Utils, only: [batch_insert: 2] alias Console.Schema.{Cluster, ClusterUsage, ClusterNamespaceUsage, ClusterScalingRecommendation} def ingest(attrs, %Cluster{id: id}) do start_transaction() - |> add_operation(:wipe_cluster, fn _ -> - ClusterUsage.for_cluster(id) - |> Repo.delete_all() - |> ok() + |> add_operation(:cluster, fn _ -> + case Repo.get_by(ClusterUsage, cluster_id: id) do + %ClusterUsage{} = usage -> usage + nil -> %ClusterUsage{cluster_id: id} + end + |> ClusterUsage.changeset(attrs[:cluster]) + |> Repo.insert_or_update() end) |> add_operation(:wipe_namespace, fn _ -> ClusterNamespaceUsage.for_cluster(id) @@ -21,22 +24,17 @@ defmodule Console.Cost.Ingester do |> Repo.delete_all() |> ok() end) - |> add_operation(:cluster, fn _ -> - %ClusterUsage{id: id} - |> ClusterUsage.changeset(attrs[:cluster]) - |> Repo.insert() - end) |> add_operation(:namespace, fn _ -> Map.get(attrs, :namespaces, []) - |> Stream.map(×tamped/1) + |> Stream.map(&cluster_timestamped(&1, id)) |> Stream.map(&Map.drop(&1, ~w(gpu_util)a)) - |> batch_insert(ClusterNamespaceUsage, repo: Repo) + |> batch_insert(ClusterNamespaceUsage) |> ok() end) |> add_operation(:scaling, fn _ -> Map.get(attrs, :recommendations, []) - |> Stream.map(×tamped/1) - |> batch_insert(ClusterScalingRecommendation, repo: Repo) + |> Stream.map(&cluster_timestamped(&1, id)) + |> batch_insert(ClusterScalingRecommendation) |> ok() end) |> execute() @@ -45,4 +43,9 @@ defmodule Console.Cost.Ingester do err -> err end end + + defp cluster_timestamped(map, cluster_id) do + timestamped(map) + |> Map.put(:cluster_id, cluster_id) + end end diff --git a/lib/console/deployments/pr/validation.ex b/lib/console/deployments/pr/validation.ex index 6036e3865..00f624c57 100644 --- a/lib/console/deployments/pr/validation.ex +++ b/lib/console/deployments/pr/validation.ex @@ -16,10 +16,10 @@ defmodule Console.Deployments.Pr.Validation do defp do_validate(%Configuration{type: :int}, val) when is_integer(val), do: :ok defp do_validate(%Configuration{type: :bool}, val) when is_boolean(val), do: :ok - defp do_validate(%Configuration{type: :enum, values: vals}, val) do + defp do_validate(%Configuration{type: :enum, name: n, values: vals}, val) do case val in vals do true -> :ok - false -> {:error, "#{inspect(val)} is not a member of {#{Enum.join(vals, ",")}}"} + false -> {:error, ~s(field "#{n}" with value "#{inspect(val)}" is not a member of {#{Enum.join(vals, ",")}})} end end @@ -32,30 +32,30 @@ defmodule Console.Deployments.Pr.Validation do ) when is_binary(val) do query = scope_query(scope) case Repo.get_by(query, name: val) do - %^query{} -> {:error, "there is already a #{scope} with name #{val}"} + %^query{} -> {:error, ~s(there is already a #{scope} with name #{val})} _ -> do_validate(put_in(conf.validation.uniq_by, nil), val) end end - defp do_validate(%Configuration{type: :string, validation: %Validation{json: true}}, val) when is_binary(val) do + defp do_validate(%Configuration{type: :string, name: n, validation: %Validation{json: true}}, val) when is_binary(val) do case Jason.decode(val) do {:ok, _} -> :ok - _ -> {:error, "value #{val} is not a json-encoded string"} + _ -> {:error, ~s(field "#{n}" with value "#{val}" is not a json-encoded string)} end end - defp do_validate(%Configuration{type: :string, validation: %Validation{regex: r}}, val) + defp do_validate(%Configuration{type: :string, name: n, validation: %Validation{regex: r}}, val) when is_binary(r) and is_binary(val) do case String.match?(val, ~r/#{r}/) do true -> :ok - false -> {:error, "value #{val} does not match regex #{r}"} + false -> {:error, ~s(field "#{n}" with value "#{val}" does not match regex #{r})} end end defp do_validate(%Configuration{type: :string}, val) when is_binary(val) and byte_size(val) > 0, do: :ok - defp do_validate(%Configuration{type: t}, val), - do: {:error, "value #{inspect(val)} does not match type #{String.upcase(to_string(t))}"} + defp do_validate(%Configuration{type: t, name: n}, val), + do: {:error, ~s(field "#{n}" with value "#{inspect(val)}" does not match type #{String.upcase(to_string(t))})} defp scope_query(:project), do: Project defp scope_query(:cluster), do: Cluster diff --git a/lib/console/deployments/services.ex b/lib/console/deployments/services.ex index 0ee5ae4bb..b0d046e6b 100644 --- a/lib/console/deployments/services.ex +++ b/lib/console/deployments/services.ex @@ -475,7 +475,7 @@ defmodule Console.Deployments.Services do def update_service(attrs, %Service{} = svc) do start_transaction() |> add_operation(:base, fn _ -> - svc = Repo.preload(svc, [:context_bindings, :dependencies, :read_bindings, :write_bindings]) + svc = Repo.preload(svc, [:context_bindings, :dependencies, :read_bindings, :write_bindings, :imports]) attrs = Map.put(attrs, :status, :stale) svc |> Service.changeset(stabilize_deps(attrs, svc)) diff --git a/lib/console/graphql/deployments/service.ex b/lib/console/graphql/deployments/service.ex index 91bb243cb..24f4131ea 100644 --- a/lib/console/graphql/deployments/service.ex +++ b/lib/console/graphql/deployments/service.ex @@ -80,6 +80,7 @@ defmodule Console.GraphQl.Deployments.Service do field :read_bindings, list_of(:policy_binding_attributes) field :write_bindings, list_of(:policy_binding_attributes) field :context_bindings, list_of(:context_binding_attributes) + field :imports, list_of(:service_import_attributes) end input_object :service_clone_attributes do diff --git a/schema/schema.graphql b/schema/schema.graphql index 16bf473b4..10e07c0e6 100644 --- a/schema/schema.graphql +++ b/schema/schema.graphql @@ -3818,6 +3818,8 @@ input ServiceUpdateAttributes { writeBindings: [PolicyBindingAttributes] contextBindings: [ContextBindingAttributes] + + imports: [ServiceImportAttributes] } input ServiceCloneAttributes { diff --git a/test/console/graphql/mutations/deployments/cluster_mutations_test.exs b/test/console/graphql/mutations/deployments/cluster_mutations_test.exs index aedae9445..a32978989 100644 --- a/test/console/graphql/mutations/deployments/cluster_mutations_test.exs +++ b/test/console/graphql/mutations/deployments/cluster_mutations_test.exs @@ -500,9 +500,20 @@ defmodule Console.GraphQl.Deployments.ClusterMutationsTest do } """, %{"costs" => ingest}, %{cluster: cluster}) - assert Console.Repo.aggregate(Console.Schema.ClusterUsage, :count, :id) == 1 - assert Console.Repo.aggregate(Console.Schema.ClusterNamespaceUsage, :count, :id) == 1 - assert Console.Repo.aggregate(Console.Schema.ClusterScalingRecommendation, :count, :id) == 1 + {:ok, %{data: %{"ingestClusterCost" => true}}} = run_query(""" + mutation Ingest($costs: CostIngestAttributes!) { + ingestClusterCost(costs: $costs) + } + """, %{"costs" => ingest}, %{cluster: cluster}) + + [usage] = Console.Repo.all(Console.Schema.ClusterUsage) + assert usage.cluster_id == cluster.id + + [ns] = Console.Repo.all(Console.Schema.ClusterNamespaceUsage) + assert ns.cluster_id == cluster.id + + [sr] = Console.Repo.all(Console.Schema.ClusterScalingRecommendation) + assert sr.cluster_id == cluster.id end end end diff --git a/test/console/graphql/queries/deployments/cluster_queries_test.exs b/test/console/graphql/queries/deployments/cluster_queries_test.exs index 80fe5b5f1..daec6435a 100644 --- a/test/console/graphql/queries/deployments/cluster_queries_test.exs +++ b/test/console/graphql/queries/deployments/cluster_queries_test.exs @@ -405,16 +405,13 @@ defmodule Console.GraphQl.Deployments.ClusterQueriesTest do query Runtime($id: ID!) { runtimeService(id: $id) { id - addon { - versions { version kube } - readme - } + addon { versions { version kube } } } } """, %{"id" => runtime.id}, %{current_user: user}) assert rs["id"] == runtime.id - assert rs["addon"]["readme"] + # assert rs["addon"]["readme"] end test "users w/o cluster read cannot fetch a runtime service by id" do @@ -428,7 +425,6 @@ defmodule Console.GraphQl.Deployments.ClusterQueriesTest do id addon { versions { version kube } - readme } } }