diff --git a/charts/console-backend-rbac/templates/rbac.yaml b/charts/console-backend-rbac/templates/rbac.yaml index 715aa0e95..b312dd02c 100644 --- a/charts/console-backend-rbac/templates/rbac.yaml +++ b/charts/console-backend-rbac/templates/rbac.yaml @@ -119,6 +119,28 @@ rules: - update - patch - delete + - apiGroups: + - "nais.io" + resources: + - postgresaccesses + verbs: + - get + - create + - apiGroups: + - "nais.io" + resources: + - postgresbranches + verbs: + - get + - list + - watch + - delete + - apiGroups: + - postgresql.cnpg.io + resources: + - clusters + verbs: + - get - apiGroups: - "aiven.nais.io" resources: @@ -265,14 +287,43 @@ rules: - get - list - watch + - apiGroups: + - "nais.io" + resources: + - postgres + verbs: + - get + - list + - watch + # Legacy CLI port-forward grants still validate the old Zalando cluster. - apiGroups: - "data.nais.io" resources: - postgres verbs: - get + - apiGroups: + - "nais.io" + resources: + - postgresaccesses + verbs: + - get + - create + - apiGroups: + - "nais.io" + resources: + - postgresbranches + verbs: + - get - list - watch + - delete + - apiGroups: + - postgresql.cnpg.io + resources: + - clusters + verbs: + - get - apiGroups: - "" resources: diff --git a/data/k8s/dev/devteam/postgres.yaml b/data/k8s/dev/devteam/postgres.yaml index 20069a296..7a8df5dbb 100644 --- a/data/k8s/dev/devteam/postgres.yaml +++ b/data/k8s/dev/devteam/postgres.yaml @@ -1,5 +1,16 @@ -apiVersion: data.nais.io/v1 +--- +apiVersion: nais.io/v1 kind: Postgres +metadata: + name: postgres-1 + namespace: devteam +spec: + majorVersion: "18" +status: + activeBranch: postgres-1 +--- +apiVersion: nais.io/v1 +kind: PostgresBranch metadata: name: postgres-1 namespace: devteam @@ -7,10 +18,12 @@ metadata: key: value team: devteam spec: - cluster: - allowDeletion: false - majorVersion: "18" - resources: - diskSize: "2Gi" - cpu: "100m" - memory: "1Gi" + postgres: postgres-1 +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" diff --git a/go.mod b/go.mod index a64d648b3..008b2e66a 100644 --- a/go.mod +++ b/go.mod @@ -40,23 +40,23 @@ require ( github.com/lestrrat-go/jwx/v3 v3.0.1 github.com/nais/api/pkg/apiclient v0.0.0-20250219111538-2b76a0fd6ed9 github.com/nais/bifrost v0.0.0-20260106105449-911627ac2c61 - github.com/nais/liberator v0.0.0-20260216142648-ee49a9372bc4 - github.com/nais/pgrator/pkg/api v0.0.0-20260915202302-d699a2823ec5 + github.com/nais/liberator v0.0.0-20260903194126-706ea87ddf9a + github.com/nais/pgrator/pkg/api v0.0.0-20261001051319-3ab0adfbd6ce github.com/nais/tester v0.2.0 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe github.com/nais/v13s/pkg/api v0.0.0-20260826091953-1b518b13ca28 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pressly/goose/v3 v3.27.0 - github.com/prometheus/client_golang v1.23.2 - github.com/prometheus/common v0.67.5 + github.com/prometheus/client_golang v1.24.0 + github.com/prometheus/common v0.70.0 github.com/prometheus/prometheus v0.312.0 github.com/ravilushqa/otelgqlgen v0.19.0 github.com/robfig/cron/v3 v3.0.1 github.com/rs/cors v1.11.1 github.com/sethvargo/go-envconfig v1.3.0 - github.com/sirupsen/logrus v1.9.4 + github.com/sirupsen/logrus v1.10.2 github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 - github.com/stretchr/testify v1.11.1 + github.com/stretchr/testify v1.12.1 github.com/testcontainers/testcontainers-go v0.35.0 github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 github.com/vektah/gqlparser/v2 v2.5.33 @@ -64,7 +64,7 @@ require ( github.com/yuin/gopher-lua v1.1.2 github.com/zitadel/oidc/v3 v3.33.1 github.com/zitadel/zitadel-go/v3 v3.4.3 - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 go.opentelemetry.io/otel v1.44.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 @@ -74,7 +74,7 @@ require ( go.opentelemetry.io/otel/sdk v1.44.0 go.opentelemetry.io/otel/sdk/metric v1.44.0 go.opentelemetry.io/otel/trace v1.44.0 - golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa + golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.22.0 golang.org/x/text v0.41.0 @@ -83,9 +83,9 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7 google.golang.org/grpc v1.83.1 google.golang.org/protobuf v1.36.12 - k8s.io/api v0.36.0 - k8s.io/apimachinery v0.36.4 - k8s.io/client-go v0.36.0 + k8s.io/api v0.37.0 + k8s.io/apimachinery v0.37.0 + k8s.io/client-go v0.37.0 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 sigs.k8s.io/yaml v1.6.0 @@ -189,37 +189,38 @@ require ( github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect github.com/evanphx/json-patch v5.9.0+incompatible // indirect github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect - github.com/fatih/color v1.18.0 // indirect + github.com/fatih/color v1.19.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect - github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.2 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.6.1 // indirect - github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/analysis v0.25.0 // indirect github.com/go-openapi/errors v0.22.7 // indirect - github.com/go-openapi/jsonpointer v0.23.1 // indirect - github.com/go-openapi/jsonreference v0.21.5 // indirect + github.com/go-openapi/jsonpointer v1.0.0 // indirect + github.com/go-openapi/jsonreference v1.0.0 // indirect github.com/go-openapi/loads v0.23.3 // indirect github.com/go-openapi/spec v0.22.4 // indirect github.com/go-openapi/strfmt v0.26.2 // indirect - github.com/go-openapi/swag v0.25.5 // indirect - github.com/go-openapi/swag/cmdutils v0.25.5 // indirect - github.com/go-openapi/swag/conv v0.25.5 // indirect - github.com/go-openapi/swag/fileutils v0.25.5 // indirect + github.com/go-openapi/swag v0.27.1 // indirect + github.com/go-openapi/swag/cmdutils v0.27.1 // indirect + github.com/go-openapi/swag/conv v0.27.1 // indirect + github.com/go-openapi/swag/fileutils v0.27.1 // indirect github.com/go-openapi/swag/jsonname v0.26.0 // indirect - github.com/go-openapi/swag/jsonutils v0.25.5 // indirect - github.com/go-openapi/swag/loading v0.25.5 // indirect - github.com/go-openapi/swag/mangling v0.25.5 // indirect - github.com/go-openapi/swag/netutils v0.25.5 // indirect - github.com/go-openapi/swag/stringutils v0.25.5 // indirect - github.com/go-openapi/swag/typeutils v0.25.5 // indirect - github.com/go-openapi/swag/yamlutils v0.25.5 // indirect + github.com/go-openapi/swag/jsonutils v0.27.1 // indirect + github.com/go-openapi/swag/loading v0.27.1 // indirect + github.com/go-openapi/swag/mangling v0.27.1 // indirect + github.com/go-openapi/swag/netutils v0.27.1 // indirect + github.com/go-openapi/swag/pools v0.27.1 // indirect + github.com/go-openapi/swag/stringutils v0.27.1 // indirect + github.com/go-openapi/swag/typeutils v0.27.1 // indirect + github.com/go-openapi/swag/yamlutils v0.27.1 // indirect github.com/go-openapi/validate v0.25.2 // indirect github.com/go-sql-driver/mysql v1.9.3 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect @@ -235,7 +236,7 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.28.0 // indirect + github.com/google/cel-go v0.29.2 // indirect github.com/google/flatbuffers v25.12.19+incompatible // indirect github.com/google/gnostic-models v0.7.1 // indirect github.com/google/s2a-go v0.1.9 // indirect @@ -270,7 +271,6 @@ require ( github.com/hashicorp/memberlist v0.5.4 // indirect github.com/hashicorp/serf v0.10.2 // indirect github.com/huandu/xstrings v1.5.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/influxdata/tdigest v0.0.2-0.20210216194612-fc98d27c9e8b // indirect github.com/invopop/jsonschema v0.13.0 // indirect @@ -285,7 +285,7 @@ require ( github.com/julienschmidt/httprouter v1.3.0 // indirect github.com/kamstrup/intmap v0.5.2 // indirect github.com/kelseyhightower/envconfig v1.4.0 // indirect - github.com/klauspost/compress v1.18.6 // indirect + github.com/klauspost/compress v1.19.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/knadh/koanf/maps v0.1.2 // indirect github.com/knadh/koanf/parsers/yaml v1.1.0 // indirect @@ -359,7 +359,7 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/exporter-toolkit v0.16.0 // indirect github.com/prometheus/otlptranslator v1.0.0 // indirect - github.com/prometheus/procfs v0.19.2 // indirect + github.com/prometheus/procfs v0.21.1 // indirect github.com/prometheus/sigv4 v0.4.1 // indirect github.com/puzpuzpuz/xsync/v4 v4.5.0 // indirect github.com/redis/go-redis/v9 v9.18.0 // indirect @@ -407,9 +407,9 @@ require ( github.com/zitadel/logging v0.6.1 // indirect github.com/zitadel/schema v1.3.0 // indirect go.etcd.io/bbolt v1.5.0 // indirect - go.etcd.io/etcd/api/v3 v3.6.8 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.6.8 // indirect - go.etcd.io/etcd/client/v3 v3.6.8 // indirect + go.etcd.io/etcd/api/v3 v3.7.0 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.7.0 // indirect + go.etcd.io/etcd/client/v3 v3.7.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/collector/component v1.59.0 // indirect @@ -444,7 +444,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect go.yaml.in/yaml/v4 v4.0.0-rc.4 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/crypto v0.54.0 // indirect @@ -467,13 +467,13 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.7.0 // indirect - k8s.io/apiextensions-apiserver v0.36.0 // indirect - k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect + k8s.io/apiextensions-apiserver v0.37.0 // indirect + k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad // indirect mvdan.cc/gofumpt v0.9.2 // indirect - sigs.k8s.io/controller-runtime v0.24.1 // indirect + sigs.k8s.io/controller-runtime v0.25.1 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.3 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.4.2 // indirect ) replace github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp => ./mockgcp @@ -483,3 +483,10 @@ replace github.com/hashicorp/terraform-provider-google-beta => ./third_party/git replace github.com/nais/api/pkg/apiclient => ./pkg/apiclient replace github.com/hashicorp/memberlist => github.com/grafana/memberlist v0.3.1-0.20251126142931-6f9f62ab6f86 + +// Loki's dskit dependency is not compatible with the etcd 3.7 API pulled in by pgrator's Kubernetes dependencies. +replace go.etcd.io/etcd/api/v3 => go.etcd.io/etcd/api/v3 v3.6.14 + +replace go.etcd.io/etcd/client/pkg/v3 => go.etcd.io/etcd/client/pkg/v3 v3.6.14 + +replace go.etcd.io/etcd/client/v3 => go.etcd.io/etcd/client/v3 v3.6.14 diff --git a/go.sum b/go.sum index b280fb940..39b6b1641 100644 --- a/go.sum +++ b/go.sum @@ -341,8 +341,8 @@ github.com/exaring/otelpgx v0.9.0/go.mod h1:ANkRZDfgfmN6yJS1xKMkshbnsHO8at5sYwtV github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:IT4JYU7k4ikYg1SCxNI1/Tieq/NFvh6dzLdgi7eu0tM= github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= @@ -354,8 +354,8 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= -github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= -github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78= +github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4= github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= @@ -372,8 +372,8 @@ github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkv github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= +github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= @@ -385,46 +385,48 @@ github.com/go-openapi/analysis v0.25.0 h1:EnjAq1yO8wEO9HbPmY8vLPEIkdZuuFhCAKBPvC github.com/go-openapi/analysis v0.25.0/go.mod h1:5WFTRE43WLkPG9r9OtlMfqkkvUTYLVVCIxLlEpyF8kE= github.com/go-openapi/errors v0.22.7 h1:JLFBGC0Apwdzw3484MmBqspjPbwa2SHvpDm0u5aGhUA= github.com/go-openapi/errors v0.22.7/go.mod h1://QW6SD9OsWtH6gHllUCddOXDL0tk0ZGNYHwsw4sW3w= -github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4= -github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY= -github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= -github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= +github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxgL3h1H8s= +github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y= +github.com/go-openapi/jsonreference v1.0.0 h1:jlmTr6torcd1YgDQvSfNmRtKzYDO4FGBkrAdlAVWnpY= +github.com/go-openapi/jsonreference v1.0.0/go.mod h1:jtwdyGbJk0Xhe5Y+rwtglQP6Sb1WZST4rT32LWB+sv0= github.com/go-openapi/loads v0.23.3 h1:g5Xap1JfwKkUnZdn+S0L3SzBDpcTIYzZ5Qaag0YDkKQ= github.com/go-openapi/loads v0.23.3/go.mod h1:NOH07zLajXo8y55hom0omlHWDVVvCwBM/S+csCK8LqA= github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ= github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ= github.com/go-openapi/strfmt v0.26.2 h1:ysjheCh4i1rmFEo2LanhELDNucNzfWTZhUDKgWWPaFM= github.com/go-openapi/strfmt v0.26.2/go.mod h1:fXh1e449cyUn2NYuz+wb3wARBUdMl7qPEZwX00nqivY= -github.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU= -github.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA= -github.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c= -github.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= -github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g= -github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k= -github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk= -github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc= +github.com/go-openapi/swag v0.27.1 h1:VotvOLWW8q/EAxB0YdsBBGC8XYyeL1YwBj2ungAGPNg= +github.com/go-openapi/swag v0.27.1/go.mod h1:GTkJPwHfhJp6MWr4/rCh64HVI3Ofu+tcsbfjfHmTxpE= +github.com/go-openapi/swag/cmdutils v0.27.1 h1:I7sYqaWVl5mq0NEmNQkAmFDyNin9ufvMX/p2zwtQaOE= +github.com/go-openapi/swag/cmdutils v0.27.1/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= +github.com/go-openapi/swag/conv v0.27.1 h1:8wi9ZG+olmY1wXphl93EWniPtbSPkXM/feH7FgjsvrU= +github.com/go-openapi/swag/conv v0.27.1/go.mod h1:QbqMivkpKhC3g1B1GGGOJ6ANewI3S62dbzYu3Duowqs= +github.com/go-openapi/swag/fileutils v0.27.1 h1:QQqBSoi5mW4XpU85nS0mLcA+zAE6vLzrb0QkmLKf9oM= +github.com/go-openapi/swag/fileutils v0.27.1/go.mod h1:VvJFZLTZS0AI854gEQz5tk7dBESdLjiNUMSZ/th2ry8= github.com/go-openapi/swag/jsonname v0.26.0 h1:gV1NFX9M8avo0YSpmWogqfQISigCmpaiNci8cGECU5w= github.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M= -github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo= -github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo= -github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU= -github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g= -github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw= -github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY= -github.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyOa7ZGfxDKlU= -github.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14= -github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M= -github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII= -github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E= -github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc= -github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ= -github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.1 h1:NZOrZmIb6PTv5LTFxr5/mKV/FjbUzGE7E6gLz7vFoOQ= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.1/go.mod h1:r7dwsujEHawapMsxA69i+XMGZrQ5tRauhLAjV/sxg3Q= -github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4= -github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= +github.com/go-openapi/swag/jsonutils v0.27.1 h1:SVgK3i4USzCU5mibOOS/l4ea2h9UQXy7J7RNLTjuXjU= +github.com/go-openapi/swag/jsonutils v0.27.1/go.mod h1:tdlEpZqdcQ17uj6J4YdK9vd8It5qWMwjWXOs0tjpRlk= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.1 h1:mJu3COL9WEaZVp/Kf2PRMi7tPszPEJfSr/OO75ynCs8= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.1/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY= +github.com/go-openapi/swag/loading v0.27.1 h1:/DxUgDXKbBX4bcn7r9uEXfJyzN5XpiJmZplzQTjrRCY= +github.com/go-openapi/swag/loading v0.27.1/go.mod h1:jvGh3iA2+zyUUycB5fgJWzeHnhrpvGnJJM0RVE9ZShE= +github.com/go-openapi/swag/mangling v0.27.1 h1:yC9D0HyUE8gbP+BfmGx9+AA89ikwZTMjESK3OnnoaqA= +github.com/go-openapi/swag/mangling v0.27.1/go.mod h1:jtBE2+V+3pILxOR7Vgce+Cwp6A2PgZbvVqfNntbVs0w= +github.com/go-openapi/swag/netutils v0.27.1 h1:mICMFoS82F5TZ4Zy3cqmcQk+BFeCp3Uyq3Np7GI0/qU= +github.com/go-openapi/swag/netutils v0.27.1/go.mod h1:J+WYyFMLtvtCGqa6jLv+YNUmIKI3ZRQRrvfNDMoQoEQ= +github.com/go-openapi/swag/pools v0.27.1 h1:9LeadcMyb2GJCbXX5hVQDbZ2Lq9TL4dCs/nx1j5DO0E= +github.com/go-openapi/swag/pools v0.27.1/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE= +github.com/go-openapi/swag/stringutils v0.27.1 h1:ZXePZ0r2p1qSjo8tD3Un4vFj8+FqlCkczxDrJIhYUp8= +github.com/go-openapi/swag/stringutils v0.27.1/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM= +github.com/go-openapi/swag/typeutils v0.27.1 h1:KSTdFlfnse4r6dP9IrEnwMldjE+zs71UeEB3//PtVXc= +github.com/go-openapi/swag/typeutils v0.27.1/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ= +github.com/go-openapi/swag/yamlutils v0.27.1 h1:ftxv6xvXb1E3zohUc+okZ9nSqNb9StQX/FXnKZ98sQA= +github.com/go-openapi/swag/yamlutils v0.27.1/go.mod h1:bnxFIB1qewGRiZHypXGZ3fNgf13/0HfRgnS/iZBDrOo= +github.com/go-openapi/testify/enable/yaml/v2 v2.6.0 h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0= +github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo= +github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug= +github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= github.com/go-openapi/validate v0.25.2 h1:12NsfLAwGegqbGWr2CnvT65X/Q2USJipmJ9b7xDJZz0= github.com/go-openapi/validate v0.25.2/go.mod h1:Pgl1LpPPGFnZ+ys4/hTlDiRYQdI1ocKypgE+8Q8BLfY= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= @@ -485,8 +487,8 @@ github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.28.0 h1:KjSWstCpz/MN5t4a8gnGJNIYUsJRpdi/r97xWDphIQc= -github.com/google/cel-go v0.28.0/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= +github.com/google/cel-go v0.29.2 h1:ZtDxkeiMmz0mxbKDYiNkE5Lk7V5edMRcaaDf2jX002k= +github.com/google/cel-go v0.29.2/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8= github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs= github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= @@ -607,8 +609,6 @@ github.com/hetznercloud/hcloud-go/v2 v2.41.2/go.mod h1:9OGvC//jbHE4sv2Oyo0bQ2vEW github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/tdigest v0.0.2-0.20210216194612-fc98d27c9e8b h1:i44CesU68ZBRvtCjBi3QSosCIKrjmMbYlQMFAwVLds4= @@ -674,8 +674,8 @@ github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXw github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= -github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ= +github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo= @@ -805,10 +805,10 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nais/bifrost v0.0.0-20260106105449-911627ac2c61 h1:DMIjq7U47OJ8GlgOR3Z6kMzIWm7f+r8jzYbyE1oYCrg= github.com/nais/bifrost v0.0.0-20260106105449-911627ac2c61/go.mod h1:sAeomjrnGAI9VAErCaOHbTehVkf6hhKoJpHL8uzOqGg= -github.com/nais/liberator v0.0.0-20260216142648-ee49a9372bc4 h1:i7jBukqLtNpQIBhy/YBA8XjLRVdI8B7WxC9nhQyxlWE= -github.com/nais/liberator v0.0.0-20260216142648-ee49a9372bc4/go.mod h1:jmMoQtUMhvv7j1C2gz89Gxc4hxc73GXtTR3mEXn7cvU= -github.com/nais/pgrator/pkg/api v0.0.0-20260915202302-d699a2823ec5 h1:7VBS6QIP1BTG3i9OLW1J1ekbd+yh6XIGfmAlM8BSQFU= -github.com/nais/pgrator/pkg/api v0.0.0-20260915202302-d699a2823ec5/go.mod h1:kjcHI6Uh4++6CEsQf8JeGKE37XrY+ffhoaQv3jn0qAc= +github.com/nais/liberator v0.0.0-20260903194126-706ea87ddf9a h1:GAIHGbZVhT5Yzx/NrYWdLxlsn+IaqXetGm4zsnJW5hU= +github.com/nais/liberator v0.0.0-20260903194126-706ea87ddf9a/go.mod h1:11Mi+k5w8IcdmgNwk6gORoBhHR9Ua4I9UtI8uljg4kc= +github.com/nais/pgrator/pkg/api v0.0.0-20261001051319-3ab0adfbd6ce h1:I2y/mQFkvJboKMsBNHnqYDVPVbQqTAEVdvvDjCdOQu8= +github.com/nais/pgrator/pkg/api v0.0.0-20261001051319-3ab0adfbd6ce/go.mod h1:jwS3ovEbOWcVTYyM2EicvkUSjtOrYA57t+jcTct2uzM= github.com/nais/tester v0.2.0 h1:lcTkDP52ddw9l5s3KC4snUP+GlUpZMUtJ3XR2vF4G0w= github.com/nais/tester v0.2.0/go.mod h1:Pp7CtcVk/NZI3z0MW7V4j8CPMG5lhKZeAV4IaJKcslo= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe h1:CdRVopOihru4tXVwKZjhg6C8SbPLCQYOhJKpjBZYhjg= @@ -907,8 +907,8 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= -github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_golang v1.24.0 h1:5XStIklKuAtJSNpdD3s8XJj/Yv78IQmE1kbNk87JrAI= +github.com/prometheus/client_golang v1.24.0/go.mod h1:QcsNdotprC2nS4BTM2ucbcqxd2CeXTEa9jW7zHO9iDE= github.com/prometheus/client_golang/exp v0.0.0-20260518105423-c9d5bc4c50a9 h1:e33IfrrwrJkylWwAGcQ2jMvbWVv13lv0suTXjGNeiqY= github.com/prometheus/client_golang/exp v0.0.0-20260518105423-c9d5bc4c50a9/go.mod h1:vW/EVguzbNw6xMRmozJQWbY60/+Zsg0TgVJOSXGx2iI= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -921,8 +921,8 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= -github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/common v0.70.0 h1:bcpru3tWPVnxGnETLgOV5jbp/JRXgYEyv65CuBLAMMI= +github.com/prometheus/common v0.70.0/go.mod h1:S/SFasQmgGiYH6C81LKCtYa8QACgthGg5zxL2udV7SY= github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= github.com/prometheus/exporter-toolkit v0.16.0 h1:xT/j7L2XKF+VJd6B4fpUw6xWabHrSmsUf6mYmFqyu0s= @@ -934,8 +934,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= -github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= +github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= github.com/prometheus/prometheus v0.312.0 h1:f9jdv2fQhQ1fks9a9YwlGZrKr4hih0rRP/rh0mu3Q18= github.com/prometheus/prometheus v0.312.0/go.mod h1:8oAYd2XPgHXLP4fFKam594R/ZLlPicrrBkVdaWt74Sw= github.com/prometheus/sigv4 v0.4.1 h1:EIc3j+8NBea9u1iV6O5ZAN8uvPq2xOIUPcqCTivHuXs= @@ -993,8 +993,8 @@ github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+D github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= -github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo= +github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q= github.com/sony/gobreaker/v2 v2.4.0 h1:g2KJRW1Ubty3+ZOcSEUN7K+REQJdN6yo6XvaML+jptg= github.com/sony/gobreaker/v2 v2.4.0/go.mod h1:pTyFJgcZ3h2tdQVLZZruK2C0eoFL1fb/G83wK1ZQl+s= github.com/sosodev/duration v1.4.0 h1:35ed0KiVFriGHHzZZJaZLgmTEEICIyt8Sx0RQfj9IjE= @@ -1035,8 +1035,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= +github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 h1:eEGx9kYzZb2cNhRbBrNOCL/YPOM7+RMJiy3bB+ie0/I= @@ -1121,12 +1121,12 @@ go.einride.tech/aip v0.79.0 h1:19zdPlZzlUvxOA8syAFw4LkdJdXepzyTl6gt9XEeqdU= go.einride.tech/aip v0.79.0/go.mod h1:E8+wdTApA70odnpFzJgsGogHozC2JCIhFJBKPr8bVig= go.etcd.io/bbolt v1.5.0 h1:S7GAl7Fxv12yohbwFfIbQCGDWbQbtDGPET4P/bD4lxU= go.etcd.io/bbolt v1.5.0/go.mod h1:mkltfYE5aUHQxUct9N9V+Kp7aSjFqjgrhcXIS70Lrdk= -go.etcd.io/etcd/api/v3 v3.6.8 h1:gqb1VN92TAI6G2FiBvWcqKtHiIjr4SU2GdXxTwyexbM= -go.etcd.io/etcd/api/v3 v3.6.8/go.mod h1:qyQj1HZPUV3B5cbAL8scG62+fyz5dSxxu0w8pn28N6Q= -go.etcd.io/etcd/client/pkg/v3 v3.6.8 h1:Qs/5C0LNFiqXxYf2GU8MVjYUEXJ6sZaYOz0zEqQgy50= -go.etcd.io/etcd/client/pkg/v3 v3.6.8/go.mod h1:GsiTRUZE2318PggZkAo6sWb6l8JLVrnckTNfbG8PWtw= -go.etcd.io/etcd/client/v3 v3.6.8 h1:B3G76t1UykqAOrbio7s/EPatixQDkQBevN8/mwiplrY= -go.etcd.io/etcd/client/v3 v3.6.8/go.mod h1:MVG4BpSIuumPi+ELF7wYtySETmoTWBHVcDoHdVupwt8= +go.etcd.io/etcd/api/v3 v3.6.14 h1:3EEwTzQPiCyhLtacyl2ZkC0pMJWowghi61nJ9JSpO1w= +go.etcd.io/etcd/api/v3 v3.6.14/go.mod h1:L4HXnXoJ5NqXSxiwB4RihT5gGJJVvHEEOpEZ37g1Uj4= +go.etcd.io/etcd/client/pkg/v3 v3.6.14 h1:kqZf/BCRDWk9u5cNwBn1mTA+4GIZAU0POFPHmWHvo/I= +go.etcd.io/etcd/client/pkg/v3 v3.6.14/go.mod h1:Po3WXW01VRS7/gSDf8xjiY2rJTLmAwq/YmKAEz6u1+E= +go.etcd.io/etcd/client/v3 v3.6.14 h1:3hjJbZCFJ3nFR47dZ/jjVu1/z6BRUHN1AA34pRbUW8Q= +go.etcd.io/etcd/client/v3 v3.6.14/go.mod h1:rQqHPE7ju1B1nmaqpGdhRgBHqOTiVaUeymlY1/ATcoM= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= @@ -1175,8 +1175,8 @@ go.opentelemetry.io/contrib/detectors/gcp v1.44.0 h1:NmLfL734pJhM0JKaYd2Y28+nY9d go.opentelemetry.io/contrib/detectors/gcp v1.44.0/go.mod h1:tNAsgd8avTGke1+MndXlU5Cru4PQ9Ai/cCNWQv/ZJ/s= go.opentelemetry.io/contrib/exporters/autoexport v0.64.0 h1:9pzPj3RFyKOxBAMkM2w84LpT+rdHam1XoFA+QhARiRw= go.opentelemetry.io/contrib/exporters/autoexport v0.64.0/go.mod h1:hlVZx1btWH0XTfXpuGX9dsquB50s+tc3fYFOO5elo2M= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.69.0 h1:MCcYL7J6Vt/X0kjqbMZkekCmwsurbQRbL69vkiye2lk= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.69.0/go.mod h1:3jnStNwSufK+f5ktjL4EPcwtig4rtd81NS70lqHuXl8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo= @@ -1256,8 +1256,9 @@ go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U= go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s= @@ -1272,8 +1273,8 @@ golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= -golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM= +golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80= golang.org/x/exp/typeparams v0.0.0-20251209150349-8475f28825e9 h1:DXiKAjbw2KpfWz1Bq2YqF/dBDPEZGJsl3IA2JuVzy8U= golang.org/x/exp/typeparams v0.0.0-20251209150349-8475f28825e9/go.mod h1:4Mzdyp/6jzw9auFDJ3OMF5qksa7UvPnzKqTVGcb04ms= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1460,18 +1461,18 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.7.0 h1:w6WUp1VbkqPEgLz4rkBzH/CSU6HkoqNLp6GstyTx3lU= honnef.co/go/tools v0.7.0/go.mod h1:pm29oPxeP3P82ISxZDgIYeOaf9ta6Pi0EWvCFoLG2vc= -k8s.io/api v0.36.0 h1:SgqDhZzHdOtMk40xVSvCXkP9ME0H05hPM3p9AB1kL80= -k8s.io/api v0.36.0/go.mod h1:m1LVrGPNYax5NBHdO+QuAedXyuzTt4RryI/qnmNvs34= -k8s.io/apiextensions-apiserver v0.36.0 h1:Wt7E8J+VBCbj4FjiBfDTK/neXDDjyJVJc7xfuOHImZ0= -k8s.io/apiextensions-apiserver v0.36.0/go.mod h1:kGDjH0msuiIB3tgsYRV0kS9GqpMYMUsQ3GHv7TApyug= -k8s.io/apimachinery v0.36.4 h1:PT2UzkupGuAx/+xT5XjiMJ1WGpY3fn9/hdAvjweRet4= -k8s.io/apimachinery v0.36.4/go.mod h1:p2I2dipt7JHG+quVwQ1d02d28O4GdDi77RByQ13MTpk= -k8s.io/client-go v0.36.0 h1:pOYi7C4RHChYjMiHpZSpSbIM6ZxVbRXBy7CuiIwqA3c= -k8s.io/client-go v0.36.0/go.mod h1:ZKKcpwF0aLYfkHFCjillCKaTK/yBkEDHTDXCFY6AS9Y= +k8s.io/api v0.37.0 h1:Z//Vj9N7RA/yS2sDmxyeo7h+RR4zbUrd2vrd3Z0TbB4= +k8s.io/api v0.37.0/go.mod h1:LKXgcJWMc+f4OLbP5SFR8rulEg07zZhpi/zMULiBImk= +k8s.io/apiextensions-apiserver v0.37.0 h1:zRMQ3+/LIE5oZ0tVvXwYHC+dIkSP5cjNWju7AZU1LOI= +k8s.io/apiextensions-apiserver v0.37.0/go.mod h1:HU0PfSBwchHL5iDau6jjt9zU6ryWkDDlaVUiq91NK80= +k8s.io/apimachinery v0.37.0 h1:Np2AbDtf8x6RDHiD8T9LbKJ9gaegeVNa8yNm5FuGKm0= +k8s.io/apimachinery v0.37.0/go.mod h1:RN3nhprFSCxOi5Selxd7oMTXOe/c+ZbcE7Im+TS2zkE= +k8s.io/client-go v0.37.0 h1:nsN31fy8wBySuZ+QRnKmrjRSQLOG2rvoGN0tKd12zhQ= +k8s.io/client-go v0.37.0/go.mod h1:FcGqw+Ll/gNQiq+nPGY1Oyt9y7SgDh1d3MW3RFDEbn0= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= -k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg= -k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= +k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad h1:oXImqH8mQNk7PmvzKhmN3ddJoY6OnyM225MXwGHPm0A= +k8s.io/kube-openapi v0.0.0-20260721132016-d427ff9ee9ad/go.mod h1:0/mqHCVhlumdJ3BhCfnjSZQE037nAhNodh1/hK0T8/I= k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 h1:jVkFFVfXdXP74B/zbO3hM3hpSFD0xvhQ5U686DPurkE= k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3/go.mod h1:M2s5JB1lIYP3jzZdorPLHXIPJzt9vv2muW5a6L9DtNM= modernc.org/libc v1.68.0 h1:PJ5ikFOV5pwpW+VqCK1hKJuEWsonkIJhhIXyuF/91pQ= @@ -1484,13 +1485,13 @@ modernc.org/sqlite v1.46.1 h1:eFJ2ShBLIEnUWlLy12raN0Z1plqmFX9Qe3rjQTKt6sU= modernc.org/sqlite v1.46.1/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA= mvdan.cc/gofumpt v0.9.2 h1:zsEMWL8SVKGHNztrx6uZrXdp7AX8r421Vvp23sz7ik4= mvdan.cc/gofumpt v0.9.2/go.mod h1:iB7Hn+ai8lPvofHd9ZFGVg2GOr8sBUw1QUWjNbmIL/s= -sigs.k8s.io/controller-runtime v0.24.1 h1:miPEwrmirImAvgME1L9qebGHrOnGJoVmVdtOU9fRfo4= -sigs.k8s.io/controller-runtime v0.24.1/go.mod h1:vFkfY5fGt5xAC/sKb8IBFKgWPNKG9OUG29dR8Y2wImw= +sigs.k8s.io/controller-runtime v0.25.1 h1:BKgU9OeE8xv8EbbM8cY0NVzTQs35rokkdq1jh12fMb4= +sigs.k8s.io/controller-runtime v0.25.1/go.mod h1:4QqLdT6z/L6Olj8JJCtvztid4/fnIiYsfaTFScegctc= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.3.3 h1:u08YRbVUi59ri4YD6cg0UqNM4Dimn0sIl+wldcx5PYw= -sigs.k8s.io/structured-merge-diff/v6 v6.3.3/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.4.2 h1:qdOxHwrl2Kaag1aQEarlYcOA9vSyGCp3CIki3aW8c4Q= +sigs.k8s.io/structured-merge-diff/v6 v6.4.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/integration_tests/create_postgres_access.lua b/integration_tests/create_postgres_access.lua new file mode 100644 index 000000000..b4e042cdc --- /dev/null +++ b/integration_tests/create_postgres_access.lua @@ -0,0 +1,376 @@ +local user = User.new("user", "user@usersen.com") +local otherMemberUser = User.new("othermember", "othermember@usersen.com") +local nonMemberUser = User.new("nonmember", "other@user.com") + +local mainTeam = Team.new("someteamname", "purpose", "#slack_channel") +mainTeam:addMember(user) +mainTeam:addMember(otherMemberUser) + +Helper.readK8sResources("k8s_resources/create_postgres_access") + +Test.gql("Postgres branches use local names and stay scoped to their Postgres", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[{ team(slug: "someteamname") { environment(name: "dev") { + postgres(name: "foobar") { activeBranch { name } branches { nodes { name postgres { name } } } branch(name: "recovered") { name postgres { name } } } + } } }]] + t.check { data = { team = { environment = { postgres = { + activeBranch = { name = "main" }, + branches = { nodes = { { name = "main", postgres = { name = "foobar" } }, { name = "recovered", postgres = { name = "foobar" } } } }, + branch = { name = "recovered", postgres = { name = "foobar" } }, + } } } } } +end) + +Test.gql("Create personal postgres access without authorization", function(t) + t.addHeader("x-user-email", nonMemberUser:email()) + t.query [[ + mutation CreatePostgresAccess { + createPostgresAccess(input: { + postgres: "foobar", branch: "main" + environmentName: "dev" + teamSlug: "someteamname" + accessLevel: READ + reason: "Testing personal database access" + }) { + name + expiresAt + } + } + ]] + + t.check { + errors = { + { + locations = NotNull(), + message = Contains('you need the "postgres:access:grant" authorization.'), + path = { "createPostgresAccess" }, + }, + }, + data = Null, + } +end) + +Test.gql("Create personal postgres access requires an audit reason", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[ + mutation CreatePostgresAccess { + createPostgresAccess(input: { + postgres: "foobar", branch: "main" + environmentName: "dev" + teamSlug: "someteamname" + accessLevel: READ + reason: "short" + }) { + name + } + } + ]] + + t.check { + errors = { + { + extensions = { field = "reason" }, + message = Contains("Reason must be at least 10 characters"), + path = { "createPostgresAccess" }, + }, + }, + data = Null, + } +end) + +Test.gql("Create personal postgres access rejects an unknown instance", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[ + mutation CreatePostgresAccess { + createPostgresAccess(input: { + postgres: "foobar", branch: "unknown" + environmentName: "dev" + teamSlug: "someteamname" + accessLevel: READ + reason: "Testing personal database access" + }) { + name + expiresAt + } + } + ]] + + t.check { + errors = { + { + extensions = { field = "branch" }, + message = Contains("Could not find PostgresBranch"), + path = { "createPostgresAccess" }, + }, + }, + data = Null, + } +end) + +Test.gql("Create personal postgres access rejects a logical Postgres without a physical instance", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[ + mutation { createPostgresAccess(input: { + postgres: "legacy-only", branch: "main", environmentName: "dev", + teamSlug: "someteamname", accessLevel: READ, + reason: "Testing missing physical database instance" + }) { name } } + ]] + t.check { + errors = { { extensions = { field = "branch" }, message = Contains("Could not find PostgresBranch"), path = { "createPostgresAccess" } } }, + data = Null, + } +end) + +Test.gql("Create personal postgres access rejects an unavailable instance", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[ + mutation CreatePostgresAccess { + createPostgresAccess(input: { + postgres: "progressing", branch: "main" + environmentName: "dev" + teamSlug: "someteamname" + accessLevel: READ + reason: "Testing personal database access" + }) { + name + expiresAt + } + } + ]] + + t.check { + errors = { + { + extensions = { field = "branch" }, + message = Contains("is not available"), + path = { "createPostgresAccess" }, + }, + }, + data = Null, + } +end) + +Test.gql("Create personal postgres access", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[ + mutation CreatePostgresAccess { + createPostgresAccess(input: { + postgres: "foobar", branch: "main" + environmentName: "dev" + teamSlug: "someteamname" + accessLevel: READWRITE + reason: "Testing personal database access" + ttl: "30m" + }) { + name + expiresAt + } + } + ]] + + t.check { + data = { + createPostgresAccess = { + name = NotNull(), + expiresAt = NotNull(), + }, + }, + } +end) + +Test.gql("Personal postgres access is audited as a self-grant", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[ + { + team(slug: "someteamname") { + activityLog { + nodes { + message + ... on PostgresPersonalAccessCreatedActivityLogEntry { + data { + username + accessLevel + expiresAt + reason + } + } + } + } + } + } + ]] + + t.check { + data = { + team = { + activityLog = { + nodes = { + { + message = Contains("Requested READWRITE personal Postgres access for user@usersen.com"), + data = { + username = "user@usersen.com", + accessLevel = "READWRITE", + expiresAt = NotNull(), + reason = "Testing personal database access", + }, + }, + }, + }, + }, + }, + } +end) + +Test.gql("Personal access targets a physical instance, even when its name differs from logical Postgres", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[ + mutation { createPostgresAccess(input: { + postgres: "foobar", branch: "recovered", environmentName: "dev", + teamSlug: "someteamname", accessLevel: READ, + reason: "Testing access to a recovered instance" + }) { name } } + ]] + t.check { data = { createPostgresAccess = { name = NotNull() } } } + + t.query [[ + query { team(slug: "someteamname") { environment(name: "dev") { + postgresAccess(name: "recovered-access") { postgresBranch { name } } + } } } + ]] + t.check { data = { team = { environment = { postgresAccess = { postgresBranch = { name = "recovered" } } } } } } +end) + +Test.gql("PostgresAccess status is visible to authorized team members", function(t) + t.addHeader("x-user-email", otherMemberUser:email()) + for _, test in ipairs({ + { name = "ready-access", state = "READY", message = "database role and relay mapping are ready" }, + { name = "pending-access", state = "PENDING", message = Null }, + { name = "failed-access", state = "FAILED", message = Contains("not supported") }, + { name = "expired-access", state = "EXPIRED", message = "access has expired" }, + }) do + t.query(string.format( + [[query { team(slug: "someteamname") { environment(name: "dev") { postgresAccess(name: "%s") { name state message } } } }]], + test.name)) + t.check { + data = { team = { environment = { + postgresAccess = { + name = test.name, + state = test.state, + message = test.message, + }, + } } }, + } + end +end) + +Test.gql("PostgresAccess status rejects users outside the team", function(t) + t.addHeader("x-user-email", nonMemberUser:email()) + t.query [[ + query { team(slug: "someteamname") { environment(name: "dev") { postgresAccess(name: "ready-access") { state } } } } + ]] + t.check { + errors = { + { + locations = NotNull(), + message = Contains('you need the "postgres:access:grant" authorization.'), + path = { "team", "environment", "postgresAccess" }, + }, + }, + data = Null, + } +end) + +Test.gql("PostgresAccess connection returns credentials only to its owner", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[ + query GetPostgresAccessConnection { + team(slug: "someteamname") { environment(name: "dev") { + postgresAccess(name: "ready-access") { connection { + password + caCertificate + serverName + username + relayEndpoint + relayAccess + relayToken + } } + } } + } + ]] + + t.check { + data = { team = { environment = { postgresAccess = { + connection = { + password = "supersecret", + caCertificate = "test-ca-certificate", + serverName = "pg-foobar-main-a4f04c0c-rw.someteamname.svc.cluster.local", + username = "user-foobar-role", + relayEndpoint = Contains("https://relay.external.dev."), + relayAccess = "someteamname/mapped-access", + relayToken = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8", + }, + } } } }, + } +end) + +Test.gql("PostgresAccess connection rejects a different team member", function(t) + t.addHeader("x-user-email", otherMemberUser:email()) + t.query [[ + query { team(slug: "someteamname") { environment(name: "dev") { postgresAccess(name: "ready-access") { state connection { password } } } } } + ]] + t.check { + errors = { { locations = NotNull(), path = { "team", "environment", "postgresAccess", "connection" }, message = Contains("not authorized") } }, + data = { team = { environment = { postgresAccess = { state = "READY", connection = Null } } } }, + } +end) + +Test.gql("PostgresAccess credentials cannot be read through generic Secret elevation", function(t) + t.addHeader("x-user-email", otherMemberUser:email()) + for _, name in ipairs({ "ready-access-relay-token", "ready-access-credentials" }) do + t.query(string.format([[ + mutation { viewSecretValues(input: { + name: "%s", team: "someteamname", environment: "dev", + reason: "Trying to read personal access credentials" + }) { values { name value } } } + ]], name)) + t.check { + errors = { { locations = NotNull(), path = { "viewSecretValues" }, message = Contains("only available through postgresAccessConnection") } }, + data = Null, + } + end +end) + +Test.gql("PostgresAccess connection rejects expired, unready, and missing-secret access", function(t) + t.addHeader("x-user-email", user:email()) + for _, test in ipairs({ + { name = "expired-access", state = "EXPIRED", message = "has expired" }, + { name = "pending-access", state = "PENDING", message = "is not ready" }, + { name = "failed-access", state = "FAILED", message = "is not ready" }, + { name = "missing-secret-access", state = "READY", message = "secrets for PostgresAccess is not available" }, + }) do + t.query(string.format( + [[query { team(slug: "someteamname") { environment(name: "dev") { postgresAccess(name: "%s") { state connection { password } } } } }]], + test.name)) + t.check { + errors = { { locations = NotNull(), path = { "team", "environment", "postgresAccess", "connection" }, message = Contains(test.message) } }, + data = { team = { environment = { postgresAccess = { state = test.state, connection = Null } } } }, + } + end +end) + +Test.gql("Personal postgres connection retrieval is audited", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[ + query { team(slug: "someteamname") { environment(name: "dev") { postgresAccess(name: "ready-access") { connection { password } } } } } + ]] + t.check { data = { team = { environment = { postgresAccess = { connection = { password = "supersecret" } } } } } } + + t.query [[ + { team(slug: "someteamname") { activityLog(first: 1) { nodes { message ... on PostgresPersonalAccessConnectionActivityLogEntry { resourceName } } } } } + ]] + t.check { + data = { team = { activityLog = { nodes = { + { message = Contains("Retrieved personal Postgres connection materials"), resourceName = "ready-access" }, + } } } }, + } +end) diff --git a/integration_tests/k8s_resources/create_postgres_access/dev/someteamname/accesses.yaml b/integration_tests/k8s_resources/create_postgres_access/dev/someteamname/accesses.yaml new file mode 100644 index 000000000..394e2bae9 --- /dev/null +++ b/integration_tests/k8s_resources/create_postgres_access/dev/someteamname/accesses.yaml @@ -0,0 +1,106 @@ +--- +apiVersion: nais.io/v1 +kind: PostgresAccess +metadata: + name: ready-access + namespace: someteamname + uid: 11111111-1111-4111-8111-111111111111 +spec: + postgresBranch: foobar-main-a4f04c0c + username: user@usersen.com + accessLevel: readwrite + expiresAt: "2099-09-17T12:00:00Z" +status: + databaseRole: user-foobar-role + relayAccess: mapped-access + tokenSecret: ready-access-relay-token + serverName: pg-foobar-main-a4f04c0c-rw.someteamname.svc.cluster.local + serverCASecret: pg-foobar-ca + conditions: + - type: Ready + status: "True" + reason: Ready + message: "database role and relay mapping are ready" +--- +apiVersion: nais.io/v1 +kind: PostgresAccess +metadata: + name: recovered-access + namespace: someteamname +spec: + postgresBranch: foobar-recovered-b88d8360 + username: user@usersen.com + accessLevel: read + expiresAt: "2099-09-17T12:00:00Z" +status: + conditions: + - type: Ready + status: "False" +--- +apiVersion: nais.io/v1 +kind: PostgresAccess +metadata: + name: pending-access + namespace: someteamname +spec: + postgresBranch: foobar-main-a4f04c0c + username: user@usersen.com + accessLevel: read + expiresAt: "2099-09-17T12:00:00Z" +status: + conditions: + - type: Ready + status: "False" +--- +apiVersion: nais.io/v1 +kind: PostgresAccess +metadata: + name: failed-access + namespace: someteamname +spec: + postgresBranch: foobar-main-a4f04c0c + username: user@usersen.com + accessLevel: readwritecreate + expiresAt: "2099-09-17T12:00:00Z" +status: + conditions: + - type: Ready + status: "False" + reason: UnsupportedAccessLevel + message: "readwritecreate is not supported for this Postgres instance" +--- +apiVersion: nais.io/v1 +kind: PostgresAccess +metadata: + name: expired-access + namespace: someteamname +spec: + postgresBranch: foobar-main-a4f04c0c + username: user@usersen.com + accessLevel: read + expiresAt: "2000-01-01T00:00:00Z" +status: + conditions: + - type: Ready + status: "True" +--- +apiVersion: nais.io/v1 +kind: PostgresAccess +metadata: + name: missing-secret-access + namespace: someteamname + uid: 22222222-2222-4222-8222-222222222222 +spec: + postgresBranch: foobar-main-a4f04c0c + username: user@usersen.com + accessLevel: read + expiresAt: "2099-09-17T12:00:00Z" +status: + databaseRole: user-foobar-role + relayAccess: missing-secret-access + tokenSecret: missing-secret-access-relay-token + serverName: pg-foobar-main-a4f04c0c-rw.someteamname.svc.cluster.local + serverCASecret: pg-foobar-ca + conditions: + - type: Ready + status: "True" diff --git a/integration_tests/k8s_resources/create_postgres_access/dev/someteamname/connection.yaml b/integration_tests/k8s_resources/create_postgres_access/dev/someteamname/connection.yaml new file mode 100644 index 000000000..eb8beacb9 --- /dev/null +++ b/integration_tests/k8s_resources/create_postgres_access/dev/someteamname/connection.yaml @@ -0,0 +1,40 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: ready-access-relay-token + namespace: someteamname + ownerReferences: + - apiVersion: nais.io/v1 + kind: PostgresAccess + name: ready-access + uid: 11111111-1111-4111-8111-111111111111 + controller: true +type: Opaque +data: + token: QUFFQ0F3UUZCZ2NJQ1FvTERBME9EeEFSRWhNVUZSWVhHQmthR3h3ZEhoOA== +--- +apiVersion: v1 +kind: Secret +metadata: + name: ready-access-credentials + namespace: someteamname + ownerReferences: + - apiVersion: nais.io/v1 + kind: PostgresAccess + name: ready-access + uid: 11111111-1111-4111-8111-111111111111 + controller: true +type: kubernetes.io/basic-auth +data: + username: dXNlci1mb29iYXItcm9sZQ== + password: c3VwZXJzZWNyZXQ= +--- +apiVersion: v1 +kind: Secret +metadata: + name: pg-foobar-ca + namespace: someteamname +type: Opaque +data: + ca.crt: dGVzdC1jYS1jZXJ0aWZpY2F0ZQ== diff --git a/integration_tests/k8s_resources/create_postgres_access/dev/someteamname/postgres.yaml b/integration_tests/k8s_resources/create_postgres_access/dev/someteamname/postgres.yaml new file mode 100644 index 000000000..319a2d49f --- /dev/null +++ b/integration_tests/k8s_resources/create_postgres_access/dev/someteamname/postgres.yaml @@ -0,0 +1,111 @@ +--- +apiVersion: nais.io/v1 +kind: Postgres +metadata: + name: foobar + namespace: someteamname +spec: + majorVersion: "17" +status: + activeBranch: main +--- +apiVersion: nais.io/v1 +kind: Postgres +metadata: + name: progressing + namespace: someteamname +spec: + majorVersion: "17" +status: + activeBranch: main +--- +apiVersion: nais.io/v1 +kind: Postgres +metadata: + name: legacy-only + namespace: someteamname +spec: + majorVersion: "17" +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: foobar-main-a4f04c0c + namespace: someteamname +spec: + postgres: foobar + branchName: main +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: progressing-main-6cb99f88 + namespace: someteamname +spec: + postgres: progressing + branchName: main +status: + reconcilePhase: Preparing + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "False" + reason: Reconciling + message: "Cluster is in phase: " + lastTransitionTime: "2026-09-17T00:00:00Z" +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: foobar-recovered-b88d8360 + namespace: someteamname +spec: + postgres: foobar + branchName: recovered +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: pg-foobar-main-a4f04c0c + namespace: someteamname +spec: + certificates: + serverCASecret: pg-foobar-ca +status: + phase: Cluster in healthy state + conditions: + - type: Ready + status: "True" + reason: ClusterReady + lastTransitionTime: "2026-09-17T00:00:00Z" +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: pg-foobar-recovered-b88d8360 + namespace: someteamname +spec: + certificates: + serverCASecret: pg-foobar-ca +status: + phase: Cluster in healthy state + conditions: + - type: Ready + status: "True" + reason: ClusterReady + lastTransitionTime: "2026-09-17T00:00:00Z" diff --git a/integration_tests/k8s_resources/label_selectors/dev/labelteam/postgres.yaml b/integration_tests/k8s_resources/label_selectors/dev/labelteam/postgres.yaml index 7b92807cd..1f0e2507c 100644 --- a/integration_tests/k8s_resources/label_selectors/dev/labelteam/postgres.yaml +++ b/integration_tests/k8s_resources/label_selectors/dev/labelteam/postgres.yaml @@ -1,52 +1,88 @@ --- -apiVersion: data.nais.io/v1 +apiVersion: nais.io/v1 kind: Postgres metadata: name: postgres-one namespace: labelteam +spec: + majorVersion: "17" +status: + activeBranch: main +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: postgres-one-main-e58682b9 + namespace: labelteam labels: tag: target priority: high spec: - cluster: - majorVersion: "17" - resources: - cpu: 100m - diskSize: 2Gi - memory: 2G - database: - collation: nb_NO + postgres: postgres-one + branchName: main +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" --- -apiVersion: data.nais.io/v1 +apiVersion: nais.io/v1 kind: Postgres metadata: name: postgres-two namespace: labelteam +spec: + majorVersion: "17" +status: + activeBranch: main +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: postgres-two-main-935c2b2a + namespace: labelteam labels: tag: target spec: - cluster: - majorVersion: "17" - resources: - cpu: 100m - diskSize: 2Gi - memory: 2G - database: - collation: nb_NO + postgres: postgres-two + branchName: main +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" --- -apiVersion: data.nais.io/v1 +apiVersion: nais.io/v1 kind: Postgres metadata: name: postgres-three namespace: labelteam +spec: + majorVersion: "17" +status: + activeBranch: main +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: postgres-three-main-1013cb94 + namespace: labelteam labels: tag: other spec: - cluster: - majorVersion: "17" - resources: - cpu: 100m - diskSize: 2Gi - memory: 2G - database: - collation: nb_NO + postgres: postgres-three + branchName: main +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" diff --git a/integration_tests/k8s_resources/postgres_audit_log/dev-gcp/audit-postgres-team/postgres_with_audit.yaml b/integration_tests/k8s_resources/postgres_audit_log/dev-gcp/audit-postgres-team/postgres_with_audit.yaml index bdfbcd8f2..fc59fc74b 100644 --- a/integration_tests/k8s_resources/postgres_audit_log/dev-gcp/audit-postgres-team/postgres_with_audit.yaml +++ b/integration_tests/k8s_resources/postgres_audit_log/dev-gcp/audit-postgres-team/postgres_with_audit.yaml @@ -1,17 +1,27 @@ --- -apiVersion: data.nais.io/v1 +apiVersion: nais.io/v1 kind: Postgres metadata: name: audit-enabled namespace: audit-postgres-team spec: - cluster: - majorVersion: "16" - resources: - cpu: 100m - diskSize: 5Gi - memory: 2G - audit: - enabled: true - database: - collation: en_US + majorVersion: "16" +status: + activeBranch: main +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: audit-enabled-main-74f70e60 + namespace: audit-postgres-team +spec: + postgres: audit-enabled + branchName: main +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" diff --git a/integration_tests/k8s_resources/postgres_branch_delete/dev/pg-delete-team/postgres.yaml b/integration_tests/k8s_resources/postgres_branch_delete/dev/pg-delete-team/postgres.yaml new file mode 100644 index 000000000..bf6eb1e4e --- /dev/null +++ b/integration_tests/k8s_resources/postgres_branch_delete/dev/pg-delete-team/postgres.yaml @@ -0,0 +1,27 @@ +apiVersion: nais.io/v1 +kind: Postgres +metadata: + name: orders + namespace: pg-delete-team +spec: + majorVersion: "17" +status: + activeBranch: new +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: orders-new-51fe6d69 + namespace: pg-delete-team +spec: + postgres: orders + branchName: new +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: orders-old-9cdd54c9 + namespace: pg-delete-team +spec: + postgres: orders + branchName: old diff --git a/integration_tests/k8s_resources/postgres_instances/dev/someteamname/applications.yaml b/integration_tests/k8s_resources/postgres_branches/dev/someteamname/applications.yaml similarity index 83% rename from integration_tests/k8s_resources/postgres_instances/dev/someteamname/applications.yaml rename to integration_tests/k8s_resources/postgres_branches/dev/someteamname/applications.yaml index 5a57282e5..304150ad5 100644 --- a/integration_tests/k8s_resources/postgres_instances/dev/someteamname/applications.yaml +++ b/integration_tests/k8s_resources/postgres_branches/dev/someteamname/applications.yaml @@ -6,8 +6,9 @@ metadata: namespace: someteamname spec: image: ghcr.io/nais/testapp:latest - postgres: - clusterName: foobar + uses: + postgres: + - name: foobar --- apiVersion: nais.io/v1alpha1 kind: Application @@ -16,8 +17,9 @@ metadata: namespace: someteamname spec: image: ghcr.io/nais/testapp:latest - postgres: - clusterName: foobar + uses: + postgres: + - name: foobar --- apiVersion: nais.io/v1 kind: Naisjob @@ -27,8 +29,9 @@ metadata: spec: image: ghcr.io/nais/testapp:latest schedule: "0 0 * * *" - postgres: - clusterName: foobar + uses: + postgres: + - name: foobar --- apiVersion: nais.io/v1alpha1 kind: Application diff --git a/integration_tests/k8s_resources/postgres_branches/dev/someteamname/postgres_another_db.yaml b/integration_tests/k8s_resources/postgres_branches/dev/someteamname/postgres_another_db.yaml new file mode 100644 index 000000000..6716f9e58 --- /dev/null +++ b/integration_tests/k8s_resources/postgres_branches/dev/someteamname/postgres_another_db.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: nais.io/v1 +kind: Postgres +metadata: + name: another-db + namespace: someteamname +spec: + majorVersion: "16" +status: + activeBranch: main +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: another-db-main-72a85261 + namespace: someteamname +spec: + postgres: another-db + branchName: main +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" diff --git a/integration_tests/k8s_resources/postgres_branches/dev/someteamname/postgres_foobar.yaml b/integration_tests/k8s_resources/postgres_branches/dev/someteamname/postgres_foobar.yaml new file mode 100644 index 000000000..3410b559c --- /dev/null +++ b/integration_tests/k8s_resources/postgres_branches/dev/someteamname/postgres_foobar.yaml @@ -0,0 +1,31 @@ +--- +apiVersion: nais.io/v1 +kind: Postgres +metadata: + name: foobar + namespace: someteamname +spec: + majorVersion: "17" + resources: + cpu: 100m + memory: 2Gi + diskSize: 2Gi +status: + activeBranch: main +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: foobar-main-a4f04c0c + namespace: someteamname +spec: + postgres: foobar + branchName: main +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" diff --git a/integration_tests/k8s_resources/postgres_branches/dev/someteamname/postgres_with_audit.yaml b/integration_tests/k8s_resources/postgres_branches/dev/someteamname/postgres_with_audit.yaml new file mode 100644 index 000000000..ace266d1d --- /dev/null +++ b/integration_tests/k8s_resources/postgres_branches/dev/someteamname/postgres_with_audit.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: nais.io/v1 +kind: Postgres +metadata: + name: with-audit + namespace: someteamname +spec: + majorVersion: "16" +status: + activeBranch: main +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: with-audit-main-cb3cab0a + namespace: someteamname +spec: + postgres: with-audit + branchName: main +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" diff --git a/integration_tests/k8s_resources/postgres_branches/dev/someteamname/postgres_without_audit.yaml b/integration_tests/k8s_resources/postgres_branches/dev/someteamname/postgres_without_audit.yaml new file mode 100644 index 000000000..ca8c42dac --- /dev/null +++ b/integration_tests/k8s_resources/postgres_branches/dev/someteamname/postgres_without_audit.yaml @@ -0,0 +1,27 @@ +--- +apiVersion: nais.io/v1 +kind: Postgres +metadata: + name: without-audit + namespace: someteamname +spec: + majorVersion: "15" +status: + activeBranch: main +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: without-audit-main-d2111831 + namespace: someteamname +spec: + postgres: without-audit + branchName: main +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" diff --git a/integration_tests/k8s_resources/postgres_instances/dev/someteamname/postgres_another_db.yaml b/integration_tests/k8s_resources/postgres_instances/dev/someteamname/postgres_another_db.yaml deleted file mode 100644 index 3c35bfab3..000000000 --- a/integration_tests/k8s_resources/postgres_instances/dev/someteamname/postgres_another_db.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -apiVersion: data.nais.io/v1 -kind: Postgres -metadata: - name: another-db - namespace: someteamname -spec: - cluster: - majorVersion: "16" - resources: - cpu: 200m - diskSize: 10Gi - memory: 4G - database: - collation: en_US diff --git a/integration_tests/k8s_resources/postgres_instances/dev/someteamname/postgres_foobar.yaml b/integration_tests/k8s_resources/postgres_instances/dev/someteamname/postgres_foobar.yaml deleted file mode 100644 index 6796d66e0..000000000 --- a/integration_tests/k8s_resources/postgres_instances/dev/someteamname/postgres_foobar.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -apiVersion: data.nais.io/v1 -kind: Postgres -metadata: - name: foobar - namespace: someteamname -spec: - cluster: - majorVersion: "17" - resources: - cpu: 100m - diskSize: 2Gi - memory: 2G - database: - collation: nb_NO diff --git a/integration_tests/k8s_resources/postgres_instances/dev/someteamname/postgres_with_audit.yaml b/integration_tests/k8s_resources/postgres_instances/dev/someteamname/postgres_with_audit.yaml deleted file mode 100644 index 6123e143d..000000000 --- a/integration_tests/k8s_resources/postgres_instances/dev/someteamname/postgres_with_audit.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -apiVersion: data.nais.io/v1 -kind: Postgres -metadata: - name: with-audit - namespace: someteamname -spec: - cluster: - majorVersion: "16" - resources: - cpu: 100m - diskSize: 5Gi - memory: 2G - audit: - enabled: true - database: - collation: en_US diff --git a/integration_tests/k8s_resources/postgres_instances/dev/someteamname/postgres_without_audit.yaml b/integration_tests/k8s_resources/postgres_instances/dev/someteamname/postgres_without_audit.yaml deleted file mode 100644 index cdb00f766..000000000 --- a/integration_tests/k8s_resources/postgres_instances/dev/someteamname/postgres_without_audit.yaml +++ /dev/null @@ -1,17 +0,0 @@ ---- -apiVersion: data.nais.io/v1 -kind: Postgres -metadata: - name: without-audit - namespace: someteamname -spec: - cluster: - majorVersion: "15" - resources: - cpu: 100m - diskSize: 3Gi - memory: 1G - audit: - enabled: false - database: - collation: en_US diff --git a/integration_tests/k8s_resources/postgres_workloads/dev/postgres-workload-team/resources.yaml b/integration_tests/k8s_resources/postgres_workloads/dev/postgres-workload-team/resources.yaml new file mode 100644 index 000000000..e71fb782a --- /dev/null +++ b/integration_tests/k8s_resources/postgres_workloads/dev/postgres-workload-team/resources.yaml @@ -0,0 +1,64 @@ +apiVersion: nais.io/v1 +kind: Postgres +metadata: + name: orders + namespace: postgres-workload-team +spec: + majorVersion: "17" +status: + activeBranch: green +--- +apiVersion: nais.io/v1 +kind: Postgres +metadata: + name: reports + namespace: postgres-workload-team +spec: + majorVersion: "17" +status: + activeBranch: recovered +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: orders-green-d77a35b4 + namespace: postgres-workload-team +spec: + postgres: orders + branchName: green +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: reports-recovered-9e2f7180 + namespace: postgres-workload-team +spec: + postgres: reports + branchName: recovered +--- +apiVersion: nais.io/v1alpha1 +kind: Application +metadata: + name: consumer + namespace: postgres-workload-team +spec: + image: ghcr.io/nais/testapp:latest + uses: + postgres: + - name: orders + - name: reports + envPrefix: REPORTS_ +--- +apiVersion: nais.io/v1 +kind: Naisjob +metadata: + name: scheduled-reader + namespace: postgres-workload-team +spec: + image: ghcr.io/nais/testapp:latest + schedule: "0 0 * * *" + uses: + postgres: + - name: orders + - name: reports + envPrefix: REPORTS_ diff --git a/integration_tests/label_selectors.lua b/integration_tests/label_selectors.lua index 20a087769..b25c865c0 100644 --- a/integration_tests/label_selectors.lua +++ b/integration_tests/label_selectors.lua @@ -139,12 +139,15 @@ Test.gql("Check all Postgres instances (no filter)", function(t) { team(slug: "labelteam") { slug - postgresInstances { + postgresBranches { pageInfo { totalCount } nodes { name + postgres { + name + } labels { key value @@ -159,26 +162,29 @@ Test.gql("Check all Postgres instances (no filter)", function(t) data = { team = { slug = "labelteam", - postgresInstances = { + postgresBranches = { pageInfo = { totalCount = 3, }, nodes = { { - name = "postgres-one", + name = "main", + postgres = { name = "postgres-one" }, labels = { { key = "priority", value = "high" }, { key = "tag", value = "target" }, }, }, { - name = "postgres-three", + name = "main", + postgres = { name = "postgres-three" }, labels = { { key = "tag", value = "other" }, }, }, { - name = "postgres-two", + name = "main", + postgres = { name = "postgres-two" }, labels = { { key = "tag", value = "target" }, }, @@ -195,12 +201,15 @@ Test.gql("Postgres filter by tag=target", function(t) t.query [[ { team(slug: "labelteam") { - postgresInstances(filter: { labels: [{ key: "tag", value: "target" }] }) { + postgresBranches(filter: { labels: [{ key: "tag", value: "target" }] }) { pageInfo { totalCount } nodes { name + postgres { + name + } } } } @@ -210,13 +219,13 @@ Test.gql("Postgres filter by tag=target", function(t) t.check { data = { team = { - postgresInstances = { + postgresBranches = { pageInfo = { totalCount = 2, }, nodes = { - { name = "postgres-one" }, - { name = "postgres-two" }, + { name = "main", postgres = { name = "postgres-one" } }, + { name = "main", postgres = { name = "postgres-two" } }, }, }, }, @@ -229,7 +238,7 @@ Test.gql("Postgres filter by tag=target and priority=high", function(t) t.query [[ { team(slug: "labelteam") { - postgresInstances(filter: { + postgresBranches(filter: { labels: [ { key: "tag", value: "target" }, { key: "priority", value: "high" } @@ -240,6 +249,9 @@ Test.gql("Postgres filter by tag=target and priority=high", function(t) } nodes { name + postgres { + name + } } } } @@ -249,12 +261,12 @@ Test.gql("Postgres filter by tag=target and priority=high", function(t) t.check { data = { team = { - postgresInstances = { + postgresBranches = { pageInfo = { totalCount = 1, }, nodes = { - { name = "postgres-one" }, + { name = "main", postgres = { name = "postgres-one" } }, }, }, }, diff --git a/integration_tests/postgres_audit_log.lua b/integration_tests/postgres_audit_log.lua index 5266e2cfe..97e6aafbb 100644 --- a/integration_tests/postgres_audit_log.lua +++ b/integration_tests/postgres_audit_log.lua @@ -1,74 +1,16 @@ +-- The old per-instance audit flag and Cloud SQL Logs URL are not part of +-- nais.io/v1 PostgresBranch. Verify logical configuration instead. Helper.readK8sResources("k8s_resources/postgres_audit_log") - local user = User.new("authenticated", "postgres-audit-user@example.com", "postgres-audit-user-id") -local team = Team.new("audit-postgres-team", "Testing Postgres audit logging", "#audit-postgres") +local team = Team.new("audit-postgres-team", "Testing logical Postgres", "#audit-postgres") team:addMember(user) -team:setEnvironmentGCPProjectID("dev-gcp", "nais-audit-project") - -Test.gql("Postgres instance with audit logging enabled has audit URL", function(t) - t.addHeader("x-user-email", user:email()) - - t.query [[ - query { - team(slug: "audit-postgres-team") { - environment(name: "dev-gcp") { - postgresInstance(name: "audit-enabled") { - name - audit { - enabled - url - } - } - } - } - } - ]] - t.check { - data = { - team = { - environment = { - postgresInstance = { - name = "audit-enabled", - audit = { - enabled = true, - url = Contains("console.cloud.google.com/logs"), - }, - }, - }, - }, - }, - } -end) - -Test.gql("Postgres audit URL contains expected components", function(t) +Test.gql("Logical Postgres settings are not fabricated on physical instances", function(t) t.addHeader("x-user-email", user:email()) - - t.query [[ - query { - team(slug: "audit-postgres-team") { - environment(name: "dev-gcp") { - postgresInstance(name: "audit-enabled") { - audit { - url - } - } - } - } - } - ]] - - t.check { - data = { - team = { - environment = { - postgresInstance = { - audit = { - url = Contains("nais-audit-project%3Aaudit-enabled"), - }, - }, - }, - }, - }, - } + t.query [[{ team(slug:"audit-postgres-team") { environment(name:"dev-gcp") { + postgres(name:"audit-enabled") { branch(name:"main") { name state postgres { name majorVersion } } } + } } }]] + t.check { data = { team = { environment = { postgres = { branch = { + name = "main", state = "AVAILABLE", postgres = { name = "audit-enabled", majorVersion = "16" }, + } } } } } } end) diff --git a/integration_tests/postgres_branch_delete.lua b/integration_tests/postgres_branch_delete.lua new file mode 100644 index 000000000..a3e1941e2 --- /dev/null +++ b/integration_tests/postgres_branch_delete.lua @@ -0,0 +1,20 @@ +local user = User.new("postgres-delete-user", "postgres-delete-user@usersen.com") +local team = Team.new("pg-delete-team", "Testing PostgresBranch deletion", "#postgres-delete") +team:addMember(user) +Helper.readK8sResources("k8s_resources/postgres_branch_delete") + +Test.gql("Active PostgresBranch cannot be marked for deletion", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[mutation { deletePostgresBranch(input: { + postgres: "orders", branch: "new", environmentName: "dev", teamSlug: "pg-delete-team" + }) { postgresBranchDeleted } }]] + t.check { errors = { { locations = NotNull(), path = { "deletePostgresBranch" }, message = Contains("is active and cannot be deleted") } }, data = Null } +end) + +Test.gql("Inactive PostgresBranch can be deleted", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[mutation { deletePostgresBranch(input: { + postgres: "orders", branch: "old", environmentName: "dev", teamSlug: "pg-delete-team" + }) { postgresBranchDeleted } }]] + t.check { data = { deletePostgresBranch = { postgresBranchDeleted = true } } } +end) diff --git a/integration_tests/postgres_branches.lua b/integration_tests/postgres_branches.lua new file mode 100644 index 000000000..3f2769551 --- /dev/null +++ b/integration_tests/postgres_branches.lua @@ -0,0 +1,61 @@ +local user = User.new("user", "user@usersen.com") +local nonMember = User.new("nonmember", "not@usersen.com") +local team = Team.new("someteamname", "purpose", "#slack_channel") +team:addMember(user) +Helper.readK8sResources("k8s_resources/postgres_branches") + +Test.gql("List concrete PostgresBranches with their logical owners", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[{ team(slug: "someteamname") { postgresBranches(orderBy: {field: NAME, direction: ASC}) { + nodes { name state postgres { name majorVersion activeBranch { name } } } + } } }]] + t.check { data = { team = { postgresBranches = { nodes = { + { name = "main", state = "AVAILABLE", postgres = { name = "another-db", majorVersion = "16", activeBranch = { name = "main" } } }, + { name = "main", state = "AVAILABLE", postgres = { name = "foobar", majorVersion = "17", activeBranch = { name = "main" } } }, + { name = "main", state = "AVAILABLE", postgres = { name = "with-audit", majorVersion = "16", activeBranch = { name = "main" } } }, + { name = "main", state = "AVAILABLE", postgres = { name = "without-audit", majorVersion = "15", activeBranch = { name = "main" } } }, + } } } } } +end) + +Test.gql("Retrieve logical Postgres and selected physical instance separately", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[{ team(slug:"someteamname") { environment(name:"dev") { + postgres(name:"foobar") { name activeBranch { name } majorVersion highAvailability resources { cpu memory diskSize } branch(name:"main") { name postgres { name } teamEnvironment { name } } branches { nodes { name } } } + } } }]] + t.check { data = { team = { environment = { + postgres = { name = "foobar", activeBranch = { name = "main" }, majorVersion = "17", highAvailability = false, resources = { cpu = "100m", memory = "2Gi", diskSize = "2Gi" }, branch = { name = "main", postgres = { name = "foobar" }, teamEnvironment = { name = "dev" } }, branches = { nodes = { { name = "main" } } } }, + } } } } +end) + +Test.gql("Physical instances may be filtered by observed state", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[{ team(slug:"someteamname") { postgresBranches(filter: {states:[AVAILABLE]}) { + nodes { name } facets { states { state count } } + } } }]] + t.check { data = { team = { postgresBranches = { + nodes = { { name = "main" }, { name = "main" }, { name = "main" }, { name = "main" } }, + facets = { states = { { state = "AVAILABLE", count = 4 } } }, + } } } } +end) + +Test.gql("A workload follows the active physical instance", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[{ team(slug:"someteamname") { environment(name:"dev") { + application(name:"app-with-postgres") { postgresBranches { nodes { name postgres { name } } } } + postgres(name:"foobar") { branch(name:"main") { workloads { nodes { __typename name } } } } + } } }]] + t.check { data = { team = { environment = { + application = { postgresBranches = { nodes = { { name = "main", postgres = { name = "foobar" } } } } }, + postgres = { branch = { workloads = { nodes = { + { __typename = "Application", name = "app-with-postgres" }, + { __typename = "Application", name = "app-with-postgres-2" }, + { __typename = "Job", name = "job-with-postgres" }, + } } } }, + } } } } +end) + +Test.gql("Delete a concrete PostgresBranch requires authorization", function(t) + t.addHeader("x-user-email", nonMember:email()) + t.query [[mutation { deletePostgresBranch(input:{postgres:"foobar",branch:"main",environmentName:"dev",teamSlug:"someteamname"}) { postgresBranchDeleted } }]] + t.check { errors = { { locations = NotNull(), message = Contains('postgres:delete'), path = { "deletePostgresBranch" } } }, data = Null } +end) diff --git a/integration_tests/postgres_instances.lua b/integration_tests/postgres_instances.lua deleted file mode 100644 index 122e58c54..000000000 --- a/integration_tests/postgres_instances.lua +++ /dev/null @@ -1,640 +0,0 @@ -local user = User.new("user", "user@usersen.com") -local nonMemberUser = User.new("nonmember", "other@user.com") - -local mainTeam = Team.new("someteamname", "purpose", "#slack_channel") -mainTeam:addMember(user) - -Helper.readK8sResources("k8s_resources/postgres_instances") - -Test.gql("List postgres instances for team", function(t) - t.addHeader("x-user-email", user:email()) - - t.query [[ - { - team(slug: "someteamname") { - postgresInstances(orderBy: {field: NAME, direction: ASC}) { - nodes { - name - majorVersion - resources { - cpu - memory - diskSize - } - audit { - enabled - } - } - } - } - } - ]] - - t.check { - data = { - team = { - postgresInstances = { - nodes = { - { - name = "another-db", - majorVersion = "16", - resources = { - cpu = "200m", - memory = "4G", - diskSize = "10Gi", - }, - audit = { - enabled = false, - }, - }, - { - name = "foobar", - majorVersion = "17", - resources = { - cpu = "100m", - memory = "2G", - diskSize = "2Gi", - }, - audit = { - enabled = false, - }, - }, - { - name = "with-audit", - majorVersion = "16", - resources = { - cpu = "100m", - memory = "2G", - diskSize = "5Gi", - }, - audit = { - enabled = true, - }, - }, - { - name = "without-audit", - majorVersion = "15", - resources = { - cpu = "100m", - memory = "1G", - diskSize = "3Gi", - }, - audit = { - enabled = false, - }, - }, - }, - }, - }, - }, - } -end) - -Test.gql("Get specific postgres instance from team environment", function(t) - t.addHeader("x-user-email", user:email()) - - t.query [[ - { - team(slug: "someteamname") { - environment(name: "dev") { - postgresInstance(name: "foobar") { - name - majorVersion - teamEnvironment { - name - } - } - } - } - } - ]] - - t.check { - data = { - team = { - environment = { - postgresInstance = { - name = "foobar", - majorVersion = "17", - teamEnvironment = { - name = "dev", - }, - }, - }, - }, - }, - } -end) - -Test.gql("List postgres instances with ordering by name", function(t) - t.addHeader("x-user-email", user:email()) - - t.query [[ - { - team(slug: "someteamname") { - postgresInstances(orderBy: {field: NAME, direction: ASC}) { - nodes { - name - } - } - } - } - ]] - - t.check { - data = { - team = { - postgresInstances = { - nodes = { - { - name = "another-db", - }, - { - name = "foobar", - }, - { - name = "with-audit", - }, - { - name = "without-audit", - }, - }, - }, - }, - }, - } -end) - -Test.gql("List postgres instances with ordering by environment", function(t) - t.addHeader("x-user-email", user:email()) - - t.query [[ - { - team(slug: "someteamname") { - postgresInstances(orderBy: {field: ENVIRONMENT, direction: DESC}, first: 10) { - nodes { - name - teamEnvironment { - name - } - } - } - } - } - ]] - - t.check { - data = { - team = { - postgresInstances = { - nodes = { - { - name = "another-db", - teamEnvironment = { - name = "dev", - }, - }, - { - name = "foobar", - teamEnvironment = { - name = "dev", - }, - }, - { - name = "with-audit", - teamEnvironment = { - name = "dev", - }, - }, - { - name = "without-audit", - teamEnvironment = { - name = "dev", - }, - }, - }, - }, - }, - }, - } -end) - -Test.gql("Get postgres instance from application", function(t) - t.addHeader("x-user-email", user:email()) - - t.query [[ - { - team(slug: "someteamname") { - environment(name: "dev") { - application(name: "app-with-postgres") { - name - postgresInstances { - nodes { - name - majorVersion - } - } - } - } - } - } - ]] - - t.check { - data = { - team = { - environment = { - application = { - name = "app-with-postgres", - postgresInstances = { - nodes = { - { - name = "foobar", - majorVersion = "17", - }, - }, - }, - }, - }, - }, - }, - } -end) - -Test.gql("Get workloads referencing postgres instance", function(t) - t.addHeader("x-user-email", user:email()) - - t.query [[ - { - team(slug: "someteamname") { - environment(name: "dev") { - postgresInstance(name: "foobar") { - name - workloads { - nodes { - __typename - name - } - } - } - } - } - } - ]] - - t.check { - data = { - team = { - environment = { - postgresInstance = { - name = "foobar", - workloads = { - nodes = { - { - __typename = "Application", - name = "app-with-postgres", - }, - { - __typename = "Application", - name = "app-with-postgres-2", - }, - { - __typename = "Job", - name = "job-with-postgres", - }, - }, - }, - }, - }, - }, - }, - } -end) - -Test.gql("Get empty postgres instances from application without postgres", function(t) - t.addHeader("x-user-email", user:email()) - - t.query [[ - { - team(slug: "someteamname") { - environment(name: "dev") { - application(name: "app-without-postgres") { - name - postgresInstances { - nodes { - name - } - } - } - } - } - } - ]] - - t.check { - data = { - team = { - environment = { - application = { - name = "app-without-postgres", - postgresInstances = { - nodes = {}, - }, - }, - }, - }, - }, - } -end) - -Test.gql("Access postgres instance fields", function(t) - t.addHeader("x-user-email", user:email()) - - t.query [[ - { - team(slug: "someteamname") { - postgresInstances(first: 1) { - nodes { - id - name - majorVersion - team { - slug - } - teamEnvironment { - name - } - resources { - cpu - memory - diskSize - } - } - } - } - } - ]] - - t.check { - data = { - team = { - postgresInstances = { - nodes = { - { - id = NotNull(), - name = NotNull(), - majorVersion = NotNull(), - team = { - slug = "someteamname", - }, - teamEnvironment = { - name = NotNull(), - }, - resources = { - cpu = NotNull(), - memory = NotNull(), - diskSize = NotNull(), - }, - }, - }, - }, - }, - }, - } -end) - -Test.gql("Postgres instance with audit logging enabled", function(t) - t.addHeader("x-user-email", user:email()) - - t.query [[ - { - team(slug: "someteamname") { - environment(name: "dev") { - postgresInstance(name: "with-audit") { - name - audit { - enabled - url - } - } - } - } - } - ]] - - t.check { - data = { - team = { - environment = { - postgresInstance = { - name = "with-audit", - audit = { - enabled = true, - url = Null, - }, - }, - }, - }, - }, - } -end) - -Test.gql("Postgres instance without audit logging", function(t) - t.addHeader("x-user-email", user:email()) - - t.query [[ - { - team(slug: "someteamname") { - environment(name: "dev") { - postgresInstance(name: "foobar") { - name - audit { - enabled - url - } - } - } - } - } - ]] - - t.check { - data = { - team = { - environment = { - postgresInstance = { - name = "foobar", - audit = { - enabled = false, - url = Null, - }, - }, - }, - }, - }, - } -end) - -Test.gql("Postgres instance with explicit audit disabled", function(t) - t.addHeader("x-user-email", user:email()) - - t.query [[ - { - team(slug: "someteamname") { - environment(name: "dev") { - postgresInstance(name: "without-audit") { - name - audit { - enabled - url - } - } - } - } - } - ]] - - t.check { - data = { - team = { - environment = { - postgresInstance = { - name = "without-audit", - audit = { - enabled = false, - url = Null, - }, - }, - }, - }, - }, - } -end) - -Test.gql("Delete Postgres in non-existing team", function(t) - t.addHeader("x-user-email", user:email()) - t.query [[ - mutation DeletePostgres { - deletePostgres( - input: { - name: "foobar" - environmentName: "dev" - teamSlug: "devteam" - } - ) { - postgresDeleted - } - } - ]] - - t.check { - errors = { - { - locations = NotNull(), - message = Contains("you need the \"postgres:delete\" authorization."), - path = { - "deletePostgres", - }, - }, - }, - data = Null, - } -end) - -Test.gql("Delete Postgres as non-team-member", function(t) - t.addHeader("x-user-email", nonMemberUser:email()) - t.query [[ - mutation DeletePostgres { - deletePostgres( - input: { - name: "foobar" - environmentName: "dev" - teamSlug: "someteamname" - } - ) { - postgresDeleted - } - } - ]] - - t.check { - errors = { - { - locations = NotNull(), - message = Contains("You are authenticated"), - path = { - "deletePostgres", - }, - }, - }, - data = Null, - } -end) - -Test.gql("Delete Postgres as team-member", function(t) - t.addHeader("x-user-email", user:email()) - t.query [[ - mutation DeletePostgres { - deletePostgres( - input: { - name: "foobar" - environmentName: "dev" - teamSlug: "someteamname" - } - ) { - postgresDeleted - } - } - ]] - - t.check { - data = { - deletePostgres = { - postgresDeleted = true, - }, - }, - } -end) - -Test.gql("Verify activity log for postgres delete", function(t) - t.addHeader("x-user-email", user:email()) - t.query(string.format([[ - { - team(slug: "%s") { - activityLog(first: 50, filter: { activityTypes: [POSTGRES_DELETED] }) { - nodes { - __typename - message - actor - createdAt - resourceType - resourceName - environmentName - teamSlug - } - } - } - } - ]], mainTeam:slug())) - - t.check { - data = { - team = { - activityLog = { - nodes = { - { - __typename = "PostgresDeletedActivityLogEntry", - message = "Deleted Postgres", - actor = user:email(), - createdAt = NotNull(), - resourceType = "POSTGRES", - resourceName = "foobar", - environmentName = "dev", - teamSlug = mainTeam:slug(), - }, - }, - }, - }, - }, - } -end) diff --git a/integration_tests/postgres_workloads.lua b/integration_tests/postgres_workloads.lua new file mode 100644 index 000000000..57a485744 --- /dev/null +++ b/integration_tests/postgres_workloads.lua @@ -0,0 +1,36 @@ +local user = User.new("pg-workload-user", "pg-workload-user@usersen.com") +local team = Team.new("postgres-workload-team", "Testing workload Postgres uses", "#postgres-workloads") +team:addMember(user) +Helper.readK8sResources("k8s_resources/postgres_workloads") + +Test.gql("Application and job resolve every uses.postgres entry to its selected instance", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[{ team(slug: "postgres-workload-team") { environment(name: "dev") { + application(name: "consumer") { postgresBranches { nodes { name postgres { name } } } } + job(name: "scheduled-reader") { postgresBranches { nodes { name postgres { name } } } } + } } }]] + local instances = { + { name = "green", postgres = { name = "orders" } }, + { name = "recovered", postgres = { name = "reports" } }, + } + t.check { data = { team = { environment = { + application = { postgresBranches = { nodes = instances } }, + job = { postgresBranches = { nodes = instances } }, + } } } } +end) + +Test.gql("PostgresBranch workloads reference its Postgres through uses.postgres", function(t) + t.addHeader("x-user-email", user:email()) + t.query [[{ team(slug: "postgres-workload-team") { environment(name: "dev") { + postgres(name: "orders") { branch(name: "green") { workloads { nodes { __typename name } } } } + other: postgres(name: "reports") { branch(name: "recovered") { workloads { nodes { __typename name } } } } + } } }]] + local workloads = { + { __typename = "Application", name = "consumer" }, + { __typename = "Job", name = "scheduled-reader" }, + } + t.check { data = { team = { environment = { + postgres = { branch = { workloads = { nodes = workloads } } }, + other = { branch = { workloads = { nodes = workloads } } }, + } } } } +end) diff --git a/internal/apply/whitelist.go b/internal/apply/whitelist.go index de1e59af0..23acf25e7 100644 --- a/internal/apply/whitelist.go +++ b/internal/apply/whitelist.go @@ -63,8 +63,8 @@ var allowedResources = map[AllowedResource]schema.GroupVersionResource{ }, // Postgres (NAIS) - {APIVersion: "data.nais.io/v1", Kind: "Postgres"}: { - Group: "data.nais.io", Version: "v1", Resource: "postgres", + {APIVersion: "nais.io/v1", Kind: "Postgres"}: { + Group: "nais.io", Version: "v1", Resource: "postgres", }, // IAM (Config Connector) diff --git a/internal/cmd/api/api.go b/internal/cmd/api/api.go index 2b5bec209..e33861cea 100644 --- a/internal/cmd/api/api.go +++ b/internal/cmd/api/api.go @@ -393,7 +393,7 @@ func run(ctx context.Context, cfg *Config, log logrus.FieldLogger) error { }) wg.Go(func() error { - if err := grpc.Run(ctx, cfg.GRPCListenAddress, pool, watchers.SqlDatabaseWatcher, watchers.ZalandoPostgresWatcher, log.WithField("subsystem", "grpc")); err != nil { + if err := grpc.Run(ctx, cfg.GRPCListenAddress, pool, watchers.SqlDatabaseWatcher, watchers.PostgresBranchWatcher, log.WithField("subsystem", "grpc")); err != nil { log.WithError(err).Errorf("error in GRPC server") return err } diff --git a/internal/cmd/api/http.go b/internal/cmd/api/http.go index bb41fe3b9..b75259623 100644 --- a/internal/cmd/api/http.go +++ b/internal/cmd/api/http.go @@ -232,7 +232,7 @@ func ConfigureGraph( kafkatopic.AddSearch(searcher, watchers.KafkaTopicWatcher) opensearch.AddSearch(searcher, watchers.OpenSearchWatcher) sqlinstance.AddSearchSQLInstance(searcher, watchers.SqlInstanceWatcher) - postgres.AddSearchZalandoPostgres(searcher, watchers.ZalandoPostgresWatcher) + postgres.AddSearchPostgresBranch(searcher, watchers.PostgresBranchWatcher) valkey.AddSearch(searcher, watchers.ValkeyWatcher) team.AddSearch(searcher, pool, notifier, log.WithField("subsystem", "team_search")) return nil @@ -355,7 +355,7 @@ func ConfigureGraph( ctx = alerts.NewLoaderContext(ctx, prometheusClient, log) ctx = metrics.NewLoaderContext(ctx, prometheusClient, log) ctx = sqlinstance.NewLoaderContext(ctx, sqlAdminService, watchers.SqlDatabaseWatcher, watchers.SqlInstanceWatcher, auditLogProjectID, auditLogLocation) - ctx = postgres.NewLoaderContext(ctx, watchers.ZalandoPostgresWatcher, auditLogProjectID, auditLogLocation) + ctx = postgres.NewLoaderContext(ctx, watchers.PostgresBranchWatcher, auditLogProjectID, auditLogLocation, tenantName, dynamicClients) ctx = aivencredentials.NewClientContext(ctx, dynamicClients, log) ctx = database.NewLoaderContext(ctx, pool) ctx = issue.NewContext(ctx, pool) diff --git a/internal/graph/gengql/activitylog.generated.go b/internal/graph/gengql/activitylog.generated.go index 8152a69b2..dbdf65c8c 100644 --- a/internal/graph/gengql/activitylog.generated.go +++ b/internal/graph/gengql/activitylog.generated.go @@ -789,6 +789,20 @@ func (ec *executionContext) _ActivityLogEntry(ctx context.Context, sel ast.Selec return graphql.Null } return ec._ReconcilerConfiguredActivityLogEntry(ctx, sel, obj) + case postgres.PostgresPersonalAccessCreatedActivityLogEntry: + return ec._PostgresPersonalAccessCreatedActivityLogEntry(ctx, sel, &obj) + case *postgres.PostgresPersonalAccessCreatedActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._PostgresPersonalAccessCreatedActivityLogEntry(ctx, sel, obj) + case postgres.PostgresPersonalAccessConnectionActivityLogEntry: + return ec._PostgresPersonalAccessConnectionActivityLogEntry(ctx, sel, &obj) + case *postgres.PostgresPersonalAccessConnectionActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._PostgresPersonalAccessConnectionActivityLogEntry(ctx, sel, obj) case postgres.PostgresGrantAccessActivityLogEntry: return ec._PostgresGrantAccessActivityLogEntry(ctx, sel, &obj) case *postgres.PostgresGrantAccessActivityLogEntry: diff --git a/internal/graph/gengql/applications.generated.go b/internal/graph/gengql/applications.generated.go index dcbe7a6e2..f5760a559 100644 --- a/internal/graph/gengql/applications.generated.go +++ b/internal/graph/gengql/applications.generated.go @@ -68,7 +68,7 @@ type ApplicationResolver interface { LogDestinations(ctx context.Context, obj *application.Application) ([]logging.LogDestination, error) NetworkPolicy(ctx context.Context, obj *application.Application) (*netpol.NetworkPolicy, error) OpenSearch(ctx context.Context, obj *application.Application) (*opensearch.OpenSearch, error) - PostgresInstances(ctx context.Context, obj *application.Application, orderBy *postgres.PostgresInstanceOrder) (*pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter], error) + PostgresBranches(ctx context.Context, obj *application.Application, orderBy *postgres.PostgresBranchOrder) (*pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter], error) Secrets(ctx context.Context, obj *application.Application, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) (*pagination.FacetableConnection[*secret.Secret, *secret.SecretFilter], error) ServiceAccount(ctx context.Context, obj *application.Application) (*serviceaccount.ServiceAccount, error) SQLInstances(ctx context.Context, obj *application.Application, orderBy *sqlinstance.SQLInstanceOrder) (*pagination.Connection[*sqlinstance.SQLInstance], error) @@ -384,12 +384,12 @@ func (ec *executionContext) field_Application_kafkaTopicAcls_args(ctx context.Co return args, nil } -func (ec *executionContext) field_Application_postgresInstances_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { +func (ec *executionContext) field_Application_postgresBranches_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} arg0, err := graphql.ProcessArgField(ctx, rawArgs, "orderBy", - func(ctx context.Context, v any) (*postgres.PostgresInstanceOrder, error) { - return ec.unmarshalOPostgresInstanceOrder2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceOrder(ctx, v) + func(ctx context.Context, v any) (*postgres.PostgresBranchOrder, error) { + return ec.unmarshalOPostgresBranchOrder2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchOrder(ctx, v) }) if err != nil { return nil, err @@ -1369,34 +1369,34 @@ func (ec *executionContext) fieldContext_Application_openSearch(_ context.Contex return fc, nil } -func (ec *executionContext) _Application_postgresInstances(ctx context.Context, field graphql.CollectedField, obj *application.Application) (ret graphql.Marshaler) { +func (ec *executionContext) _Application_postgresBranches(ctx context.Context, field graphql.CollectedField, obj *application.Application) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Application_postgresInstances(ctx, field) + return ec.fieldContext_Application_postgresBranches(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Application().PostgresInstances(ctx, obj, fc.Args["orderBy"].(*postgres.PostgresInstanceOrder)) + return ec.Resolvers.Application().PostgresBranches(ctx, obj, fc.Args["orderBy"].(*postgres.PostgresBranchOrder)) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter]) graphql.Marshaler { - return ec.marshalNPostgresInstanceConnection2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐFacetableConnection(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) graphql.Marshaler { + return ec.marshalNPostgresBranchConnection2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐFacetableConnection(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Application_postgresInstances(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Application_postgresBranches(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Application", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresInstanceConnection(ctx, field) + return ec.childFields_PostgresBranchConnection(ctx, field) }, } defer func() { @@ -1406,7 +1406,7 @@ func (ec *executionContext) fieldContext_Application_postgresInstances(ctx conte } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Application_postgresInstances_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Application_postgresBranches_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -5462,7 +5462,7 @@ func (ec *executionContext) _Application(ctx context.Context, sel ast.SelectionS } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "postgresInstances": + case "postgresBranches": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -5471,7 +5471,7 @@ func (ec *executionContext) _Application(ctx context.Context, sel ast.SelectionS ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Application_postgresInstances(ctx, field, obj) + res = ec._Application_postgresBranches(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } diff --git a/internal/graph/gengql/complexity.go b/internal/graph/gengql/complexity.go index 9031a405c..79504cdc0 100644 --- a/internal/graph/gengql/complexity.go +++ b/internal/graph/gengql/complexity.go @@ -121,7 +121,10 @@ func NewComplexityRoot() ComplexityRoot { c.OpenSearchMaintenance.Updates = func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) int { return cursorComplexity(first, last) * childComplexity } - c.PostgresInstance.Workloads = func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) int { + c.Postgres.Branches = func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *postgres.PostgresBranchOrder) int { + return cursorComplexity(first, last) * childComplexity + } + c.PostgresBranch.Workloads = func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) int { return cursorComplexity(first, last) * childComplexity } c.Query.ActivityLog = func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, filter *activitylog.ActivityLogFilter) int { @@ -229,7 +232,7 @@ func NewComplexityRoot() ComplexityRoot { c.Team.OpenSearches = func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *opensearch.OpenSearchOrder, filter *opensearch.OpenSearchFilter) int { return cursorComplexity(first, last) * childComplexity } - c.Team.PostgresInstances = func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *postgres.PostgresInstanceOrder, filter *postgres.PostgresInstanceFilter) int { + c.Team.PostgresBranches = func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *postgres.PostgresBranchOrder, filter *postgres.PostgresBranchFilter) int { return cursorComplexity(first, last) * childComplexity } c.Team.Repositories = func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *repository.RepositoryOrder, filter *repository.TeamRepositoryFilter) int { diff --git a/internal/graph/gengql/jobs.generated.go b/internal/graph/gengql/jobs.generated.go index b78204420..c39912a92 100644 --- a/internal/graph/gengql/jobs.generated.go +++ b/internal/graph/gengql/jobs.generated.go @@ -70,7 +70,7 @@ type JobResolver interface { LogDestinations(ctx context.Context, obj *job.Job) ([]logging.LogDestination, error) NetworkPolicy(ctx context.Context, obj *job.Job) (*netpol.NetworkPolicy, error) OpenSearch(ctx context.Context, obj *job.Job) (*opensearch.OpenSearch, error) - PostgresInstances(ctx context.Context, obj *job.Job, orderBy *postgres.PostgresInstanceOrder) (*pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter], error) + PostgresBranches(ctx context.Context, obj *job.Job, orderBy *postgres.PostgresBranchOrder) (*pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter], error) Secrets(ctx context.Context, obj *job.Job, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) (*pagination.FacetableConnection[*secret.Secret, *secret.SecretFilter], error) ServiceAccount(ctx context.Context, obj *job.Job) (*serviceaccount.ServiceAccount, error) SQLInstances(ctx context.Context, obj *job.Job, orderBy *sqlinstance.SQLInstanceOrder) (*pagination.Connection[*sqlinstance.SQLInstance], error) @@ -366,12 +366,12 @@ func (ec *executionContext) field_Job_kafkaTopicAcls_args(ctx context.Context, r return args, nil } -func (ec *executionContext) field_Job_postgresInstances_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { +func (ec *executionContext) field_Job_postgresBranches_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} arg0, err := graphql.ProcessArgField(ctx, rawArgs, "orderBy", - func(ctx context.Context, v any) (*postgres.PostgresInstanceOrder, error) { - return ec.unmarshalOPostgresInstanceOrder2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceOrder(ctx, v) + func(ctx context.Context, v any) (*postgres.PostgresBranchOrder, error) { + return ec.unmarshalOPostgresBranchOrder2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchOrder(ctx, v) }) if err != nil { return nil, err @@ -1435,34 +1435,34 @@ func (ec *executionContext) fieldContext_Job_openSearch(_ context.Context, field return fc, nil } -func (ec *executionContext) _Job_postgresInstances(ctx context.Context, field graphql.CollectedField, obj *job.Job) (ret graphql.Marshaler) { +func (ec *executionContext) _Job_postgresBranches(ctx context.Context, field graphql.CollectedField, obj *job.Job) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Job_postgresInstances(ctx, field) + return ec.fieldContext_Job_postgresBranches(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Job().PostgresInstances(ctx, obj, fc.Args["orderBy"].(*postgres.PostgresInstanceOrder)) + return ec.Resolvers.Job().PostgresBranches(ctx, obj, fc.Args["orderBy"].(*postgres.PostgresBranchOrder)) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter]) graphql.Marshaler { - return ec.marshalNPostgresInstanceConnection2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐFacetableConnection(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) graphql.Marshaler { + return ec.marshalNPostgresBranchConnection2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐFacetableConnection(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Job_postgresInstances(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Job_postgresBranches(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Job", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresInstanceConnection(ctx, field) + return ec.childFields_PostgresBranchConnection(ctx, field) }, } defer func() { @@ -1472,7 +1472,7 @@ func (ec *executionContext) fieldContext_Job_postgresInstances(ctx context.Conte } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Job_postgresInstances_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Job_postgresBranches_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -5488,7 +5488,7 @@ func (ec *executionContext) _Job(ctx context.Context, sel ast.SelectionSet, obj } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "postgresInstances": + case "postgresBranches": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -5497,7 +5497,7 @@ func (ec *executionContext) _Job(ctx context.Context, sel ast.SelectionSet, obj ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Job_postgresInstances(ctx, field, obj) + res = ec._Job_postgresBranches(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } diff --git a/internal/graph/gengql/persistence.generated.go b/internal/graph/gengql/persistence.generated.go index e8ca691e7..273558efc 100644 --- a/internal/graph/gengql/persistence.generated.go +++ b/internal/graph/gengql/persistence.generated.go @@ -72,13 +72,13 @@ func (ec *executionContext) _Persistence(ctx context.Context, sel ast.SelectionS return graphql.Null } return ec._SqlDatabase(ctx, sel, obj) - case postgres.PostgresInstance: - return ec._PostgresInstance(ctx, sel, &obj) - case *postgres.PostgresInstance: + case postgres.PostgresBranch: + return ec._PostgresBranch(ctx, sel, &obj) + case *postgres.PostgresBranch: if obj == nil { return graphql.Null } - return ec._PostgresInstance(ctx, sel, obj) + return ec._PostgresBranch(ctx, sel, obj) case kafkatopic.KafkaTopic: return ec._KafkaTopic(ctx, sel, &obj) case *kafkatopic.KafkaTopic: diff --git a/internal/graph/gengql/postgres.generated.go b/internal/graph/gengql/postgres.generated.go index 7d76a743f..c55efd78d 100644 --- a/internal/graph/gengql/postgres.generated.go +++ b/internal/graph/gengql/postgres.generated.go @@ -25,23 +25,85 @@ import ( // region ************************** generated!.gotpl ************************** -type PostgresInstanceResolver interface { - Team(ctx context.Context, obj *postgres.PostgresInstance) (*team.Team, error) - TeamEnvironment(ctx context.Context, obj *postgres.PostgresInstance) (*team.TeamEnvironment, error) - Workloads(ctx context.Context, obj *postgres.PostgresInstance, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) (*pagination.Connection[workload.Workload], error) +type PostgresResolver interface { + ActiveBranch(ctx context.Context, obj *postgres.Postgres) (*postgres.PostgresBranch, error) + Branch(ctx context.Context, obj *postgres.Postgres, name string) (*postgres.PostgresBranch, error) + Branches(ctx context.Context, obj *postgres.Postgres, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *postgres.PostgresBranchOrder) (*pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter], error) } -type PostgresInstanceAuditResolver interface { - URL(ctx context.Context, obj *postgres.PostgresInstanceAudit) (*string, error) +type PostgresAccessResolver interface { + Team(ctx context.Context, obj *postgres.PostgresAccess) (*team.Team, error) + TeamEnvironment(ctx context.Context, obj *postgres.PostgresAccess) (*team.TeamEnvironment, error) + PostgresBranch(ctx context.Context, obj *postgres.PostgresAccess) (*postgres.PostgresBranch, error) + + Connection(ctx context.Context, obj *postgres.PostgresAccess) (*postgres.PostgresAccessConnectionDetails, error) +} +type PostgresBranchResolver interface { + Team(ctx context.Context, obj *postgres.PostgresBranch) (*team.Team, error) + TeamEnvironment(ctx context.Context, obj *postgres.PostgresBranch) (*team.TeamEnvironment, error) + Postgres(ctx context.Context, obj *postgres.PostgresBranch) (*postgres.Postgres, error) + Workloads(ctx context.Context, obj *postgres.PostgresBranch, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) (*pagination.Connection[workload.Workload], error) } -type PostgresInstanceConnectionResolver interface { - Facets(ctx context.Context, obj *pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter]) (*postgres.PostgresInstanceFacets, error) +type PostgresBranchConnectionResolver interface { + Facets(ctx context.Context, obj *pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) (*postgres.PostgresBranchFacets, error) } // endregion ************************** generated!.gotpl ************************** // region ***************************** args.gotpl ***************************** -func (ec *executionContext) field_PostgresInstance_workloads_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { +func (ec *executionContext) field_PostgresBranch_workloads_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "first", + func(ctx context.Context, v any) (*int, error) { + return ec.unmarshalOInt2ᚖint(ctx, v) + }) + if err != nil { + return nil, err + } + args["first"] = arg0 + arg1, err := graphql.ProcessArgField(ctx, rawArgs, "after", + func(ctx context.Context, v any) (*pagination.Cursor, error) { + return ec.unmarshalOCursor2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐCursor(ctx, v) + }) + if err != nil { + return nil, err + } + args["after"] = arg1 + arg2, err := graphql.ProcessArgField(ctx, rawArgs, "last", + func(ctx context.Context, v any) (*int, error) { + return ec.unmarshalOInt2ᚖint(ctx, v) + }) + if err != nil { + return nil, err + } + args["last"] = arg2 + arg3, err := graphql.ProcessArgField(ctx, rawArgs, "before", + func(ctx context.Context, v any) (*pagination.Cursor, error) { + return ec.unmarshalOCursor2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐCursor(ctx, v) + }) + if err != nil { + return nil, err + } + args["before"] = arg3 + return args, nil +} + +func (ec *executionContext) field_Postgres_branch_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "name", + func(ctx context.Context, v any) (string, error) { + return ec.unmarshalNString2string(ctx, v) + }) + if err != nil { + return nil, err + } + args["name"] = arg0 + return args, nil +} + +func (ec *executionContext) field_Postgres_branches_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} arg0, err := graphql.ProcessArgField(ctx, rawArgs, "first", @@ -76,6 +138,14 @@ func (ec *executionContext) field_PostgresInstance_workloads_args(ctx context.Co return nil, err } args["before"] = arg3 + arg4, err := graphql.ProcessArgField(ctx, rawArgs, "orderBy", + func(ctx context.Context, v any) (*postgres.PostgresBranchOrder, error) { + return ec.unmarshalOPostgresBranchOrder2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchOrder(ctx, v) + }) + if err != nil { + return nil, err + } + args["orderBy"] = arg4 return args, nil } @@ -87,16 +157,62 @@ func (ec *executionContext) field_PostgresInstance_workloads_args(ctx context.Co // region **************************** field.gotpl ***************************** -func (ec *executionContext) _DeletePostgresPayload_postgresDeleted(ctx context.Context, field graphql.CollectedField, obj *postgres.DeletePostgresPayload) (ret graphql.Marshaler) { +func (ec *executionContext) _CreatePostgresAccessPayload_name(ctx context.Context, field graphql.CollectedField, obj *postgres.CreatePostgresAccessPayload) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CreatePostgresAccessPayload_name(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Name, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CreatePostgresAccessPayload_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CreatePostgresAccessPayload", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _CreatePostgresAccessPayload_expiresAt(ctx context.Context, field graphql.CollectedField, obj *postgres.CreatePostgresAccessPayload) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_CreatePostgresAccessPayload_expiresAt(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ExpiresAt, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v time.Time) graphql.Marshaler { + return ec.marshalNTime2timeᚐTime(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_CreatePostgresAccessPayload_expiresAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("CreatePostgresAccessPayload", field, false, false, errors.New("field of type Time does not have child fields")) +} + +func (ec *executionContext) _DeletePostgresBranchPayload_postgresBranchDeleted(ctx context.Context, field graphql.CollectedField, obj *postgres.DeletePostgresBranchPayload) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_DeletePostgresPayload_postgresDeleted(ctx, field) + return ec.fieldContext_DeletePostgresBranchPayload_postgresBranchDeleted(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.PostgresDeleted, nil + return obj.PostgresBranchDeleted, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v *bool) graphql.Marshaler { @@ -106,8 +222,8 @@ func (ec *executionContext) _DeletePostgresPayload_postgresDeleted(ctx context.C false, ) } -func (ec *executionContext) fieldContext_DeletePostgresPayload_postgresDeleted(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("DeletePostgresPayload", field, false, false, errors.New("field of type Boolean does not have child fields")) +func (ec *executionContext) fieldContext_DeletePostgresBranchPayload_postgresBranchDeleted(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("DeletePostgresBranchPayload", field, false, false, errors.New("field of type Boolean does not have child fields")) } func (ec *executionContext) _GrantPostgresAccessPayload_error(ctx context.Context, field graphql.CollectedField, obj *postgres.GrantPostgresAccessPayload) (ret graphql.Marshaler) { @@ -133,13 +249,13 @@ func (ec *executionContext) fieldContext_GrantPostgresAccessPayload_error(_ cont return graphql.NewScalarFieldContext("GrantPostgresAccessPayload", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresDeletedActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _Postgres_id(ctx context.Context, field graphql.CollectedField, obj *postgres.Postgres) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresDeletedActivityLogEntry_id(ctx, field) + return ec.fieldContext_Postgres_id(ctx, field) }, func(ctx context.Context) (any, error) { return obj.ID(), nil @@ -152,20 +268,20 @@ func (ec *executionContext) _PostgresDeletedActivityLogEntry_id(ctx context.Cont true, ) } -func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, true, false, errors.New("field of type ID does not have child fields")) +func (ec *executionContext) fieldContext_Postgres_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("Postgres", field, true, false, errors.New("field of type ID does not have child fields")) } -func (ec *executionContext) _PostgresDeletedActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _Postgres_name(ctx context.Context, field graphql.CollectedField, obj *postgres.Postgres) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresDeletedActivityLogEntry_actor(ctx, field) + return ec.fieldContext_Postgres_name(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Actor, nil + return obj.Name, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { @@ -175,187 +291,247 @@ func (ec *executionContext) _PostgresDeletedActivityLogEntry_actor(ctx context.C true, ) } -func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_Postgres_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("Postgres", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresDeletedActivityLogEntry_gitHubActorClaims(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _Postgres_majorVersion(ctx context.Context, field graphql.CollectedField, obj *postgres.Postgres) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresDeletedActivityLogEntry_gitHubActorClaims(ctx, field) + return ec.fieldContext_Postgres_majorVersion(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.GitHubActorClaims, nil + return obj.MajorVersion, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *github.GitHubActorClaims) graphql.Marshaler { - return ec.marshalOGitHubActorClaims2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋauthᚋmiddlewareᚋgithubᚐGitHubActorClaims(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) }, true, - false, + true, ) } -func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_gitHubActorClaims(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "PostgresDeletedActivityLogEntry", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_GitHubActorClaims(ctx, field) - }, - } - return fc, nil +func (ec *executionContext) fieldContext_Postgres_majorVersion(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("Postgres", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresDeletedActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _Postgres_highAvailability(ctx context.Context, field graphql.CollectedField, obj *postgres.Postgres) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresDeletedActivityLogEntry_createdAt(ctx, field) + return ec.fieldContext_Postgres_highAvailability(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.CreatedAt, nil + return obj.HighAvailability, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v time.Time) graphql.Marshaler { - return ec.marshalNTime2timeᚐTime(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { + return ec.marshalNBoolean2bool(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type Time does not have child fields")) +func (ec *executionContext) fieldContext_Postgres_highAvailability(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("Postgres", field, false, false, errors.New("field of type Boolean does not have child fields")) } -func (ec *executionContext) _PostgresDeletedActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _Postgres_resources(ctx context.Context, field graphql.CollectedField, obj *postgres.Postgres) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresDeletedActivityLogEntry_message(ctx, field) + return ec.fieldContext_Postgres_resources(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Message, nil + return obj.Resources, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { - return ec.marshalNString2string(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v postgres.PostgresResources) graphql.Marshaler { + return ec.marshalNPostgresResources2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresResources(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_Postgres_resources(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Postgres", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_PostgresResources(ctx, field) + }, + } + return fc, nil } -func (ec *executionContext) _PostgresDeletedActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _Postgres_activeBranch(ctx context.Context, field graphql.CollectedField, obj *postgres.Postgres) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresDeletedActivityLogEntry_resourceType(ctx, field) + return ec.fieldContext_Postgres_activeBranch(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.ResourceType, nil + return ec.Resolvers.Postgres().ActiveBranch(ctx, obj) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v activitylog.ActivityLogEntryResourceType) graphql.Marshaler { - return ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresBranch) graphql.Marshaler { + return ec.marshalOPostgresBranch2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranch(ctx, selections, v) }, true, - true, + false, ) } -func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type ActivityLogEntryResourceType does not have child fields")) +func (ec *executionContext) fieldContext_Postgres_activeBranch(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Postgres", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_PostgresBranch(ctx, field) + }, + } + return fc, nil } -func (ec *executionContext) _PostgresDeletedActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _Postgres_branch(ctx context.Context, field graphql.CollectedField, obj *postgres.Postgres) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresDeletedActivityLogEntry_resourceName(ctx, field) + return ec.fieldContext_Postgres_branch(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.ResourceName, nil + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Postgres().Branch(ctx, obj, fc.Args["name"].(string)) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { - return ec.marshalNString2string(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresBranch) graphql.Marshaler { + return ec.marshalNPostgresBranch2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranch(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_Postgres_branch(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Postgres", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_PostgresBranch(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Postgres_branch_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil } -func (ec *executionContext) _PostgresDeletedActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _Postgres_branches(ctx context.Context, field graphql.CollectedField, obj *postgres.Postgres) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresDeletedActivityLogEntry_teamSlug(ctx, field) + return ec.fieldContext_Postgres_branches(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.TeamSlug, nil + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Postgres().Branches(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*pagination.Cursor), fc.Args["last"].(*int), fc.Args["before"].(*pagination.Cursor), fc.Args["orderBy"].(*postgres.PostgresBranchOrder)) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *slug.Slug) graphql.Marshaler { - return ec.marshalNSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) graphql.Marshaler { + return ec.marshalNPostgresBranchConnection2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐFacetableConnection(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type Slug does not have child fields")) +func (ec *executionContext) fieldContext_Postgres_branches(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Postgres", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_PostgresBranchConnection(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Postgres_branches_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil } -func (ec *executionContext) _PostgresDeletedActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _Postgres_labels(ctx context.Context, field graphql.CollectedField, obj *postgres.Postgres) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresDeletedActivityLogEntry_environmentName(ctx, field) + return ec.fieldContext_Postgres_labels(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.EnvironmentName, nil + return obj.Labels, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { - return ec.marshalOString2ᚖstring(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v []*model.ResourceLabel) graphql.Marshaler { + return ec.marshalNResourceLabel2ᚕᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋmodelᚐResourceLabelᚄ(ctx, selections, v) }, true, - false, + true, ) } -func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_Postgres_labels(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Postgres", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_ResourceLabel(ctx, field) + }, + } + return fc, nil } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccess_id(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccess) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresGrantAccessActivityLogEntry_id(ctx, field) + return ec.fieldContext_PostgresAccess_id(ctx, field) }, func(ctx context.Context) (any, error) { return obj.ID(), nil @@ -368,20 +544,20 @@ func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_id(ctx context. true, ) } -func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, true, false, errors.New("field of type ID does not have child fields")) +func (ec *executionContext) fieldContext_PostgresAccess_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccess", field, true, false, errors.New("field of type ID does not have child fields")) } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccess_name(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccess) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresGrantAccessActivityLogEntry_actor(ctx, field) + return ec.fieldContext_PostgresAccess_name(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Actor, nil + return obj.Name, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { @@ -391,167 +567,185 @@ func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_actor(ctx conte true, ) } -func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_PostgresAccess_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccess", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_gitHubActorClaims(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccess_team(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccess) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresGrantAccessActivityLogEntry_gitHubActorClaims(ctx, field) + return ec.fieldContext_PostgresAccess_team(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.GitHubActorClaims, nil + return ec.Resolvers.PostgresAccess().Team(ctx, obj) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *github.GitHubActorClaims) graphql.Marshaler { - return ec.marshalOGitHubActorClaims2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋauthᚋmiddlewareᚋgithubᚐGitHubActorClaims(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *team.Team) graphql.Marshaler { + return ec.marshalNTeam2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋteamᚐTeam(ctx, selections, v) }, true, - false, + true, ) } -func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_gitHubActorClaims(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresAccess_team(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresGrantAccessActivityLogEntry", + Object: "PostgresAccess", Field: field, - IsMethod: false, - IsResolver: false, + IsMethod: true, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_GitHubActorClaims(ctx, field) + return ec.childFields_Team(ctx, field) }, } return fc, nil } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccess_teamEnvironment(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccess) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresGrantAccessActivityLogEntry_createdAt(ctx, field) + return ec.fieldContext_PostgresAccess_teamEnvironment(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.CreatedAt, nil + return ec.Resolvers.PostgresAccess().TeamEnvironment(ctx, obj) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v time.Time) graphql.Marshaler { - return ec.marshalNTime2timeᚐTime(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *team.TeamEnvironment) graphql.Marshaler { + return ec.marshalNTeamEnvironment2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋteamᚐTeamEnvironment(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type Time does not have child fields")) +func (ec *executionContext) fieldContext_PostgresAccess_teamEnvironment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostgresAccess", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_TeamEnvironment(ctx, field) + }, + } + return fc, nil } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccess_postgresBranch(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccess) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresGrantAccessActivityLogEntry_message(ctx, field) + return ec.fieldContext_PostgresAccess_postgresBranch(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Message, nil + return ec.Resolvers.PostgresAccess().PostgresBranch(ctx, obj) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { - return ec.marshalNString2string(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresBranch) graphql.Marshaler { + return ec.marshalNPostgresBranch2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranch(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_PostgresAccess_postgresBranch(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostgresAccess", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_PostgresBranch(ctx, field) + }, + } + return fc, nil } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccess_accessLevel(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccess) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresGrantAccessActivityLogEntry_resourceType(ctx, field) + return ec.fieldContext_PostgresAccess_accessLevel(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.ResourceType, nil + return obj.AccessLevel, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v activitylog.ActivityLogEntryResourceType) graphql.Marshaler { - return ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v postgres.PostgresAccessLevel) graphql.Marshaler { + return ec.marshalNPostgresAccessLevel2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccessLevel(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type ActivityLogEntryResourceType does not have child fields")) +func (ec *executionContext) fieldContext_PostgresAccess_accessLevel(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccess", field, false, false, errors.New("field of type PostgresAccessLevel does not have child fields")) } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccess_expiresAt(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccess) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresGrantAccessActivityLogEntry_resourceName(ctx, field) + return ec.fieldContext_PostgresAccess_expiresAt(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.ResourceName, nil + return obj.ExpiresAt, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { - return ec.marshalNString2string(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v time.Time) graphql.Marshaler { + return ec.marshalNTime2timeᚐTime(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_PostgresAccess_expiresAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccess", field, false, false, errors.New("field of type Time does not have child fields")) } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccess_state(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccess) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresGrantAccessActivityLogEntry_teamSlug(ctx, field) + return ec.fieldContext_PostgresAccess_state(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.TeamSlug, nil + return obj.State, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *slug.Slug) graphql.Marshaler { - return ec.marshalNSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v postgres.PostgresAccessState) graphql.Marshaler { + return ec.marshalNPostgresAccessState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccessState(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type Slug does not have child fields")) +func (ec *executionContext) fieldContext_PostgresAccess_state(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccess", field, false, false, errors.New("field of type PostgresAccessState does not have child fields")) } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccess_message(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccess) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresGrantAccessActivityLogEntry_environmentName(ctx, field) + return ec.fieldContext_PostgresAccess_message(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.EnvironmentName, nil + return obj.Message, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { @@ -561,52 +755,75 @@ func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_environmentName false, ) } -func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_PostgresAccess_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccess", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_data(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccess_relayAccess(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccess) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresGrantAccessActivityLogEntry_data(ctx, field) + return ec.fieldContext_PostgresAccess_relayAccess(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Data, nil + return obj.RelayAccess, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresGrantAccessActivityLogEntryData) graphql.Marshaler { - return ec.marshalNPostgresGrantAccessActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresGrantAccessActivityLogEntryData(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) }, true, + false, + ) +} +func (ec *executionContext) fieldContext_PostgresAccess_relayAccess(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccess", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresAccess_connection(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccess) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresAccess_connection(ctx, field) + }, + func(ctx context.Context) (any, error) { + return ec.Resolvers.PostgresAccess().Connection(ctx, obj) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresAccessConnectionDetails) graphql.Marshaler { + return ec.marshalOPostgresAccessConnectionDetails2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccessConnectionDetails(ctx, selections, v) + }, true, + false, ) } -func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresAccess_connection(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresGrantAccessActivityLogEntry", + Object: "PostgresAccess", Field: field, - IsMethod: false, - IsResolver: false, + IsMethod: true, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresGrantAccessActivityLogEntryData(ctx, field) + return ec.childFields_PostgresAccessConnectionDetails(ctx, field) }, } return fc, nil } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntryData_grantee(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntryData) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccessConnectionDetails_username(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccessConnectionDetails) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresGrantAccessActivityLogEntryData_grantee(ctx, field) + return ec.fieldContext_PostgresAccessConnectionDetails_username(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Grantee, nil + return obj.Username, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { @@ -616,66 +833,66 @@ func (ec *executionContext) _PostgresGrantAccessActivityLogEntryData_grantee(ctx true, ) } -func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntryData_grantee(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntryData", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_PostgresAccessConnectionDetails_username(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccessConnectionDetails", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntryData_until(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntryData) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccessConnectionDetails_password(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccessConnectionDetails) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresGrantAccessActivityLogEntryData_until(ctx, field) + return ec.fieldContext_PostgresAccessConnectionDetails_password(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Until, nil + return obj.Password, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v time.Time) graphql.Marshaler { - return ec.marshalNTime2timeᚐTime(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntryData_until(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntryData", field, false, false, errors.New("field of type Time does not have child fields")) +func (ec *executionContext) fieldContext_PostgresAccessConnectionDetails_password(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccessConnectionDetails", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresInstance_id(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstance) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccessConnectionDetails_caCertificate(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccessConnectionDetails) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstance_id(ctx, field) + return ec.fieldContext_PostgresAccessConnectionDetails_caCertificate(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.ID(), nil + return obj.CACertificate, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v ident.Ident) graphql.Marshaler { - return ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstance_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstance", field, true, false, errors.New("field of type ID does not have child fields")) +func (ec *executionContext) fieldContext_PostgresAccessConnectionDetails_caCertificate(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccessConnectionDetails", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresInstance_name(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstance) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccessConnectionDetails_serverName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccessConnectionDetails) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstance_name(ctx, field) + return ec.fieldContext_PostgresAccessConnectionDetails_serverName(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Name, nil + return obj.ServerName, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { @@ -685,160 +902,112 @@ func (ec *executionContext) _PostgresInstance_name(ctx context.Context, field gr true, ) } -func (ec *executionContext) fieldContext_PostgresInstance_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstance", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_PostgresAccessConnectionDetails_serverName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccessConnectionDetails", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresInstance_team(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstance) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccessConnectionDetails_relayEndpoint(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccessConnectionDetails) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstance_team(ctx, field) + return ec.fieldContext_PostgresAccessConnectionDetails_relayEndpoint(ctx, field) }, func(ctx context.Context) (any, error) { - return ec.Resolvers.PostgresInstance().Team(ctx, obj) + return obj.RelayEndpoint, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *team.Team) graphql.Marshaler { - return ec.marshalNTeam2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋteamᚐTeam(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstance_team(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "PostgresInstance", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_Team(ctx, field) - }, - } - return fc, nil +func (ec *executionContext) fieldContext_PostgresAccessConnectionDetails_relayEndpoint(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccessConnectionDetails", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresInstance_teamEnvironment(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstance) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccessConnectionDetails_relayAccess(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccessConnectionDetails) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstance_teamEnvironment(ctx, field) + return ec.fieldContext_PostgresAccessConnectionDetails_relayAccess(ctx, field) }, func(ctx context.Context) (any, error) { - return ec.Resolvers.PostgresInstance().TeamEnvironment(ctx, obj) + return obj.RelayAccess, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *team.TeamEnvironment) graphql.Marshaler { - return ec.marshalNTeamEnvironment2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋteamᚐTeamEnvironment(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstance_teamEnvironment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "PostgresInstance", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_TeamEnvironment(ctx, field) - }, - } - return fc, nil +func (ec *executionContext) fieldContext_PostgresAccessConnectionDetails_relayAccess(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccessConnectionDetails", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresInstance_workloads(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstance) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresAccessConnectionDetails_relayToken(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresAccessConnectionDetails) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstance_workloads(ctx, field) + return ec.fieldContext_PostgresAccessConnectionDetails_relayToken(ctx, field) }, func(ctx context.Context) (any, error) { - fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.PostgresInstance().Workloads(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*pagination.Cursor), fc.Args["last"].(*int), fc.Args["before"].(*pagination.Cursor)) + return obj.RelayToken, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *pagination.Connection[workload.Workload]) graphql.Marshaler { - return ec.marshalNWorkloadConnection2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐConnection(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstance_workloads(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "PostgresInstance", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_WorkloadConnection(ctx, field) - }, - } - defer func() { - if r := recover(); r != nil { - err = ec.Recover(ctx, r) - ec.Error(ctx, err) - } - }() - ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_PostgresInstance_workloads_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { - ec.Error(ctx, err) - return fc, err - } - return fc, nil +func (ec *executionContext) fieldContext_PostgresAccessConnectionDetails_relayToken(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresAccessConnectionDetails", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresInstance_resources(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstance) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranch_id(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranch) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstance_resources(ctx, field) + return ec.fieldContext_PostgresBranch_id(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Resources, nil + return obj.ID(), nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresInstanceResources) graphql.Marshaler { - return ec.marshalNPostgresInstanceResources2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceResources(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v ident.Ident) graphql.Marshaler { + return ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstance_resources(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "PostgresInstance", - Field: field, - IsMethod: false, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresInstanceResources(ctx, field) - }, - } - return fc, nil +func (ec *executionContext) fieldContext_PostgresBranch_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresBranch", field, true, false, errors.New("field of type ID does not have child fields")) } -func (ec *executionContext) _PostgresInstance_majorVersion(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstance) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranch_name(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranch) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstance_majorVersion(ctx, field) + return ec.fieldContext_PostgresBranch_name(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.MajorVersion, nil + return obj.Name, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { @@ -848,127 +1017,180 @@ func (ec *executionContext) _PostgresInstance_majorVersion(ctx context.Context, true, ) } -func (ec *executionContext) fieldContext_PostgresInstance_majorVersion(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstance", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_PostgresBranch_name(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresBranch", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresInstance_audit(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstance) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranch_team(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranch) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstance_audit(ctx, field) + return ec.fieldContext_PostgresBranch_team(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Audit, nil + return ec.Resolvers.PostgresBranch().Team(ctx, obj) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v postgres.PostgresInstanceAudit) graphql.Marshaler { - return ec.marshalNPostgresInstanceAudit2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceAudit(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *team.Team) graphql.Marshaler { + return ec.marshalNTeam2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋteamᚐTeam(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstance_audit(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresBranch_team(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresInstance", + Object: "PostgresBranch", Field: field, - IsMethod: false, - IsResolver: false, + IsMethod: true, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresInstanceAudit(ctx, field) + return ec.childFields_Team(ctx, field) }, } return fc, nil } -func (ec *executionContext) _PostgresInstance_highAvailability(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstance) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranch_teamEnvironment(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranch) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstance_highAvailability(ctx, field) + return ec.fieldContext_PostgresBranch_teamEnvironment(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.HighAvailability, nil + return ec.Resolvers.PostgresBranch().TeamEnvironment(ctx, obj) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { - return ec.marshalNBoolean2bool(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *team.TeamEnvironment) graphql.Marshaler { + return ec.marshalNTeamEnvironment2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋteamᚐTeamEnvironment(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstance_highAvailability(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstance", field, false, false, errors.New("field of type Boolean does not have child fields")) +func (ec *executionContext) fieldContext_PostgresBranch_teamEnvironment(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostgresBranch", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_TeamEnvironment(ctx, field) + }, + } + return fc, nil } -func (ec *executionContext) _PostgresInstance_state(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstance) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranch_postgres(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranch) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstance_state(ctx, field) + return ec.fieldContext_PostgresBranch_postgres(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.State, nil + return ec.Resolvers.PostgresBranch().Postgres(ctx, obj) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v postgres.PostgresInstanceState) graphql.Marshaler { - return ec.marshalNPostgresInstanceState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceState(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *postgres.Postgres) graphql.Marshaler { + return ec.marshalNPostgres2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgres(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstance_state(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstance", field, false, false, errors.New("field of type PostgresInstanceState does not have child fields")) +func (ec *executionContext) fieldContext_PostgresBranch_postgres(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostgresBranch", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_Postgres(ctx, field) + }, + } + return fc, nil } -func (ec *executionContext) _PostgresInstance_maintenanceWindow(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstance) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranch_workloads(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranch) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstance_maintenanceWindow(ctx, field) + return ec.fieldContext_PostgresBranch_workloads(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.MaintenanceWindow, nil + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.PostgresBranch().Workloads(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*pagination.Cursor), fc.Args["last"].(*int), fc.Args["before"].(*pagination.Cursor)) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresInstanceMaintenanceWindow) graphql.Marshaler { - return ec.marshalOPostgresInstanceMaintenanceWindow2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceMaintenanceWindow(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *pagination.Connection[workload.Workload]) graphql.Marshaler { + return ec.marshalNWorkloadConnection2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐConnection(ctx, selections, v) }, true, - false, + true, ) } -func (ec *executionContext) fieldContext_PostgresInstance_maintenanceWindow(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresBranch_workloads(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresInstance", + Object: "PostgresBranch", Field: field, - IsMethod: false, - IsResolver: false, + IsMethod: true, + IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresInstanceMaintenanceWindow(ctx, field) + return ec.childFields_WorkloadConnection(ctx, field) }, } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_PostgresBranch_workloads_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } return fc, nil } -func (ec *executionContext) _PostgresInstance_labels(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstance) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranch_state(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranch) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresBranch_state(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.State, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v postgres.PostgresBranchState) graphql.Marshaler { + return ec.marshalNPostgresBranchState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchState(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresBranch_state(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresBranch", field, false, false, errors.New("field of type PostgresBranchState does not have child fields")) +} + +func (ec *executionContext) _PostgresBranch_labels(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranch) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstance_labels(ctx, field) + return ec.fieldContext_PostgresBranch_labels(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Labels, nil @@ -981,9 +1203,9 @@ func (ec *executionContext) _PostgresInstance_labels(ctx context.Context, field true, ) } -func (ec *executionContext) fieldContext_PostgresInstance_labels(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresBranch_labels(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresInstance", + Object: "PostgresBranch", Field: field, IsMethod: false, IsResolver: false, @@ -994,97 +1216,28 @@ func (ec *executionContext) fieldContext_PostgresInstance_labels(_ context.Conte return fc, nil } -func (ec *executionContext) _PostgresInstanceAudit_enabled(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceAudit) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranchConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceAudit_enabled(ctx, field) + return ec.fieldContext_PostgresBranchConnection_pageInfo(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Enabled, nil + return obj.PageInfo, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v bool) graphql.Marshaler { - return ec.marshalNBoolean2bool(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v pagination.PageInfo) graphql.Marshaler { + return ec.marshalNPageInfo2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐPageInfo(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceAudit_enabled(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstanceAudit", field, false, false, errors.New("field of type Boolean does not have child fields")) -} - -func (ec *executionContext) _PostgresInstanceAudit_url(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceAudit) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceAudit_url(ctx, field) - }, - func(ctx context.Context) (any, error) { - return ec.Resolvers.PostgresInstanceAudit().URL(ctx, obj) - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { - return ec.marshalOString2ᚖstring(ctx, selections, v) - }, - true, - false, - ) -} -func (ec *executionContext) fieldContext_PostgresInstanceAudit_url(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstanceAudit", field, true, true, errors.New("field of type String does not have child fields")) -} - -func (ec *executionContext) _PostgresInstanceAudit_statementClasses(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceAudit) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceAudit_statementClasses(ctx, field) - }, - func(ctx context.Context) (any, error) { - return obj.StatementClasses, nil - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v []string) graphql.Marshaler { - return ec.marshalOString2ᚕstringᚄ(ctx, selections, v) - }, - true, - false, - ) -} -func (ec *executionContext) fieldContext_PostgresInstanceAudit_statementClasses(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstanceAudit", field, false, false, errors.New("field of type String does not have child fields")) -} - -func (ec *executionContext) _PostgresInstanceConnection_pageInfo(ctx context.Context, field graphql.CollectedField, obj *pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter]) (ret graphql.Marshaler) { - return graphql.ResolveField( - ctx, - ec.OperationContext, - field, - func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceConnection_pageInfo(ctx, field) - }, - func(ctx context.Context) (any, error) { - return obj.PageInfo, nil - }, - nil, - func(ctx context.Context, selections ast.SelectionSet, v pagination.PageInfo) graphql.Marshaler { - return ec.marshalNPageInfo2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐPageInfo(ctx, selections, v) - }, - true, - true, - ) -} -func (ec *executionContext) fieldContext_PostgresInstanceConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresBranchConnection_pageInfo(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresInstanceConnection", + Object: "PostgresBranchConnection", Field: field, IsMethod: false, IsResolver: false, @@ -1095,109 +1248,109 @@ func (ec *executionContext) fieldContext_PostgresInstanceConnection_pageInfo(_ c return fc, nil } -func (ec *executionContext) _PostgresInstanceConnection_nodes(ctx context.Context, field graphql.CollectedField, obj *pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter]) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranchConnection_nodes(ctx context.Context, field graphql.CollectedField, obj *pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceConnection_nodes(ctx, field) + return ec.fieldContext_PostgresBranchConnection_nodes(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Nodes(), nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v []*postgres.PostgresInstance) graphql.Marshaler { - return ec.marshalNPostgresInstance2ᚕᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceᚄ(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v []*postgres.PostgresBranch) graphql.Marshaler { + return ec.marshalNPostgresBranch2ᚕᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchᚄ(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceConnection_nodes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresBranchConnection_nodes(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresInstanceConnection", + Object: "PostgresBranchConnection", Field: field, IsMethod: true, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresInstance(ctx, field) + return ec.childFields_PostgresBranch(ctx, field) }, } return fc, nil } -func (ec *executionContext) _PostgresInstanceConnection_edges(ctx context.Context, field graphql.CollectedField, obj *pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter]) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranchConnection_edges(ctx context.Context, field graphql.CollectedField, obj *pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceConnection_edges(ctx, field) + return ec.fieldContext_PostgresBranchConnection_edges(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Edges, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v []pagination.Edge[*postgres.PostgresInstance]) graphql.Marshaler { - return ec.marshalNPostgresInstanceEdge2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐEdgeᚄ(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v []pagination.Edge[*postgres.PostgresBranch]) graphql.Marshaler { + return ec.marshalNPostgresBranchEdge2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐEdgeᚄ(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceConnection_edges(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresBranchConnection_edges(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresInstanceConnection", + Object: "PostgresBranchConnection", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresInstanceEdge(ctx, field) + return ec.childFields_PostgresBranchEdge(ctx, field) }, } return fc, nil } -func (ec *executionContext) _PostgresInstanceConnection_facets(ctx context.Context, field graphql.CollectedField, obj *pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter]) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranchConnection_facets(ctx context.Context, field graphql.CollectedField, obj *pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceConnection_facets(ctx, field) + return ec.fieldContext_PostgresBranchConnection_facets(ctx, field) }, func(ctx context.Context) (any, error) { - return ec.Resolvers.PostgresInstanceConnection().Facets(ctx, obj) + return ec.Resolvers.PostgresBranchConnection().Facets(ctx, obj) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresInstanceFacets) graphql.Marshaler { - return ec.marshalOPostgresInstanceFacets2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceFacets(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresBranchFacets) graphql.Marshaler { + return ec.marshalOPostgresBranchFacets2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchFacets(ctx, selections, v) }, true, false, ) } -func (ec *executionContext) fieldContext_PostgresInstanceConnection_facets(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresBranchConnection_facets(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresInstanceConnection", + Object: "PostgresBranchConnection", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresInstanceFacets(ctx, field) + return ec.childFields_PostgresBranchFacets(ctx, field) }, } return fc, nil } -func (ec *executionContext) _PostgresInstanceEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *pagination.Edge[*postgres.PostgresInstance]) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranchEdge_cursor(ctx context.Context, field graphql.CollectedField, obj *pagination.Edge[*postgres.PostgresBranch]) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceEdge_cursor(ctx, field) + return ec.fieldContext_PostgresBranchEdge_cursor(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Cursor, nil @@ -1210,49 +1363,49 @@ func (ec *executionContext) _PostgresInstanceEdge_cursor(ctx context.Context, fi true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceEdge_cursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstanceEdge", field, false, false, errors.New("field of type Cursor does not have child fields")) +func (ec *executionContext) fieldContext_PostgresBranchEdge_cursor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresBranchEdge", field, false, false, errors.New("field of type Cursor does not have child fields")) } -func (ec *executionContext) _PostgresInstanceEdge_node(ctx context.Context, field graphql.CollectedField, obj *pagination.Edge[*postgres.PostgresInstance]) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranchEdge_node(ctx context.Context, field graphql.CollectedField, obj *pagination.Edge[*postgres.PostgresBranch]) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceEdge_node(ctx, field) + return ec.fieldContext_PostgresBranchEdge_node(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Node, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresInstance) graphql.Marshaler { - return ec.marshalNPostgresInstance2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstance(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresBranch) graphql.Marshaler { + return ec.marshalNPostgresBranch2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranch(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceEdge_node(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresBranchEdge_node(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresInstanceEdge", + Object: "PostgresBranchEdge", Field: field, IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresInstance(ctx, field) + return ec.childFields_PostgresBranch(ctx, field) }, } return fc, nil } -func (ec *executionContext) _PostgresInstanceFacets_environments(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceFacets) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranchFacets_environments(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranchFacets) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceFacets_environments(ctx, field) + return ec.fieldContext_PostgresBranchFacets_environments(ctx, field) }, func(ctx context.Context) (any, error) { return obj.Environments(ctx), nil @@ -1265,9 +1418,9 @@ func (ec *executionContext) _PostgresInstanceFacets_environments(ctx context.Con true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceFacets_environments(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresBranchFacets_environments(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresInstanceFacets", + Object: "PostgresBranchFacets", Field: field, IsMethod: true, IsResolver: false, @@ -1278,213 +1431,273 @@ func (ec *executionContext) fieldContext_PostgresInstanceFacets_environments(_ c return fc, nil } -func (ec *executionContext) _PostgresInstanceFacets_states(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceFacets) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranchFacets_states(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranchFacets) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceFacets_states(ctx, field) + return ec.fieldContext_PostgresBranchFacets_states(ctx, field) }, func(ctx context.Context) (any, error) { return obj.States(ctx), nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v []postgres.PostgresInstanceStateFacetItem) graphql.Marshaler { - return ec.marshalNPostgresInstanceStateFacetItem2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceStateFacetItemᚄ(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v []postgres.PostgresBranchStateFacetItem) graphql.Marshaler { + return ec.marshalNPostgresBranchStateFacetItem2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchStateFacetItemᚄ(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceFacets_states(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresBranchFacets_states(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresInstanceFacets", + Object: "PostgresBranchFacets", Field: field, IsMethod: true, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresInstanceStateFacetItem(ctx, field) + return ec.childFields_PostgresBranchStateFacetItem(ctx, field) }, } return fc, nil } -func (ec *executionContext) _PostgresInstanceFacets_highAvailability(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceFacets) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranchFacets_labels(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranchFacets) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceFacets_highAvailability(ctx, field) + return ec.fieldContext_PostgresBranchFacets_labels(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.HighAvailability(ctx), nil + return obj.Labels(ctx), nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v []model.BooleanFacetItem) graphql.Marshaler { - return ec.marshalNBooleanFacetItem2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋmodelᚐBooleanFacetItemᚄ(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v []model.LabelFacetItem) graphql.Marshaler { + return ec.marshalNLabelFacetItem2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋmodelᚐLabelFacetItemᚄ(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceFacets_highAvailability(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresBranchFacets_labels(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresInstanceFacets", + Object: "PostgresBranchFacets", Field: field, IsMethod: true, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_BooleanFacetItem(ctx, field) + return ec.childFields_LabelFacetItem(ctx, field) }, } return fc, nil } -func (ec *executionContext) _PostgresInstanceFacets_majorVersions(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceFacets) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresBranchStateFacetItem_state(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranchStateFacetItem) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceFacets_majorVersions(ctx, field) + return ec.fieldContext_PostgresBranchStateFacetItem_state(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.MajorVersions(ctx), nil + return obj.State, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v []model.StringFacetItem) graphql.Marshaler { - return ec.marshalNStringFacetItem2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋmodelᚐStringFacetItemᚄ(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v postgres.PostgresBranchState) graphql.Marshaler { + return ec.marshalNPostgresBranchState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchState(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceFacets_majorVersions(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "PostgresInstanceFacets", - Field: field, - IsMethod: true, - IsResolver: false, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_StringFacetItem(ctx, field) +func (ec *executionContext) fieldContext_PostgresBranchStateFacetItem_state(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresBranchStateFacetItem", field, false, false, errors.New("field of type PostgresBranchState does not have child fields")) +} + +func (ec *executionContext) _PostgresBranchStateFacetItem_count(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresBranchStateFacetItem) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresBranchStateFacetItem_count(ctx, field) }, - } - return fc, nil + func(ctx context.Context) (any, error) { + return obj.Count, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresBranchStateFacetItem_count(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresBranchStateFacetItem", field, false, false, errors.New("field of type Int does not have child fields")) } -func (ec *executionContext) _PostgresInstanceFacets_labels(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceFacets) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresDeletedActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceFacets_labels(ctx, field) + return ec.fieldContext_PostgresDeletedActivityLogEntry_id(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Labels(ctx), nil + return obj.ID(), nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v []model.LabelFacetItem) graphql.Marshaler { - return ec.marshalNLabelFacetItem2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋmodelᚐLabelFacetItemᚄ(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v ident.Ident) graphql.Marshaler { + return ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, true, false, errors.New("field of type ID does not have child fields")) +} + +func (ec *executionContext) _PostgresDeletedActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresDeletedActivityLogEntry_actor(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Actor, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceFacets_labels(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresDeletedActivityLogEntry_gitHubActorClaims(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresDeletedActivityLogEntry_gitHubActorClaims(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.GitHubActorClaims, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *github.GitHubActorClaims) graphql.Marshaler { + return ec.marshalOGitHubActorClaims2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋauthᚋmiddlewareᚋgithubᚐGitHubActorClaims(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_gitHubActorClaims(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ - Object: "PostgresInstanceFacets", + Object: "PostgresDeletedActivityLogEntry", Field: field, - IsMethod: true, + IsMethod: false, IsResolver: false, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_LabelFacetItem(ctx, field) + return ec.childFields_GitHubActorClaims(ctx, field) }, } return fc, nil } -func (ec *executionContext) _PostgresInstanceMaintenanceWindow_day(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceMaintenanceWindow) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresDeletedActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceMaintenanceWindow_day(ctx, field) + return ec.fieldContext_PostgresDeletedActivityLogEntry_createdAt(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Day, nil + return obj.CreatedAt, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { - return ec.marshalNInt2int(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v time.Time) graphql.Marshaler { + return ec.marshalNTime2timeᚐTime(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceMaintenanceWindow_day(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstanceMaintenanceWindow", field, false, false, errors.New("field of type Int does not have child fields")) +func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type Time does not have child fields")) } -func (ec *executionContext) _PostgresInstanceMaintenanceWindow_hour(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceMaintenanceWindow) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresDeletedActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceMaintenanceWindow_hour(ctx, field) + return ec.fieldContext_PostgresDeletedActivityLogEntry_message(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Hour, nil + return obj.Message, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { - return ec.marshalNInt2int(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceMaintenanceWindow_hour(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstanceMaintenanceWindow", field, false, false, errors.New("field of type Int does not have child fields")) +func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresInstanceResources_cpu(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceResources) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresDeletedActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceResources_cpu(ctx, field) + return ec.fieldContext_PostgresDeletedActivityLogEntry_resourceType(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.CPU, nil + return obj.ResourceType, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { - return ec.marshalNString2string(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v activitylog.ActivityLogEntryResourceType) graphql.Marshaler { + return ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceResources_cpu(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstanceResources", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type ActivityLogEntryResourceType does not have child fields")) } -func (ec *executionContext) _PostgresInstanceResources_memory(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceResources) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresDeletedActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceResources_memory(ctx, field) + return ec.fieldContext_PostgresDeletedActivityLogEntry_resourceName(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Memory, nil + return obj.ResourceName, nil }, nil, func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { @@ -1494,110 +1707,1006 @@ func (ec *executionContext) _PostgresInstanceResources_memory(ctx context.Contex true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceResources_memory(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstanceResources", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresInstanceResources_diskSize(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceResources) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresDeletedActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceResources_diskSize(ctx, field) + return ec.fieldContext_PostgresDeletedActivityLogEntry_teamSlug(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.DiskSize, nil + return obj.TeamSlug, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { - return ec.marshalNString2string(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *slug.Slug) graphql.Marshaler { + return ec.marshalNSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceResources_diskSize(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstanceResources", field, false, false, errors.New("field of type String does not have child fields")) +func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type Slug does not have child fields")) } -func (ec *executionContext) _PostgresInstanceStateFacetItem_state(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceStateFacetItem) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresDeletedActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresDeletedActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceStateFacetItem_state(ctx, field) + return ec.fieldContext_PostgresDeletedActivityLogEntry_environmentName(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.State, nil + return obj.EnvironmentName, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v postgres.PostgresInstanceState) graphql.Marshaler { - return ec.marshalNPostgresInstanceState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceState(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) }, true, - true, + false, ) } -func (ec *executionContext) fieldContext_PostgresInstanceStateFacetItem_state(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstanceStateFacetItem", field, false, false, errors.New("field of type PostgresInstanceState does not have child fields")) +func (ec *executionContext) fieldContext_PostgresDeletedActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresDeletedActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) } -func (ec *executionContext) _PostgresInstanceStateFacetItem_count(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresInstanceStateFacetItem) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_PostgresInstanceStateFacetItem_count(ctx, field) + return ec.fieldContext_PostgresGrantAccessActivityLogEntry_id(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Count, nil + return obj.ID(), nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { - return ec.marshalNInt2int(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v ident.Ident) graphql.Marshaler { + return ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_PostgresInstanceStateFacetItem_count(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("PostgresInstanceStateFacetItem", field, false, false, errors.New("field of type Int does not have child fields")) +func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, true, false, errors.New("field of type ID does not have child fields")) } -func (ec *executionContext) _TeamInventoryCountPostgresInstances_total(ctx context.Context, field graphql.CollectedField, obj *postgres.TeamInventoryCountPostgresInstances) (ret graphql.Marshaler) { +func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_TeamInventoryCountPostgresInstances_total(ctx, field) + return ec.fieldContext_PostgresGrantAccessActivityLogEntry_actor(ctx, field) }, func(ctx context.Context) (any, error) { - return obj.Total, nil + return obj.Actor, nil }, nil, - func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { - return ec.marshalNInt2int(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_TeamInventoryCountPostgresInstances_total(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - return graphql.NewScalarFieldContext("TeamInventoryCountPostgresInstances", field, false, false, errors.New("field of type Int does not have child fields")) +func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) } -// endregion **************************** field.gotpl ***************************** - -// region **************************** input.gotpl ***************************** - -func (ec *executionContext) unmarshalInputDeletePostgresInput(ctx context.Context, obj any) (postgres.DeletePostgresInput, error) { - var it postgres.DeletePostgresInput - if obj == nil { - return it, nil +func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_gitHubActorClaims(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresGrantAccessActivityLogEntry_gitHubActorClaims(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.GitHubActorClaims, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *github.GitHubActorClaims) graphql.Marshaler { + return ec.marshalOGitHubActorClaims2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋauthᚋmiddlewareᚋgithubᚐGitHubActorClaims(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_gitHubActorClaims(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostgresGrantAccessActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_GitHubActorClaims(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresGrantAccessActivityLogEntry_createdAt(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.CreatedAt, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v time.Time) graphql.Marshaler { + return ec.marshalNTime2timeᚐTime(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type Time does not have child fields")) +} + +func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresGrantAccessActivityLogEntry_message(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Message, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresGrantAccessActivityLogEntry_resourceType(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ResourceType, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v activitylog.ActivityLogEntryResourceType) graphql.Marshaler { + return ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type ActivityLogEntryResourceType does not have child fields")) +} + +func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresGrantAccessActivityLogEntry_resourceName(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ResourceName, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresGrantAccessActivityLogEntry_teamSlug(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.TeamSlug, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *slug.Slug) graphql.Marshaler { + return ec.marshalNSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type Slug does not have child fields")) +} + +func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresGrantAccessActivityLogEntry_environmentName(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.EnvironmentName, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresGrantAccessActivityLogEntry_data(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresGrantAccessActivityLogEntry_data(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Data, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresGrantAccessActivityLogEntryData) graphql.Marshaler { + return ec.marshalNPostgresGrantAccessActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresGrantAccessActivityLogEntryData(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntry_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostgresGrantAccessActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_PostgresGrantAccessActivityLogEntryData(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _PostgresGrantAccessActivityLogEntryData_grantee(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntryData) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresGrantAccessActivityLogEntryData_grantee(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Grantee, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntryData_grantee(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntryData", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresGrantAccessActivityLogEntryData_until(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresGrantAccessActivityLogEntryData) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresGrantAccessActivityLogEntryData_until(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Until, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v time.Time) graphql.Marshaler { + return ec.marshalNTime2timeᚐTime(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresGrantAccessActivityLogEntryData_until(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresGrantAccessActivityLogEntryData", field, false, false, errors.New("field of type Time does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessConnectionActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessConnectionActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_id(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ID(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v ident.Ident) graphql.Marshaler { + return ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessConnectionActivityLogEntry", field, true, false, errors.New("field of type ID does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessConnectionActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessConnectionActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_actor(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Actor, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessConnectionActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessConnectionActivityLogEntry_gitHubActorClaims(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessConnectionActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_gitHubActorClaims(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.GitHubActorClaims, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *github.GitHubActorClaims) graphql.Marshaler { + return ec.marshalOGitHubActorClaims2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋauthᚋmiddlewareᚋgithubᚐGitHubActorClaims(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_gitHubActorClaims(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostgresPersonalAccessConnectionActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_GitHubActorClaims(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _PostgresPersonalAccessConnectionActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessConnectionActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_createdAt(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.CreatedAt, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v time.Time) graphql.Marshaler { + return ec.marshalNTime2timeᚐTime(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessConnectionActivityLogEntry", field, false, false, errors.New("field of type Time does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessConnectionActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessConnectionActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_message(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Message, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessConnectionActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessConnectionActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessConnectionActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_resourceType(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ResourceType, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v activitylog.ActivityLogEntryResourceType) graphql.Marshaler { + return ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessConnectionActivityLogEntry", field, false, false, errors.New("field of type ActivityLogEntryResourceType does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessConnectionActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessConnectionActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_resourceName(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ResourceName, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessConnectionActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessConnectionActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessConnectionActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_teamSlug(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.TeamSlug, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *slug.Slug) graphql.Marshaler { + return ec.marshalNSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessConnectionActivityLogEntry", field, false, false, errors.New("field of type Slug does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessConnectionActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessConnectionActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_environmentName(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.EnvironmentName, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessConnectionActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessConnectionActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntry_id(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_id(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ID(), nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v ident.Ident) graphql.Marshaler { + return ec.marshalNID2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋidentᚐIdent(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_id(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessCreatedActivityLogEntry", field, true, false, errors.New("field of type ID does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntry_actor(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_actor(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Actor, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_actor(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessCreatedActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntry_gitHubActorClaims(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_gitHubActorClaims(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.GitHubActorClaims, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *github.GitHubActorClaims) graphql.Marshaler { + return ec.marshalOGitHubActorClaims2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋauthᚋmiddlewareᚋgithubᚐGitHubActorClaims(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_gitHubActorClaims(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostgresPersonalAccessCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_GitHubActorClaims(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntry_createdAt(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_createdAt(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.CreatedAt, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v time.Time) graphql.Marshaler { + return ec.marshalNTime2timeᚐTime(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_createdAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessCreatedActivityLogEntry", field, false, false, errors.New("field of type Time does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntry_message(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_message(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Message, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_message(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessCreatedActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntry_resourceType(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_resourceType(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ResourceType, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v activitylog.ActivityLogEntryResourceType) graphql.Marshaler { + return ec.marshalNActivityLogEntryResourceType2githubᚗcomᚋnaisᚋapiᚋinternalᚋactivitylogᚐActivityLogEntryResourceType(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_resourceType(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessCreatedActivityLogEntry", field, false, false, errors.New("field of type ActivityLogEntryResourceType does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntry_resourceName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_resourceName(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ResourceName, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_resourceName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessCreatedActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntry_teamSlug(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_teamSlug(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.TeamSlug, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *slug.Slug) graphql.Marshaler { + return ec.marshalNSlug2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_teamSlug(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessCreatedActivityLogEntry", field, false, false, errors.New("field of type Slug does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntry_environmentName(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_environmentName(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.EnvironmentName, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_environmentName(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessCreatedActivityLogEntry", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntry_data(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntry) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_data(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Data, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresPersonalAccessCreatedActivityLogEntryData) graphql.Marshaler { + return ec.marshalNPostgresPersonalAccessCreatedActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresPersonalAccessCreatedActivityLogEntryData(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntry_data(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "PostgresPersonalAccessCreatedActivityLogEntry", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_PostgresPersonalAccessCreatedActivityLogEntryData(ctx, field) + }, + } + return fc, nil +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntryData_username(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntryData) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntryData_username(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Username, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntryData_username(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessCreatedActivityLogEntryData", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntryData_accessLevel(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntryData) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntryData_accessLevel(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.AccessLevel, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresAccessLevel) graphql.Marshaler { + return ec.marshalOPostgresAccessLevel2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccessLevel(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntryData_accessLevel(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessCreatedActivityLogEntryData", field, false, false, errors.New("field of type PostgresAccessLevel does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntryData_expiresAt(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntryData) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntryData_expiresAt(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.ExpiresAt, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v time.Time) graphql.Marshaler { + return ec.marshalNTime2timeᚐTime(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntryData_expiresAt(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessCreatedActivityLogEntryData", field, false, false, errors.New("field of type Time does not have child fields")) +} + +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntryData_reason(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntryData) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntryData_reason(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Reason, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v string) graphql.Marshaler { + return ec.marshalNString2string(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_PostgresPersonalAccessCreatedActivityLogEntryData_reason(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresPersonalAccessCreatedActivityLogEntryData", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresResources_cpu(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresResources) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresResources_cpu(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.CPU, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_PostgresResources_cpu(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresResources", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresResources_memory(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresResources) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresResources_memory(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Memory, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_PostgresResources_memory(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresResources", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _PostgresResources_diskSize(ctx context.Context, field graphql.CollectedField, obj *postgres.PostgresResources) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_PostgresResources_diskSize(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.DiskSize, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *string) graphql.Marshaler { + return ec.marshalOString2ᚖstring(ctx, selections, v) + }, + true, + false, + ) +} +func (ec *executionContext) fieldContext_PostgresResources_diskSize(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("PostgresResources", field, false, false, errors.New("field of type String does not have child fields")) +} + +func (ec *executionContext) _TeamInventoryCountPostgresBranches_total(ctx context.Context, field graphql.CollectedField, obj *postgres.TeamInventoryCountPostgresBranches) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_TeamInventoryCountPostgresBranches_total(ctx, field) + }, + func(ctx context.Context) (any, error) { + return obj.Total, nil + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v int) graphql.Marshaler { + return ec.marshalNInt2int(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_TeamInventoryCountPostgresBranches_total(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + return graphql.NewScalarFieldContext("TeamInventoryCountPostgresBranches", field, false, false, errors.New("field of type Int does not have child fields")) +} + +// endregion **************************** field.gotpl ***************************** + +// region **************************** input.gotpl ***************************** + +func (ec *executionContext) unmarshalInputCreatePostgresAccessInput(ctx context.Context, obj any) (postgres.CreatePostgresAccessInput, error) { + var it postgres.CreatePostgresAccessInput + if obj == nil { + return it, nil } asMap := map[string]any{} @@ -1605,20 +2714,99 @@ func (ec *executionContext) unmarshalInputDeletePostgresInput(ctx context.Contex asMap[k] = v } - fieldsInOrder := [...]string{"name", "environmentName", "teamSlug"} + fieldsInOrder := [...]string{"postgres", "branch", "teamSlug", "environmentName", "accessLevel", "reason", "ttl"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { continue } switch k { - case "name": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) + case "postgres": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("postgres")) data, err := ec.unmarshalNString2string(ctx, v) if err != nil { return it, err } - it.Name = data + it.Postgres = data + case "branch": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("branch")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.Branch = data + case "teamSlug": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("teamSlug")) + data, err := ec.unmarshalNSlug2githubᚗcomᚋnaisᚋapiᚋinternalᚋslugᚐSlug(ctx, v) + if err != nil { + return it, err + } + it.TeamSlug = data + case "environmentName": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("environmentName")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.EnvironmentName = data + case "accessLevel": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("accessLevel")) + data, err := ec.unmarshalNPostgresAccessLevel2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccessLevel(ctx, v) + if err != nil { + return it, err + } + it.AccessLevel = data + case "reason": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("reason")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.Reason = data + case "ttl": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("ttl")) + data, err := ec.unmarshalOString2string(ctx, v) + if err != nil { + return it, err + } + it.TTL = data + } + } + return it, nil +} + +func (ec *executionContext) unmarshalInputDeletePostgresBranchInput(ctx context.Context, obj any) (postgres.DeletePostgresBranchInput, error) { + var it postgres.DeletePostgresBranchInput + if obj == nil { + return it, nil + } + + asMap := map[string]any{} + for k, v := range obj.(map[string]any) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"postgres", "branch", "environmentName", "teamSlug"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "postgres": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("postgres")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.Postgres = data + case "branch": + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("branch")) + data, err := ec.unmarshalNString2string(ctx, v) + if err != nil { + return it, err + } + it.Branch = data case "environmentName": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("environmentName")) data, err := ec.unmarshalNString2string(ctx, v) @@ -1696,8 +2884,8 @@ func (ec *executionContext) unmarshalInputGrantPostgresAccessInput(ctx context.C return it, nil } -func (ec *executionContext) unmarshalInputPostgresInstanceFilter(ctx context.Context, obj any) (postgres.PostgresInstanceFilter, error) { - var it postgres.PostgresInstanceFilter +func (ec *executionContext) unmarshalInputPostgresBranchFilter(ctx context.Context, obj any) (postgres.PostgresBranchFilter, error) { + var it postgres.PostgresBranchFilter if obj == nil { return it, nil } @@ -1707,7 +2895,7 @@ func (ec *executionContext) unmarshalInputPostgresInstanceFilter(ctx context.Con asMap[k] = v } - fieldsInOrder := [...]string{"name", "environments", "states", "highAvailability", "majorVersions", "labels"} + fieldsInOrder := [...]string{"name", "environments", "states", "labels"} for _, k := range fieldsInOrder { v, ok := asMap[k] if !ok { @@ -1730,25 +2918,11 @@ func (ec *executionContext) unmarshalInputPostgresInstanceFilter(ctx context.Con it.Environments = data case "states": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("states")) - data, err := ec.unmarshalOPostgresInstanceState2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceStateᚄ(ctx, v) + data, err := ec.unmarshalOPostgresBranchState2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchStateᚄ(ctx, v) if err != nil { return it, err } it.States = data - case "highAvailability": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("highAvailability")) - data, err := ec.unmarshalOBoolean2ᚖbool(ctx, v) - if err != nil { - return it, err - } - it.HighAvailability = data - case "majorVersions": - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("majorVersions")) - data, err := ec.unmarshalOString2ᚕstringᚄ(ctx, v) - if err != nil { - return it, err - } - it.MajorVersions = data case "labels": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("labels")) data, err := ec.unmarshalOLabelFilter2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋmodelᚐLabelFiltersᚄ(ctx, v) @@ -1761,8 +2935,8 @@ func (ec *executionContext) unmarshalInputPostgresInstanceFilter(ctx context.Con return it, nil } -func (ec *executionContext) unmarshalInputPostgresInstanceOrder(ctx context.Context, obj any) (postgres.PostgresInstanceOrder, error) { - var it postgres.PostgresInstanceOrder +func (ec *executionContext) unmarshalInputPostgresBranchOrder(ctx context.Context, obj any) (postgres.PostgresBranchOrder, error) { + var it postgres.PostgresBranchOrder if obj == nil { return it, nil } @@ -1781,7 +2955,7 @@ func (ec *executionContext) unmarshalInputPostgresInstanceOrder(ctx context.Cont switch k { case "field": ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("field")) - data, err := ec.unmarshalNPostgresInstanceOrderField2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceOrderField(ctx, v) + data, err := ec.unmarshalNPostgresBranchOrderField2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchOrderField(ctx, v) if err != nil { return it, err } @@ -1806,19 +2980,27 @@ func (ec *executionContext) unmarshalInputPostgresInstanceOrder(ctx context.Cont // region **************************** object.gotpl **************************** -var deletePostgresPayloadImplementors = []string{"DeletePostgresPayload"} +var createPostgresAccessPayloadImplementors = []string{"CreatePostgresAccessPayload"} -func (ec *executionContext) _DeletePostgresPayload(ctx context.Context, sel ast.SelectionSet, obj *postgres.DeletePostgresPayload) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, deletePostgresPayloadImplementors) +func (ec *executionContext) _CreatePostgresAccessPayload(ctx context.Context, sel ast.SelectionSet, obj *postgres.CreatePostgresAccessPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, createPostgresAccessPayloadImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("DeletePostgresPayload") - case "postgresDeleted": - out.Values[i] = ec._DeletePostgresPayload_postgresDeleted(ctx, field, obj) + out.Values[i] = graphql.MarshalString("CreatePostgresAccessPayload") + case "name": + out.Values[i] = ec._CreatePostgresAccessPayload_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "expiresAt": + out.Values[i] = ec._CreatePostgresAccessPayload_expiresAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -1842,19 +3024,19 @@ func (ec *executionContext) _DeletePostgresPayload(ctx context.Context, sel ast. return out } -var grantPostgresAccessPayloadImplementors = []string{"GrantPostgresAccessPayload"} +var deletePostgresBranchPayloadImplementors = []string{"DeletePostgresBranchPayload"} -func (ec *executionContext) _GrantPostgresAccessPayload(ctx context.Context, sel ast.SelectionSet, obj *postgres.GrantPostgresAccessPayload) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, grantPostgresAccessPayloadImplementors) +func (ec *executionContext) _DeletePostgresBranchPayload(ctx context.Context, sel ast.SelectionSet, obj *postgres.DeletePostgresBranchPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, deletePostgresBranchPayloadImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("GrantPostgresAccessPayload") - case "error": - out.Values[i] = ec._GrantPostgresAccessPayload_error(ctx, field, obj) + out.Values[i] = graphql.MarshalString("DeletePostgresBranchPayload") + case "postgresBranchDeleted": + out.Values[i] = ec._DeletePostgresBranchPayload_postgresBranchDeleted(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -1878,56 +3060,19 @@ func (ec *executionContext) _GrantPostgresAccessPayload(ctx context.Context, sel return out } -var postgresDeletedActivityLogEntryImplementors = []string{"PostgresDeletedActivityLogEntry", "ActivityLogEntry", "Node"} +var grantPostgresAccessPayloadImplementors = []string{"GrantPostgresAccessPayload"} -func (ec *executionContext) _PostgresDeletedActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresDeletedActivityLogEntry) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, postgresDeletedActivityLogEntryImplementors) +func (ec *executionContext) _GrantPostgresAccessPayload(ctx context.Context, sel ast.SelectionSet, obj *postgres.GrantPostgresAccessPayload) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, grantPostgresAccessPayloadImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("PostgresDeletedActivityLogEntry") - case "id": - out.Values[i] = ec._PostgresDeletedActivityLogEntry_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "actor": - out.Values[i] = ec._PostgresDeletedActivityLogEntry_actor(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "gitHubActorClaims": - out.Values[i] = ec._PostgresDeletedActivityLogEntry_gitHubActorClaims(ctx, field, obj) - case "createdAt": - out.Values[i] = ec._PostgresDeletedActivityLogEntry_createdAt(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "message": - out.Values[i] = ec._PostgresDeletedActivityLogEntry_message(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "resourceType": - out.Values[i] = ec._PostgresDeletedActivityLogEntry_resourceType(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "resourceName": - out.Values[i] = ec._PostgresDeletedActivityLogEntry_resourceName(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "teamSlug": - out.Values[i] = ec._PostgresDeletedActivityLogEntry_teamSlug(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "environmentName": - out.Values[i] = ec._PostgresDeletedActivityLogEntry_environmentName(ctx, field, obj) + out.Values[i] = graphql.MarshalString("GrantPostgresAccessPayload") + case "error": + out.Values[i] = ec._GrantPostgresAccessPayload_error(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -1951,104 +3096,151 @@ func (ec *executionContext) _PostgresDeletedActivityLogEntry(ctx context.Context return out } -var postgresGrantAccessActivityLogEntryImplementors = []string{"PostgresGrantAccessActivityLogEntry", "ActivityLogEntry", "Node"} +var postgresImplementors = []string{"Postgres", "Node"} -func (ec *executionContext) _PostgresGrantAccessActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresGrantAccessActivityLogEntry) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, postgresGrantAccessActivityLogEntryImplementors) +func (ec *executionContext) _Postgres(ctx context.Context, sel ast.SelectionSet, obj *postgres.Postgres) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("PostgresGrantAccessActivityLogEntry") + out.Values[i] = graphql.MarshalString("Postgres") case "id": - out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_id(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ - } - case "actor": - out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_actor(ctx, field, obj) + out.Values[i] = ec._Postgres_id(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } - case "gitHubActorClaims": - out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_gitHubActorClaims(ctx, field, obj) - case "createdAt": - out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_createdAt(ctx, field, obj) + case "name": + out.Values[i] = ec._Postgres_name(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } - case "message": - out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_message(ctx, field, obj) + case "majorVersion": + out.Values[i] = ec._Postgres_majorVersion(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } - case "resourceType": - out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_resourceType(ctx, field, obj) + case "highAvailability": + out.Values[i] = ec._Postgres_highAvailability(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } - case "resourceName": - out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_resourceName(ctx, field, obj) + case "resources": + out.Values[i] = ec._Postgres_resources(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } - case "teamSlug": - out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_teamSlug(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ + case "activeBranch": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Postgres_activeBranch(ctx, field, obj) + return res } - case "environmentName": - out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_environmentName(ctx, field, obj) - case "data": - out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_data(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "branch": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Postgres_branch(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res } - default: - panic("unknown field " + strconv.Quote(field.Name)) - } - } - out.Dispatch(ctx) - if out.Invalids > 0 { - return graphql.Null - } - atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - for label, dfs := range deferred { - ec.ProcessDeferredGroup(graphql.DeferredGroup{ - Label: label, - Path: graphql.GetPath(ctx), - FieldSet: dfs, - Context: ctx, - }) - } + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } - return out -} + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "branches": + field := field -var postgresGrantAccessActivityLogEntryDataImplementors = []string{"PostgresGrantAccessActivityLogEntryData"} + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Postgres_branches(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } -func (ec *executionContext) _PostgresGrantAccessActivityLogEntryData(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresGrantAccessActivityLogEntryData) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, postgresGrantAccessActivityLogEntryDataImplementors) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("PostgresGrantAccessActivityLogEntryData") - case "grantee": - out.Values[i] = ec._PostgresGrantAccessActivityLogEntryData_grantee(ctx, field, obj) - if out.Values[i] == graphql.Null { - out.Invalids++ + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue } - case "until": - out.Values[i] = ec._PostgresGrantAccessActivityLogEntryData_until(ctx, field, obj) + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "labels": + out.Values[i] = ec._Postgres_labels(ctx, field, obj) if out.Values[i] == graphql.Null { - out.Invalids++ + atomic.AddUint32(&out.Invalids, 1) } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -2073,24 +3265,24 @@ func (ec *executionContext) _PostgresGrantAccessActivityLogEntryData(ctx context return out } -var postgresInstanceImplementors = []string{"PostgresInstance", "Persistence", "Node", "SearchNode"} +var postgresAccessImplementors = []string{"PostgresAccess", "Node"} -func (ec *executionContext) _PostgresInstance(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresInstance) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, postgresInstanceImplementors) +func (ec *executionContext) _PostgresAccess(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresAccess) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresAccessImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("PostgresInstance") + out.Values[i] = graphql.MarshalString("PostgresAccess") case "id": - out.Values[i] = ec._PostgresInstance_id(ctx, field, obj) + out.Values[i] = ec._PostgresAccess_id(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } case "name": - out.Values[i] = ec._PostgresInstance_name(ctx, field, obj) + out.Values[i] = ec._PostgresAccess_name(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } @@ -2103,7 +3295,7 @@ func (ec *executionContext) _PostgresInstance(ctx context.Context, sel ast.Selec ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._PostgresInstance_team(ctx, field, obj) + res = ec._PostgresAccess_team(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -2139,7 +3331,7 @@ func (ec *executionContext) _PostgresInstance(ctx context.Context, sel ast.Selec ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._PostgresInstance_teamEnvironment(ctx, field, obj) + res = ec._PostgresAccess_teamEnvironment(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -2166,7 +3358,7 @@ func (ec *executionContext) _PostgresInstance(ctx context.Context, sel ast.Selec } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "workloads": + case "postgresBranch": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -2175,7 +3367,7 @@ func (ec *executionContext) _PostgresInstance(ctx context.Context, sel ast.Selec ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._PostgresInstance_workloads(ctx, field, obj) + res = ec._PostgresAccess_postgresBranch(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -2202,37 +3394,126 @@ func (ec *executionContext) _PostgresInstance(ctx context.Context, sel ast.Selec } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "resources": - out.Values[i] = ec._PostgresInstance_resources(ctx, field, obj) + case "accessLevel": + out.Values[i] = ec._PostgresAccess_accessLevel(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "majorVersion": - out.Values[i] = ec._PostgresInstance_majorVersion(ctx, field, obj) + case "expiresAt": + out.Values[i] = ec._PostgresAccess_expiresAt(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "audit": - out.Values[i] = ec._PostgresInstance_audit(ctx, field, obj) + case "state": + out.Values[i] = ec._PostgresAccess_state(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } - case "highAvailability": - out.Values[i] = ec._PostgresInstance_highAvailability(ctx, field, obj) + case "message": + out.Values[i] = ec._PostgresAccess_message(ctx, field, obj) + case "relayAccess": + out.Values[i] = ec._PostgresAccess_relayAccess(ctx, field, obj) + case "connection": + field := field + + innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._PostgresAccess_connection(ctx, field, obj) + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var postgresAccessConnectionDetailsImplementors = []string{"PostgresAccessConnectionDetails"} + +func (ec *executionContext) _PostgresAccessConnectionDetails(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresAccessConnectionDetails) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresAccessConnectionDetailsImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PostgresAccessConnectionDetails") + case "username": + out.Values[i] = ec._PostgresAccessConnectionDetails_username(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } - case "state": - out.Values[i] = ec._PostgresInstance_state(ctx, field, obj) + case "password": + out.Values[i] = ec._PostgresAccessConnectionDetails_password(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ } - case "maintenanceWindow": - out.Values[i] = ec._PostgresInstance_maintenanceWindow(ctx, field, obj) - case "labels": - out.Values[i] = ec._PostgresInstance_labels(ctx, field, obj) + case "caCertificate": + out.Values[i] = ec._PostgresAccessConnectionDetails_caCertificate(ctx, field, obj) if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + out.Invalids++ + } + case "serverName": + out.Values[i] = ec._PostgresAccessConnectionDetails_serverName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "relayEndpoint": + out.Values[i] = ec._PostgresAccessConnectionDetails_relayEndpoint(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "relayAccess": + out.Values[i] = ec._PostgresAccessConnectionDetails_relayAccess(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "relayToken": + out.Values[i] = ec._PostgresAccessConnectionDetails_relayToken(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ } default: panic("unknown field " + strconv.Quote(field.Name)) @@ -2257,32 +3538,148 @@ func (ec *executionContext) _PostgresInstance(ctx context.Context, sel ast.Selec return out } -var postgresInstanceAuditImplementors = []string{"PostgresInstanceAudit"} +var postgresBranchImplementors = []string{"PostgresBranch", "Persistence", "Node", "SearchNode"} + +func (ec *executionContext) _PostgresBranch(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresBranch) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresBranchImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PostgresBranch") + case "id": + out.Values[i] = ec._PostgresBranch_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "name": + out.Values[i] = ec._PostgresBranch_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "team": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._PostgresBranch_team(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "teamEnvironment": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._PostgresBranch_teamEnvironment(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "postgres": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._PostgresBranch_postgres(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } -func (ec *executionContext) _PostgresInstanceAudit(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresInstanceAudit) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, postgresInstanceAuditImplementors) + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) - out := graphql.NewFieldSet(fields) - deferred := make(map[string]*graphql.FieldSet) - for i, field := range fields { - switch field.Name { - case "__typename": - out.Values[i] = graphql.MarshalString("PostgresInstanceAudit") - case "enabled": - out.Values[i] = ec._PostgresInstanceAudit_enabled(ctx, field, obj) - if out.Values[i] == graphql.Null { - atomic.AddUint32(&out.Invalids, 1) + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue } - case "url": + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "workloads": field := field - innerFunc := func(ctx context.Context, _ *graphql.FieldSet) (res graphql.Marshaler) { + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { defer func() { if r := recover(); r != nil { ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._PostgresInstanceAudit_url(ctx, field, obj) + res = ec._PostgresBranch_workloads(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } return res } @@ -2306,8 +3703,16 @@ func (ec *executionContext) _PostgresInstanceAudit(ctx context.Context, sel ast. } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "statementClasses": - out.Values[i] = ec._PostgresInstanceAudit_statementClasses(ctx, field, obj) + case "state": + out.Values[i] = ec._PostgresBranch_state(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } + case "labels": + out.Values[i] = ec._PostgresBranch_labels(ctx, field, obj) + if out.Values[i] == graphql.Null { + atomic.AddUint32(&out.Invalids, 1) + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -2331,29 +3736,29 @@ func (ec *executionContext) _PostgresInstanceAudit(ctx context.Context, sel ast. return out } -var postgresInstanceConnectionImplementors = []string{"PostgresInstanceConnection"} +var postgresBranchConnectionImplementors = []string{"PostgresBranchConnection"} -func (ec *executionContext) _PostgresInstanceConnection(ctx context.Context, sel ast.SelectionSet, obj *pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter]) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, postgresInstanceConnectionImplementors) +func (ec *executionContext) _PostgresBranchConnection(ctx context.Context, sel ast.SelectionSet, obj *pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresBranchConnectionImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("PostgresInstanceConnection") + out.Values[i] = graphql.MarshalString("PostgresBranchConnection") case "pageInfo": - out.Values[i] = ec._PostgresInstanceConnection_pageInfo(ctx, field, obj) + out.Values[i] = ec._PostgresBranchConnection_pageInfo(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } case "nodes": - out.Values[i] = ec._PostgresInstanceConnection_nodes(ctx, field, obj) + out.Values[i] = ec._PostgresBranchConnection_nodes(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } case "edges": - out.Values[i] = ec._PostgresInstanceConnection_edges(ctx, field, obj) + out.Values[i] = ec._PostgresBranchConnection_edges(ctx, field, obj) if out.Values[i] == graphql.Null { atomic.AddUint32(&out.Invalids, 1) } @@ -2366,7 +3771,7 @@ func (ec *executionContext) _PostgresInstanceConnection(ctx context.Context, sel ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._PostgresInstanceConnection_facets(ctx, field, obj) + res = ec._PostgresBranchConnection_facets(ctx, field, obj) return res } @@ -2413,24 +3818,24 @@ func (ec *executionContext) _PostgresInstanceConnection(ctx context.Context, sel return out } -var postgresInstanceEdgeImplementors = []string{"PostgresInstanceEdge"} +var postgresBranchEdgeImplementors = []string{"PostgresBranchEdge"} -func (ec *executionContext) _PostgresInstanceEdge(ctx context.Context, sel ast.SelectionSet, obj *pagination.Edge[*postgres.PostgresInstance]) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, postgresInstanceEdgeImplementors) +func (ec *executionContext) _PostgresBranchEdge(ctx context.Context, sel ast.SelectionSet, obj *pagination.Edge[*postgres.PostgresBranch]) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresBranchEdgeImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("PostgresInstanceEdge") + out.Values[i] = graphql.MarshalString("PostgresBranchEdge") case "cursor": - out.Values[i] = ec._PostgresInstanceEdge_cursor(ctx, field, obj) + out.Values[i] = ec._PostgresBranchEdge_cursor(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } case "node": - out.Values[i] = ec._PostgresInstanceEdge_node(ctx, field, obj) + out.Values[i] = ec._PostgresBranchEdge_node(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -2457,17 +3862,17 @@ func (ec *executionContext) _PostgresInstanceEdge(ctx context.Context, sel ast.S return out } -var postgresInstanceFacetsImplementors = []string{"PostgresInstanceFacets"} +var postgresBranchFacetsImplementors = []string{"PostgresBranchFacets"} -func (ec *executionContext) _PostgresInstanceFacets(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresInstanceFacets) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, postgresInstanceFacetsImplementors) +func (ec *executionContext) _PostgresBranchFacets(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresBranchFacets) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresBranchFacetsImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("PostgresInstanceFacets") + out.Values[i] = graphql.MarshalString("PostgresBranchFacets") case "environments": field := field @@ -2477,7 +3882,7 @@ func (ec *executionContext) _PostgresInstanceFacets(ctx context.Context, sel ast ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._PostgresInstanceFacets_environments(ctx, field, obj) + res = ec._PostgresBranchFacets_environments(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -2513,7 +3918,7 @@ func (ec *executionContext) _PostgresInstanceFacets(ctx context.Context, sel ast ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._PostgresInstanceFacets_states(ctx, field, obj) + res = ec._PostgresBranchFacets_states(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -2540,7 +3945,7 @@ func (ec *executionContext) _PostgresInstanceFacets(ctx context.Context, sel ast } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "highAvailability": + case "labels": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -2549,7 +3954,7 @@ func (ec *executionContext) _PostgresInstanceFacets(ctx context.Context, sel ast ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._PostgresInstanceFacets_highAvailability(ctx, field, obj) + res = ec._PostgresBranchFacets_labels(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -2576,78 +3981,245 @@ func (ec *executionContext) _PostgresInstanceFacets(ctx context.Context, sel ast } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "majorVersions": - field := field + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._PostgresInstanceFacets_majorVersions(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var postgresBranchStateFacetItemImplementors = []string{"PostgresBranchStateFacetItem"} + +func (ec *executionContext) _PostgresBranchStateFacetItem(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresBranchStateFacetItem) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresBranchStateFacetItemImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PostgresBranchStateFacetItem") + case "state": + out.Values[i] = ec._PostgresBranchStateFacetItem_state(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "count": + out.Values[i] = ec._PostgresBranchStateFacetItem_count(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var postgresDeletedActivityLogEntryImplementors = []string{"PostgresDeletedActivityLogEntry", "ActivityLogEntry", "Node"} + +func (ec *executionContext) _PostgresDeletedActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresDeletedActivityLogEntry) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresDeletedActivityLogEntryImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PostgresDeletedActivityLogEntry") + case "id": + out.Values[i] = ec._PostgresDeletedActivityLogEntry_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "actor": + out.Values[i] = ec._PostgresDeletedActivityLogEntry_actor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "gitHubActorClaims": + out.Values[i] = ec._PostgresDeletedActivityLogEntry_gitHubActorClaims(ctx, field, obj) + case "createdAt": + out.Values[i] = ec._PostgresDeletedActivityLogEntry_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "message": + out.Values[i] = ec._PostgresDeletedActivityLogEntry_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceType": + out.Values[i] = ec._PostgresDeletedActivityLogEntry_resourceType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceName": + out.Values[i] = ec._PostgresDeletedActivityLogEntry_resourceName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "teamSlug": + out.Values[i] = ec._PostgresDeletedActivityLogEntry_teamSlug(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "environmentName": + out.Values[i] = ec._PostgresDeletedActivityLogEntry_environmentName(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var postgresGrantAccessActivityLogEntryImplementors = []string{"PostgresGrantAccessActivityLogEntry", "ActivityLogEntry", "Node"} + +func (ec *executionContext) _PostgresGrantAccessActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresGrantAccessActivityLogEntry) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresGrantAccessActivityLogEntryImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PostgresGrantAccessActivityLogEntry") + case "id": + out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "actor": + out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_actor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "gitHubActorClaims": + out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_gitHubActorClaims(ctx, field, obj) + case "createdAt": + out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "message": + out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceType": + out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_resourceType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceName": + out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_resourceName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "teamSlug": + out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_teamSlug(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "environmentName": + out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_environmentName(ctx, field, obj) + case "data": + out.Values[i] = ec._PostgresGrantAccessActivityLogEntry_data(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "labels": - field := field + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._PostgresInstanceFacets_labels(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res - } + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) + return out +} - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } +var postgresGrantAccessActivityLogEntryDataImplementors = []string{"PostgresGrantAccessActivityLogEntryData"} - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) +func (ec *executionContext) _PostgresGrantAccessActivityLogEntryData(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresGrantAccessActivityLogEntryData) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresGrantAccessActivityLogEntryDataImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PostgresGrantAccessActivityLogEntryData") + case "grantee": + out.Values[i] = ec._PostgresGrantAccessActivityLogEntryData_grantee(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "until": + out.Values[i] = ec._PostgresGrantAccessActivityLogEntryData_until(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -2671,27 +4243,56 @@ func (ec *executionContext) _PostgresInstanceFacets(ctx context.Context, sel ast return out } -var postgresInstanceMaintenanceWindowImplementors = []string{"PostgresInstanceMaintenanceWindow"} +var postgresPersonalAccessConnectionActivityLogEntryImplementors = []string{"PostgresPersonalAccessConnectionActivityLogEntry", "ActivityLogEntry", "Node"} -func (ec *executionContext) _PostgresInstanceMaintenanceWindow(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresInstanceMaintenanceWindow) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, postgresInstanceMaintenanceWindowImplementors) +func (ec *executionContext) _PostgresPersonalAccessConnectionActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresPersonalAccessConnectionActivityLogEntry) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresPersonalAccessConnectionActivityLogEntryImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("PostgresInstanceMaintenanceWindow") - case "day": - out.Values[i] = ec._PostgresInstanceMaintenanceWindow_day(ctx, field, obj) + out.Values[i] = graphql.MarshalString("PostgresPersonalAccessConnectionActivityLogEntry") + case "id": + out.Values[i] = ec._PostgresPersonalAccessConnectionActivityLogEntry_id(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "actor": + out.Values[i] = ec._PostgresPersonalAccessConnectionActivityLogEntry_actor(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "gitHubActorClaims": + out.Values[i] = ec._PostgresPersonalAccessConnectionActivityLogEntry_gitHubActorClaims(ctx, field, obj) + case "createdAt": + out.Values[i] = ec._PostgresPersonalAccessConnectionActivityLogEntry_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "message": + out.Values[i] = ec._PostgresPersonalAccessConnectionActivityLogEntry_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceType": + out.Values[i] = ec._PostgresPersonalAccessConnectionActivityLogEntry_resourceType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceName": + out.Values[i] = ec._PostgresPersonalAccessConnectionActivityLogEntry_resourceName(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "hour": - out.Values[i] = ec._PostgresInstanceMaintenanceWindow_hour(ctx, field, obj) + case "teamSlug": + out.Values[i] = ec._PostgresPersonalAccessConnectionActivityLogEntry_teamSlug(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } + case "environmentName": + out.Values[i] = ec._PostgresPersonalAccessConnectionActivityLogEntry_environmentName(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -2715,29 +4316,58 @@ func (ec *executionContext) _PostgresInstanceMaintenanceWindow(ctx context.Conte return out } -var postgresInstanceResourcesImplementors = []string{"PostgresInstanceResources"} +var postgresPersonalAccessCreatedActivityLogEntryImplementors = []string{"PostgresPersonalAccessCreatedActivityLogEntry", "ActivityLogEntry", "Node"} -func (ec *executionContext) _PostgresInstanceResources(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresInstanceResources) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, postgresInstanceResourcesImplementors) +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntry(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntry) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresPersonalAccessCreatedActivityLogEntryImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("PostgresInstanceResources") - case "cpu": - out.Values[i] = ec._PostgresInstanceResources_cpu(ctx, field, obj) + out.Values[i] = graphql.MarshalString("PostgresPersonalAccessCreatedActivityLogEntry") + case "id": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntry_id(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "memory": - out.Values[i] = ec._PostgresInstanceResources_memory(ctx, field, obj) + case "actor": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntry_actor(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "diskSize": - out.Values[i] = ec._PostgresInstanceResources_diskSize(ctx, field, obj) + case "gitHubActorClaims": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntry_gitHubActorClaims(ctx, field, obj) + case "createdAt": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntry_createdAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "message": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntry_message(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceType": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntry_resourceType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "resourceName": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntry_resourceName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "teamSlug": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntry_teamSlug(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "environmentName": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntry_environmentName(ctx, field, obj) + case "data": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntry_data(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -2764,24 +4394,31 @@ func (ec *executionContext) _PostgresInstanceResources(ctx context.Context, sel return out } -var postgresInstanceStateFacetItemImplementors = []string{"PostgresInstanceStateFacetItem"} +var postgresPersonalAccessCreatedActivityLogEntryDataImplementors = []string{"PostgresPersonalAccessCreatedActivityLogEntryData"} -func (ec *executionContext) _PostgresInstanceStateFacetItem(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresInstanceStateFacetItem) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, postgresInstanceStateFacetItemImplementors) +func (ec *executionContext) _PostgresPersonalAccessCreatedActivityLogEntryData(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresPersonalAccessCreatedActivityLogEntryData) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresPersonalAccessCreatedActivityLogEntryDataImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("PostgresInstanceStateFacetItem") - case "state": - out.Values[i] = ec._PostgresInstanceStateFacetItem_state(ctx, field, obj) + out.Values[i] = graphql.MarshalString("PostgresPersonalAccessCreatedActivityLogEntryData") + case "username": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntryData_username(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } - case "count": - out.Values[i] = ec._PostgresInstanceStateFacetItem_count(ctx, field, obj) + case "accessLevel": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntryData_accessLevel(ctx, field, obj) + case "expiresAt": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntryData_expiresAt(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "reason": + out.Values[i] = ec._PostgresPersonalAccessCreatedActivityLogEntryData_reason(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -2808,19 +4445,59 @@ func (ec *executionContext) _PostgresInstanceStateFacetItem(ctx context.Context, return out } -var teamInventoryCountPostgresInstancesImplementors = []string{"TeamInventoryCountPostgresInstances"} +var postgresResourcesImplementors = []string{"PostgresResources"} + +func (ec *executionContext) _PostgresResources(ctx context.Context, sel ast.SelectionSet, obj *postgres.PostgresResources) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, postgresResourcesImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("PostgresResources") + case "cpu": + out.Values[i] = ec._PostgresResources_cpu(ctx, field, obj) + case "memory": + out.Values[i] = ec._PostgresResources_memory(ctx, field, obj) + case "diskSize": + out.Values[i] = ec._PostgresResources_diskSize(ctx, field, obj) + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.Deferred, int32(min(len(deferred), math.MaxInt32))) + + for label, dfs := range deferred { + ec.ProcessDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + +var teamInventoryCountPostgresBranchesImplementors = []string{"TeamInventoryCountPostgresBranches"} -func (ec *executionContext) _TeamInventoryCountPostgresInstances(ctx context.Context, sel ast.SelectionSet, obj *postgres.TeamInventoryCountPostgresInstances) graphql.Marshaler { - fields := graphql.CollectFields(ec.OperationContext, sel, teamInventoryCountPostgresInstancesImplementors) +func (ec *executionContext) _TeamInventoryCountPostgresBranches(ctx context.Context, sel ast.SelectionSet, obj *postgres.TeamInventoryCountPostgresBranches) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, teamInventoryCountPostgresBranchesImplementors) out := graphql.NewFieldSet(fields) deferred := make(map[string]*graphql.FieldSet) for i, field := range fields { switch field.Name { case "__typename": - out.Values[i] = graphql.MarshalString("TeamInventoryCountPostgresInstances") + out.Values[i] = graphql.MarshalString("TeamInventoryCountPostgresBranches") case "total": - out.Values[i] = ec._TeamInventoryCountPostgresInstances_total(ctx, field, obj) + out.Values[i] = ec._TeamInventoryCountPostgresBranches_total(ctx, field, obj) if out.Values[i] == graphql.Null { out.Invalids++ } @@ -2851,23 +4528,42 @@ func (ec *executionContext) _TeamInventoryCountPostgresInstances(ctx context.Con // region ***************************** type.gotpl ***************************** -func (ec *executionContext) unmarshalNDeletePostgresInput2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐDeletePostgresInput(ctx context.Context, v any) (postgres.DeletePostgresInput, error) { - res, err := ec.unmarshalInputDeletePostgresInput(ctx, v) +func (ec *executionContext) unmarshalNCreatePostgresAccessInput2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐCreatePostgresAccessInput(ctx context.Context, v any) (postgres.CreatePostgresAccessInput, error) { + res, err := ec.unmarshalInputCreatePostgresAccessInput(ctx, v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNCreatePostgresAccessPayload2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐCreatePostgresAccessPayload(ctx context.Context, sel ast.SelectionSet, v postgres.CreatePostgresAccessPayload) graphql.Marshaler { + return ec._CreatePostgresAccessPayload(ctx, sel, &v) +} + +func (ec *executionContext) marshalNCreatePostgresAccessPayload2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐCreatePostgresAccessPayload(ctx context.Context, sel ast.SelectionSet, v *postgres.CreatePostgresAccessPayload) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._CreatePostgresAccessPayload(ctx, sel, v) +} + +func (ec *executionContext) unmarshalNDeletePostgresBranchInput2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐDeletePostgresBranchInput(ctx context.Context, v any) (postgres.DeletePostgresBranchInput, error) { + res, err := ec.unmarshalInputDeletePostgresBranchInput(ctx, v) return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNDeletePostgresPayload2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐDeletePostgresPayload(ctx context.Context, sel ast.SelectionSet, v postgres.DeletePostgresPayload) graphql.Marshaler { - return ec._DeletePostgresPayload(ctx, sel, &v) +func (ec *executionContext) marshalNDeletePostgresBranchPayload2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐDeletePostgresBranchPayload(ctx context.Context, sel ast.SelectionSet, v postgres.DeletePostgresBranchPayload) graphql.Marshaler { + return ec._DeletePostgresBranchPayload(ctx, sel, &v) } -func (ec *executionContext) marshalNDeletePostgresPayload2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐDeletePostgresPayload(ctx context.Context, sel ast.SelectionSet, v *postgres.DeletePostgresPayload) graphql.Marshaler { +func (ec *executionContext) marshalNDeletePostgresBranchPayload2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐDeletePostgresBranchPayload(ctx context.Context, sel ast.SelectionSet, v *postgres.DeletePostgresBranchPayload) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._DeletePostgresPayload(ctx, sel, v) + return ec._DeletePostgresBranchPayload(ctx, sel, v) } func (ec *executionContext) unmarshalNGrantPostgresAccessInput2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐGrantPostgresAccessInput(ctx context.Context, v any) (postgres.GrantPostgresAccessInput, error) { @@ -2889,25 +4585,63 @@ func (ec *executionContext) marshalNGrantPostgresAccessPayload2ᚖgithubᚗcom return ec._GrantPostgresAccessPayload(ctx, sel, v) } -func (ec *executionContext) marshalNPostgresGrantAccessActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresGrantAccessActivityLogEntryData(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresGrantAccessActivityLogEntryData) graphql.Marshaler { +func (ec *executionContext) marshalNPostgres2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgres(ctx context.Context, sel ast.SelectionSet, v postgres.Postgres) graphql.Marshaler { + return ec._Postgres(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPostgres2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgres(ctx context.Context, sel ast.SelectionSet, v *postgres.Postgres) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._PostgresGrantAccessActivityLogEntryData(ctx, sel, v) + return ec._Postgres(ctx, sel, v) +} + +func (ec *executionContext) marshalNPostgresAccess2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccess(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresAccess) graphql.Marshaler { + return ec._PostgresAccess(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPostgresAccess2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccess(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresAccess) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PostgresAccess(ctx, sel, v) } -func (ec *executionContext) marshalNPostgresInstance2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstance(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresInstance) graphql.Marshaler { - return ec._PostgresInstance(ctx, sel, &v) +func (ec *executionContext) unmarshalNPostgresAccessLevel2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccessLevel(ctx context.Context, v any) (postgres.PostgresAccessLevel, error) { + var res postgres.PostgresAccessLevel + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNPostgresAccessLevel2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccessLevel(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresAccessLevel) graphql.Marshaler { + return v +} + +func (ec *executionContext) unmarshalNPostgresAccessState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccessState(ctx context.Context, v any) (postgres.PostgresAccessState, error) { + var res postgres.PostgresAccessState + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalNPostgresAccessState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccessState(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresAccessState) graphql.Marshaler { + return v } -func (ec *executionContext) marshalNPostgresInstance2ᚕᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceᚄ(ctx context.Context, sel ast.SelectionSet, v []*postgres.PostgresInstance) graphql.Marshaler { +func (ec *executionContext) marshalNPostgresBranch2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranch(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresBranch) graphql.Marshaler { + return ec._PostgresBranch(ctx, sel, &v) +} + +func (ec *executionContext) marshalNPostgresBranch2ᚕᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchᚄ(ctx context.Context, sel ast.SelectionSet, v []*postgres.PostgresBranch) graphql.Marshaler { ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { fc := graphql.GetFieldContext(ctx) fc.Result = &v[i] - return ec.marshalNPostgresInstance2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstance(ctx, sel, v[i]) + return ec.marshalNPostgresBranch2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranch(ctx, sel, v[i]) }) for _, e := range ret { @@ -2919,43 +4653,39 @@ func (ec *executionContext) marshalNPostgresInstance2ᚕᚖgithubᚗcomᚋnais return ret } -func (ec *executionContext) marshalNPostgresInstance2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstance(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresInstance) graphql.Marshaler { +func (ec *executionContext) marshalNPostgresBranch2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranch(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresBranch) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._PostgresInstance(ctx, sel, v) -} - -func (ec *executionContext) marshalNPostgresInstanceAudit2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceAudit(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresInstanceAudit) graphql.Marshaler { - return ec._PostgresInstanceAudit(ctx, sel, &v) + return ec._PostgresBranch(ctx, sel, v) } -func (ec *executionContext) marshalNPostgresInstanceConnection2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐFacetableConnection(ctx context.Context, sel ast.SelectionSet, v pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter]) graphql.Marshaler { - return ec._PostgresInstanceConnection(ctx, sel, &v) +func (ec *executionContext) marshalNPostgresBranchConnection2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐFacetableConnection(ctx context.Context, sel ast.SelectionSet, v pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) graphql.Marshaler { + return ec._PostgresBranchConnection(ctx, sel, &v) } -func (ec *executionContext) marshalNPostgresInstanceConnection2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐFacetableConnection(ctx context.Context, sel ast.SelectionSet, v *pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter]) graphql.Marshaler { +func (ec *executionContext) marshalNPostgresBranchConnection2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐFacetableConnection(ctx context.Context, sel ast.SelectionSet, v *pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._PostgresInstanceConnection(ctx, sel, v) + return ec._PostgresBranchConnection(ctx, sel, v) } -func (ec *executionContext) marshalNPostgresInstanceEdge2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐEdge(ctx context.Context, sel ast.SelectionSet, v pagination.Edge[*postgres.PostgresInstance]) graphql.Marshaler { - return ec._PostgresInstanceEdge(ctx, sel, &v) +func (ec *executionContext) marshalNPostgresBranchEdge2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐEdge(ctx context.Context, sel ast.SelectionSet, v pagination.Edge[*postgres.PostgresBranch]) graphql.Marshaler { + return ec._PostgresBranchEdge(ctx, sel, &v) } -func (ec *executionContext) marshalNPostgresInstanceEdge2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐEdgeᚄ(ctx context.Context, sel ast.SelectionSet, v []pagination.Edge[*postgres.PostgresInstance]) graphql.Marshaler { +func (ec *executionContext) marshalNPostgresBranchEdge2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐEdgeᚄ(ctx context.Context, sel ast.SelectionSet, v []pagination.Edge[*postgres.PostgresBranch]) graphql.Marshaler { ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { fc := graphql.GetFieldContext(ctx) fc.Result = &v[i] - return ec.marshalNPostgresInstanceEdge2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐEdge(ctx, sel, v[i]) + return ec.marshalNPostgresBranchEdge2githubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐEdge(ctx, sel, v[i]) }) for _, e := range ret { @@ -2967,45 +4697,35 @@ func (ec *executionContext) marshalNPostgresInstanceEdge2ᚕgithubᚗcomᚋnais return ret } -func (ec *executionContext) unmarshalNPostgresInstanceOrderField2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceOrderField(ctx context.Context, v any) (postgres.PostgresInstanceOrderField, error) { - var res postgres.PostgresInstanceOrderField +func (ec *executionContext) unmarshalNPostgresBranchOrderField2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchOrderField(ctx context.Context, v any) (postgres.PostgresBranchOrderField, error) { + var res postgres.PostgresBranchOrderField err := res.UnmarshalGQL(v) return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNPostgresInstanceOrderField2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceOrderField(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresInstanceOrderField) graphql.Marshaler { +func (ec *executionContext) marshalNPostgresBranchOrderField2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchOrderField(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresBranchOrderField) graphql.Marshaler { return v } -func (ec *executionContext) marshalNPostgresInstanceResources2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceResources(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresInstanceResources) graphql.Marshaler { - if v == nil { - if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") - } - return graphql.Null - } - return ec._PostgresInstanceResources(ctx, sel, v) -} - -func (ec *executionContext) unmarshalNPostgresInstanceState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceState(ctx context.Context, v any) (postgres.PostgresInstanceState, error) { - var res postgres.PostgresInstanceState +func (ec *executionContext) unmarshalNPostgresBranchState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchState(ctx context.Context, v any) (postgres.PostgresBranchState, error) { + var res postgres.PostgresBranchState err := res.UnmarshalGQL(v) return res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) marshalNPostgresInstanceState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceState(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresInstanceState) graphql.Marshaler { +func (ec *executionContext) marshalNPostgresBranchState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchState(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresBranchState) graphql.Marshaler { return v } -func (ec *executionContext) marshalNPostgresInstanceStateFacetItem2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceStateFacetItem(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresInstanceStateFacetItem) graphql.Marshaler { - return ec._PostgresInstanceStateFacetItem(ctx, sel, &v) +func (ec *executionContext) marshalNPostgresBranchStateFacetItem2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchStateFacetItem(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresBranchStateFacetItem) graphql.Marshaler { + return ec._PostgresBranchStateFacetItem(ctx, sel, &v) } -func (ec *executionContext) marshalNPostgresInstanceStateFacetItem2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceStateFacetItemᚄ(ctx context.Context, sel ast.SelectionSet, v []postgres.PostgresInstanceStateFacetItem) graphql.Marshaler { +func (ec *executionContext) marshalNPostgresBranchStateFacetItem2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchStateFacetItemᚄ(ctx context.Context, sel ast.SelectionSet, v []postgres.PostgresBranchStateFacetItem) graphql.Marshaler { ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { fc := graphql.GetFieldContext(ctx) fc.Result = &v[i] - return ec.marshalNPostgresInstanceStateFacetItem2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceStateFacetItem(ctx, sel, v[i]) + return ec.marshalNPostgresBranchStateFacetItem2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchStateFacetItem(ctx, sel, v[i]) }) for _, e := range ret { @@ -3017,61 +4737,108 @@ func (ec *executionContext) marshalNPostgresInstanceStateFacetItem2ᚕgithubᚗc return ret } -func (ec *executionContext) marshalNTeamInventoryCountPostgresInstances2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐTeamInventoryCountPostgresInstances(ctx context.Context, sel ast.SelectionSet, v postgres.TeamInventoryCountPostgresInstances) graphql.Marshaler { - return ec._TeamInventoryCountPostgresInstances(ctx, sel, &v) +func (ec *executionContext) marshalNPostgresGrantAccessActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresGrantAccessActivityLogEntryData(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresGrantAccessActivityLogEntryData) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PostgresGrantAccessActivityLogEntryData(ctx, sel, v) +} + +func (ec *executionContext) marshalNPostgresPersonalAccessCreatedActivityLogEntryData2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresPersonalAccessCreatedActivityLogEntryData(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresPersonalAccessCreatedActivityLogEntryData) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._PostgresPersonalAccessCreatedActivityLogEntryData(ctx, sel, v) +} + +func (ec *executionContext) marshalNPostgresResources2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresResources(ctx context.Context, sel ast.SelectionSet, v postgres.PostgresResources) graphql.Marshaler { + return ec._PostgresResources(ctx, sel, &v) +} + +func (ec *executionContext) marshalNTeamInventoryCountPostgresBranches2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐTeamInventoryCountPostgresBranches(ctx context.Context, sel ast.SelectionSet, v postgres.TeamInventoryCountPostgresBranches) graphql.Marshaler { + return ec._TeamInventoryCountPostgresBranches(ctx, sel, &v) } -func (ec *executionContext) marshalNTeamInventoryCountPostgresInstances2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐTeamInventoryCountPostgresInstances(ctx context.Context, sel ast.SelectionSet, v *postgres.TeamInventoryCountPostgresInstances) graphql.Marshaler { +func (ec *executionContext) marshalNTeamInventoryCountPostgresBranches2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐTeamInventoryCountPostgresBranches(ctx context.Context, sel ast.SelectionSet, v *postgres.TeamInventoryCountPostgresBranches) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } - return ec._TeamInventoryCountPostgresInstances(ctx, sel, v) + return ec._TeamInventoryCountPostgresBranches(ctx, sel, v) } -func (ec *executionContext) marshalOPostgresInstanceFacets2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceFacets(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresInstanceFacets) graphql.Marshaler { +func (ec *executionContext) marshalOPostgresAccessConnectionDetails2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccessConnectionDetails(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresAccessConnectionDetails) graphql.Marshaler { if v == nil { return graphql.Null } - return ec._PostgresInstanceFacets(ctx, sel, v) + return ec._PostgresAccessConnectionDetails(ctx, sel, v) } -func (ec *executionContext) unmarshalOPostgresInstanceFilter2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceFilter(ctx context.Context, v any) (*postgres.PostgresInstanceFilter, error) { +func (ec *executionContext) unmarshalOPostgresAccessLevel2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccessLevel(ctx context.Context, v any) (*postgres.PostgresAccessLevel, error) { if v == nil { return nil, nil } - res, err := ec.unmarshalInputPostgresInstanceFilter(ctx, v) - return &res, graphql.ErrorOnPath(ctx, err) + var res = new(postgres.PostgresAccessLevel) + err := res.UnmarshalGQL(v) + return res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOPostgresAccessLevel2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccessLevel(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresAccessLevel) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return v +} + +func (ec *executionContext) marshalOPostgresBranch2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranch(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresBranch) graphql.Marshaler { + if v == nil { + return graphql.Null + } + return ec._PostgresBranch(ctx, sel, v) } -func (ec *executionContext) marshalOPostgresInstanceMaintenanceWindow2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceMaintenanceWindow(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresInstanceMaintenanceWindow) graphql.Marshaler { +func (ec *executionContext) marshalOPostgresBranchFacets2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchFacets(ctx context.Context, sel ast.SelectionSet, v *postgres.PostgresBranchFacets) graphql.Marshaler { if v == nil { return graphql.Null } - return ec._PostgresInstanceMaintenanceWindow(ctx, sel, v) + return ec._PostgresBranchFacets(ctx, sel, v) +} + +func (ec *executionContext) unmarshalOPostgresBranchFilter2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchFilter(ctx context.Context, v any) (*postgres.PostgresBranchFilter, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalInputPostgresBranchFilter(ctx, v) + return &res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) unmarshalOPostgresInstanceOrder2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceOrder(ctx context.Context, v any) (*postgres.PostgresInstanceOrder, error) { +func (ec *executionContext) unmarshalOPostgresBranchOrder2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchOrder(ctx context.Context, v any) (*postgres.PostgresBranchOrder, error) { if v == nil { return nil, nil } - res, err := ec.unmarshalInputPostgresInstanceOrder(ctx, v) + res, err := ec.unmarshalInputPostgresBranchOrder(ctx, v) return &res, graphql.ErrorOnPath(ctx, err) } -func (ec *executionContext) unmarshalOPostgresInstanceState2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceStateᚄ(ctx context.Context, v any) ([]postgres.PostgresInstanceState, error) { +func (ec *executionContext) unmarshalOPostgresBranchState2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchStateᚄ(ctx context.Context, v any) ([]postgres.PostgresBranchState, error) { if v == nil { return nil, nil } var vSlice []any vSlice = graphql.CoerceList(v) var err error - res := make([]postgres.PostgresInstanceState, len(vSlice)) + res := make([]postgres.PostgresBranchState, len(vSlice)) for i := range vSlice { ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) - res[i], err = ec.unmarshalNPostgresInstanceState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceState(ctx, vSlice[i]) + res[i], err = ec.unmarshalNPostgresBranchState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchState(ctx, vSlice[i]) if err != nil { return nil, err } @@ -3079,14 +4846,14 @@ func (ec *executionContext) unmarshalOPostgresInstanceState2ᚕgithubᚗcomᚋna return res, nil } -func (ec *executionContext) marshalOPostgresInstanceState2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceStateᚄ(ctx context.Context, sel ast.SelectionSet, v []postgres.PostgresInstanceState) graphql.Marshaler { +func (ec *executionContext) marshalOPostgresBranchState2ᚕgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchStateᚄ(ctx context.Context, sel ast.SelectionSet, v []postgres.PostgresBranchState) graphql.Marshaler { if v == nil { return graphql.Null } ret := graphql.MarshalSliceConcurrently(ctx, len(v), 0, false, func(ctx context.Context, i int) graphql.Marshaler { fc := graphql.GetFieldContext(ctx) fc.Result = &v[i] - return ec.marshalNPostgresInstanceState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceState(ctx, sel, v[i]) + return ec.marshalNPostgresBranchState2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchState(ctx, sel, v[i]) }) for _, e := range ret { diff --git a/internal/graph/gengql/root_.generated.go b/internal/graph/gengql/root_.generated.go index 0164e70a8..30d83560f 100644 --- a/internal/graph/gengql/root_.generated.go +++ b/internal/graph/gengql/root_.generated.go @@ -105,9 +105,10 @@ type ResolverRoot interface { OpenSearchConnection() OpenSearchConnectionResolver OpenSearchIssue() OpenSearchIssueResolver OpenSearchMaintenance() OpenSearchMaintenanceResolver - PostgresInstance() PostgresInstanceResolver - PostgresInstanceAudit() PostgresInstanceAuditResolver - PostgresInstanceConnection() PostgresInstanceConnectionResolver + Postgres() PostgresResolver + PostgresAccess() PostgresAccessResolver + PostgresBranch() PostgresBranchResolver + PostgresBranchConnection() PostgresBranchConnectionResolver PrometheusAlert() PrometheusAlertResolver Query() QueryResolver Reconciler() ReconcilerResolver @@ -261,7 +262,7 @@ type ComplexityRoot struct { Name func(childComplexity int) int NetworkPolicy func(childComplexity int) int OpenSearch func(childComplexity int) int - PostgresInstances func(childComplexity int, orderBy *postgres.PostgresInstanceOrder) int + PostgresBranches func(childComplexity int, orderBy *postgres.PostgresBranchOrder) int Resources func(childComplexity int) int SQLInstances func(childComplexity int, orderBy *sqlinstance.SQLInstanceOrder) int Secrets func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) int @@ -731,6 +732,11 @@ type ComplexityRoot struct { OpenSearch func(childComplexity int) int } + CreatePostgresAccessPayload struct { + ExpiresAt func(childComplexity int) int + Name func(childComplexity int) int + } + CreateSecretPayload struct { Secret func(childComplexity int) int } @@ -793,8 +799,8 @@ type ComplexityRoot struct { OpenSearchDeleted func(childComplexity int) int } - DeletePostgresPayload struct { - PostgresDeleted func(childComplexity int) int + DeletePostgresBranchPayload struct { + PostgresBranchDeleted func(childComplexity int) int } DeleteSecretPayload struct { @@ -1240,7 +1246,7 @@ type ComplexityRoot struct { Name func(childComplexity int) int NetworkPolicy func(childComplexity int) int OpenSearch func(childComplexity int) int - PostgresInstances func(childComplexity int, orderBy *postgres.PostgresInstanceOrder) int + PostgresBranches func(childComplexity int, orderBy *postgres.PostgresBranchOrder) int Resources func(childComplexity int) int Runs func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) int SQLInstances func(childComplexity int, orderBy *sqlinstance.SQLInstanceOrder) int @@ -1620,6 +1626,7 @@ type ComplexityRoot struct { CreateKafkaCredentials func(childComplexity int, input kafkatopic.CreateKafkaCredentialsInput) int CreateOpenSearch func(childComplexity int, input opensearch.CreateOpenSearchInput) int CreateOpenSearchCredentials func(childComplexity int, input opensearch.CreateOpenSearchCredentialsInput) int + CreatePostgresAccess func(childComplexity int, input postgres.CreatePostgresAccessInput) int CreateSecret func(childComplexity int, input secret.CreateSecretInput) int CreateServiceAccount func(childComplexity int, input serviceaccount.CreateServiceAccountInput) int CreateServiceAccountToken func(childComplexity int, input serviceaccount.CreateServiceAccountTokenInput) int @@ -1633,7 +1640,7 @@ type ComplexityRoot struct { DeleteJob func(childComplexity int, input job.DeleteJobInput) int DeleteJobRun func(childComplexity int, input job.DeleteJobRunInput) int DeleteOpenSearch func(childComplexity int, input opensearch.DeleteOpenSearchInput) int - DeletePostgres func(childComplexity int, input postgres.DeletePostgresInput) int + DeletePostgresBranch func(childComplexity int, input postgres.DeletePostgresBranchInput) int DeleteSecret func(childComplexity int, input secret.DeleteSecretInput) int DeleteServiceAccount func(childComplexity int, input serviceaccount.DeleteServiceAccountInput) int DeleteServiceAccountToken func(childComplexity int, input serviceaccount.DeleteServiceAccountTokenInput) int @@ -1887,6 +1894,76 @@ type ComplexityRoot struct { TotalCount func(childComplexity int) int } + Postgres struct { + ActiveBranch func(childComplexity int) int + Branch func(childComplexity int, name string) int + Branches func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *postgres.PostgresBranchOrder) int + HighAvailability func(childComplexity int) int + ID func(childComplexity int) int + Labels func(childComplexity int) int + MajorVersion func(childComplexity int) int + Name func(childComplexity int) int + Resources func(childComplexity int) int + } + + PostgresAccess struct { + AccessLevel func(childComplexity int) int + Connection func(childComplexity int) int + ExpiresAt func(childComplexity int) int + ID func(childComplexity int) int + Message func(childComplexity int) int + Name func(childComplexity int) int + PostgresBranch func(childComplexity int) int + RelayAccess func(childComplexity int) int + State func(childComplexity int) int + Team func(childComplexity int) int + TeamEnvironment func(childComplexity int) int + } + + PostgresAccessConnectionDetails struct { + CACertificate func(childComplexity int) int + Password func(childComplexity int) int + RelayAccess func(childComplexity int) int + RelayEndpoint func(childComplexity int) int + RelayToken func(childComplexity int) int + ServerName func(childComplexity int) int + Username func(childComplexity int) int + } + + PostgresBranch struct { + ID func(childComplexity int) int + Labels func(childComplexity int) int + Name func(childComplexity int) int + Postgres func(childComplexity int) int + State func(childComplexity int) int + Team func(childComplexity int) int + TeamEnvironment func(childComplexity int) int + Workloads func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) int + } + + PostgresBranchConnection struct { + Edges func(childComplexity int) int + Facets func(childComplexity int) int + Nodes func(childComplexity int) int + PageInfo func(childComplexity int) int + } + + PostgresBranchEdge struct { + Cursor func(childComplexity int) int + Node func(childComplexity int) int + } + + PostgresBranchFacets struct { + Environments func(childComplexity int) int + Labels func(childComplexity int) int + States func(childComplexity int) int + } + + PostgresBranchStateFacetItem struct { + Count func(childComplexity int) int + State func(childComplexity int) int + } + PostgresDeletedActivityLogEntry struct { Actor func(childComplexity int) int CreatedAt func(childComplexity int) int @@ -1917,63 +1994,44 @@ type ComplexityRoot struct { Until func(childComplexity int) int } - PostgresInstance struct { - Audit func(childComplexity int) int - HighAvailability func(childComplexity int) int + PostgresPersonalAccessConnectionActivityLogEntry struct { + Actor func(childComplexity int) int + CreatedAt func(childComplexity int) int + EnvironmentName func(childComplexity int) int + GitHubActorClaims func(childComplexity int) int ID func(childComplexity int) int - Labels func(childComplexity int) int - MaintenanceWindow func(childComplexity int) int - MajorVersion func(childComplexity int) int - Name func(childComplexity int) int - Resources func(childComplexity int) int - State func(childComplexity int) int - Team func(childComplexity int) int - TeamEnvironment func(childComplexity int) int - Workloads func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) int - } - - PostgresInstanceAudit struct { - Enabled func(childComplexity int) int - StatementClasses func(childComplexity int) int - URL func(childComplexity int) int - } - - PostgresInstanceConnection struct { - Edges func(childComplexity int) int - Facets func(childComplexity int) int - Nodes func(childComplexity int) int - PageInfo func(childComplexity int) int - } - - PostgresInstanceEdge struct { - Cursor func(childComplexity int) int - Node func(childComplexity int) int + Message func(childComplexity int) int + ResourceName func(childComplexity int) int + ResourceType func(childComplexity int) int + TeamSlug func(childComplexity int) int } - PostgresInstanceFacets struct { - Environments func(childComplexity int) int - HighAvailability func(childComplexity int) int - Labels func(childComplexity int) int - MajorVersions func(childComplexity int) int - States func(childComplexity int) int + PostgresPersonalAccessCreatedActivityLogEntry struct { + Actor func(childComplexity int) int + CreatedAt func(childComplexity int) int + Data func(childComplexity int) int + EnvironmentName func(childComplexity int) int + GitHubActorClaims func(childComplexity int) int + ID func(childComplexity int) int + Message func(childComplexity int) int + ResourceName func(childComplexity int) int + ResourceType func(childComplexity int) int + TeamSlug func(childComplexity int) int } - PostgresInstanceMaintenanceWindow struct { - Day func(childComplexity int) int - Hour func(childComplexity int) int + PostgresPersonalAccessCreatedActivityLogEntryData struct { + AccessLevel func(childComplexity int) int + ExpiresAt func(childComplexity int) int + Reason func(childComplexity int) int + Username func(childComplexity int) int } - PostgresInstanceResources struct { + PostgresResources struct { CPU func(childComplexity int) int DiskSize func(childComplexity int) int Memory func(childComplexity int) int } - PostgresInstanceStateFacetItem struct { - Count func(childComplexity int) int - State func(childComplexity int) int - } - Price struct { Value func(childComplexity int) int } @@ -2873,7 +2931,7 @@ type ComplexityRoot struct { Member func(childComplexity int, email string) int Members func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *team.TeamMemberOrder) int OpenSearches func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *opensearch.OpenSearchOrder, filter *opensearch.OpenSearchFilter) int - PostgresInstances func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *postgres.PostgresInstanceOrder, filter *postgres.PostgresInstanceFilter) int + PostgresBranches func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *postgres.PostgresBranchOrder, filter *postgres.PostgresBranchFilter) int Purpose func(childComplexity int) int Repositories func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *repository.RepositoryOrder, filter *repository.TeamRepositoryFilter) int SQLInstances func(childComplexity int, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *sqlinstance.SQLInstanceOrder, filter *sqlinstance.SQLInstanceFilter) int @@ -3002,7 +3060,8 @@ type ComplexityRoot struct { KafkaTopic func(childComplexity int, name string) int Name func(childComplexity int) int OpenSearch func(childComplexity int, name string) int - PostgresInstance func(childComplexity int, name string) int + Postgres func(childComplexity int, name string) int + PostgresAccess func(childComplexity int, name string) int SQLInstance func(childComplexity int, name string) int Secret func(childComplexity int, name string) int SlackAlertsChannel func(childComplexity int) int @@ -3099,7 +3158,7 @@ type ComplexityRoot struct { Total func(childComplexity int) int } - TeamInventoryCountPostgresInstances struct { + TeamInventoryCountPostgresBranches struct { Total func(childComplexity int) int } @@ -3116,17 +3175,17 @@ type ComplexityRoot struct { } TeamInventoryCounts struct { - Applications func(childComplexity int) int - BigQueryDatasets func(childComplexity int) int - Buckets func(childComplexity int) int - Configs func(childComplexity int) int - Jobs func(childComplexity int) int - KafkaTopics func(childComplexity int) int - OpenSearches func(childComplexity int) int - PostgresInstances func(childComplexity int) int - SQLInstances func(childComplexity int) int - Secrets func(childComplexity int) int - Valkeys func(childComplexity int) int + Applications func(childComplexity int) int + BigQueryDatasets func(childComplexity int) int + Buckets func(childComplexity int) int + Configs func(childComplexity int) int + Jobs func(childComplexity int) int + KafkaTopics func(childComplexity int) int + OpenSearches func(childComplexity int) int + PostgresBranches func(childComplexity int) int + SQLInstances func(childComplexity int) int + Secrets func(childComplexity int) int + Valkeys func(childComplexity int) int } TeamMember struct { @@ -4328,17 +4387,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.Application.OpenSearch(childComplexity), true - case "Application.postgresInstances": - if e.ComplexityRoot.Application.PostgresInstances == nil { + case "Application.postgresBranches": + if e.ComplexityRoot.Application.PostgresBranches == nil { break } - args, err := ec.field_Application_postgresInstances_args(ctx, rawArgs) + args, err := ec.field_Application_postgresBranches_args(ctx, rawArgs) if err != nil { return 0, false } - return e.ComplexityRoot.Application.PostgresInstances(childComplexity, args["orderBy"].(*postgres.PostgresInstanceOrder)), true + return e.ComplexityRoot.Application.PostgresBranches(childComplexity, args["orderBy"].(*postgres.PostgresBranchOrder)), true case "Application.resources": if e.ComplexityRoot.Application.Resources == nil { @@ -6314,6 +6373,20 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.CreateOpenSearchPayload.OpenSearch(childComplexity), true + case "CreatePostgresAccessPayload.expiresAt": + if e.ComplexityRoot.CreatePostgresAccessPayload.ExpiresAt == nil { + break + } + + return e.ComplexityRoot.CreatePostgresAccessPayload.ExpiresAt(childComplexity), true + + case "CreatePostgresAccessPayload.name": + if e.ComplexityRoot.CreatePostgresAccessPayload.Name == nil { + break + } + + return e.ComplexityRoot.CreatePostgresAccessPayload.Name(childComplexity), true + case "CreateSecretPayload.secret": if e.ComplexityRoot.CreateSecretPayload.Secret == nil { break @@ -6454,12 +6527,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.DeleteOpenSearchPayload.OpenSearchDeleted(childComplexity), true - case "DeletePostgresPayload.postgresDeleted": - if e.ComplexityRoot.DeletePostgresPayload.PostgresDeleted == nil { + case "DeletePostgresBranchPayload.postgresBranchDeleted": + if e.ComplexityRoot.DeletePostgresBranchPayload.PostgresBranchDeleted == nil { break } - return e.ComplexityRoot.DeletePostgresPayload.PostgresDeleted(childComplexity), true + return e.ComplexityRoot.DeletePostgresBranchPayload.PostgresBranchDeleted(childComplexity), true case "DeleteSecretPayload.secretDeleted": if e.ComplexityRoot.DeleteSecretPayload.SecretDeleted == nil { @@ -8334,17 +8407,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.Job.OpenSearch(childComplexity), true - case "Job.postgresInstances": - if e.ComplexityRoot.Job.PostgresInstances == nil { + case "Job.postgresBranches": + if e.ComplexityRoot.Job.PostgresBranches == nil { break } - args, err := ec.field_Job_postgresInstances_args(ctx, rawArgs) + args, err := ec.field_Job_postgresBranches_args(ctx, rawArgs) if err != nil { return 0, false } - return e.ComplexityRoot.Job.PostgresInstances(childComplexity, args["orderBy"].(*postgres.PostgresInstanceOrder)), true + return e.ComplexityRoot.Job.PostgresBranches(childComplexity, args["orderBy"].(*postgres.PostgresBranchOrder)), true case "Job.resources": if e.ComplexityRoot.Job.Resources == nil { @@ -10012,6 +10085,18 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.Mutation.CreateOpenSearchCredentials(childComplexity, args["input"].(opensearch.CreateOpenSearchCredentialsInput)), true + case "Mutation.createPostgresAccess": + if e.ComplexityRoot.Mutation.CreatePostgresAccess == nil { + break + } + + args, err := ec.field_Mutation_createPostgresAccess_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.Mutation.CreatePostgresAccess(childComplexity, args["input"].(postgres.CreatePostgresAccessInput)), true + case "Mutation.createSecret": if e.ComplexityRoot.Mutation.CreateSecret == nil { break @@ -10168,17 +10253,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.Mutation.DeleteOpenSearch(childComplexity, args["input"].(opensearch.DeleteOpenSearchInput)), true - case "Mutation.deletePostgres": - if e.ComplexityRoot.Mutation.DeletePostgres == nil { + case "Mutation.deletePostgresBranch": + if e.ComplexityRoot.Mutation.DeletePostgresBranch == nil { break } - args, err := ec.field_Mutation_deletePostgres_args(ctx, rawArgs) + args, err := ec.field_Mutation_deletePostgresBranch_args(ctx, rawArgs) if err != nil { return 0, false } - return e.ComplexityRoot.Mutation.DeletePostgres(childComplexity, args["input"].(postgres.DeletePostgresInput)), true + return e.ComplexityRoot.Mutation.DeletePostgresBranch(childComplexity, args["input"].(postgres.DeletePostgresBranchInput)), true case "Mutation.deleteSecret": if e.ComplexityRoot.Mutation.DeleteSecret == nil { @@ -11559,6 +11644,343 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.PageInfo.TotalCount(childComplexity), true + case "Postgres.activeBranch": + if e.ComplexityRoot.Postgres.ActiveBranch == nil { + break + } + + return e.ComplexityRoot.Postgres.ActiveBranch(childComplexity), true + + case "Postgres.branch": + if e.ComplexityRoot.Postgres.Branch == nil { + break + } + + args, err := ec.field_Postgres_branch_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.Postgres.Branch(childComplexity, args["name"].(string)), true + + case "Postgres.branches": + if e.ComplexityRoot.Postgres.Branches == nil { + break + } + + args, err := ec.field_Postgres_branches_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.Postgres.Branches(childComplexity, args["first"].(*int), args["after"].(*pagination.Cursor), args["last"].(*int), args["before"].(*pagination.Cursor), args["orderBy"].(*postgres.PostgresBranchOrder)), true + + case "Postgres.highAvailability": + if e.ComplexityRoot.Postgres.HighAvailability == nil { + break + } + + return e.ComplexityRoot.Postgres.HighAvailability(childComplexity), true + + case "Postgres.id": + if e.ComplexityRoot.Postgres.ID == nil { + break + } + + return e.ComplexityRoot.Postgres.ID(childComplexity), true + + case "Postgres.labels": + if e.ComplexityRoot.Postgres.Labels == nil { + break + } + + return e.ComplexityRoot.Postgres.Labels(childComplexity), true + + case "Postgres.majorVersion": + if e.ComplexityRoot.Postgres.MajorVersion == nil { + break + } + + return e.ComplexityRoot.Postgres.MajorVersion(childComplexity), true + + case "Postgres.name": + if e.ComplexityRoot.Postgres.Name == nil { + break + } + + return e.ComplexityRoot.Postgres.Name(childComplexity), true + + case "Postgres.resources": + if e.ComplexityRoot.Postgres.Resources == nil { + break + } + + return e.ComplexityRoot.Postgres.Resources(childComplexity), true + + case "PostgresAccess.accessLevel": + if e.ComplexityRoot.PostgresAccess.AccessLevel == nil { + break + } + + return e.ComplexityRoot.PostgresAccess.AccessLevel(childComplexity), true + + case "PostgresAccess.connection": + if e.ComplexityRoot.PostgresAccess.Connection == nil { + break + } + + return e.ComplexityRoot.PostgresAccess.Connection(childComplexity), true + + case "PostgresAccess.expiresAt": + if e.ComplexityRoot.PostgresAccess.ExpiresAt == nil { + break + } + + return e.ComplexityRoot.PostgresAccess.ExpiresAt(childComplexity), true + + case "PostgresAccess.id": + if e.ComplexityRoot.PostgresAccess.ID == nil { + break + } + + return e.ComplexityRoot.PostgresAccess.ID(childComplexity), true + + case "PostgresAccess.message": + if e.ComplexityRoot.PostgresAccess.Message == nil { + break + } + + return e.ComplexityRoot.PostgresAccess.Message(childComplexity), true + + case "PostgresAccess.name": + if e.ComplexityRoot.PostgresAccess.Name == nil { + break + } + + return e.ComplexityRoot.PostgresAccess.Name(childComplexity), true + + case "PostgresAccess.postgresBranch": + if e.ComplexityRoot.PostgresAccess.PostgresBranch == nil { + break + } + + return e.ComplexityRoot.PostgresAccess.PostgresBranch(childComplexity), true + + case "PostgresAccess.relayAccess": + if e.ComplexityRoot.PostgresAccess.RelayAccess == nil { + break + } + + return e.ComplexityRoot.PostgresAccess.RelayAccess(childComplexity), true + + case "PostgresAccess.state": + if e.ComplexityRoot.PostgresAccess.State == nil { + break + } + + return e.ComplexityRoot.PostgresAccess.State(childComplexity), true + + case "PostgresAccess.team": + if e.ComplexityRoot.PostgresAccess.Team == nil { + break + } + + return e.ComplexityRoot.PostgresAccess.Team(childComplexity), true + + case "PostgresAccess.teamEnvironment": + if e.ComplexityRoot.PostgresAccess.TeamEnvironment == nil { + break + } + + return e.ComplexityRoot.PostgresAccess.TeamEnvironment(childComplexity), true + + case "PostgresAccessConnectionDetails.caCertificate": + if e.ComplexityRoot.PostgresAccessConnectionDetails.CACertificate == nil { + break + } + + return e.ComplexityRoot.PostgresAccessConnectionDetails.CACertificate(childComplexity), true + + case "PostgresAccessConnectionDetails.password": + if e.ComplexityRoot.PostgresAccessConnectionDetails.Password == nil { + break + } + + return e.ComplexityRoot.PostgresAccessConnectionDetails.Password(childComplexity), true + + case "PostgresAccessConnectionDetails.relayAccess": + if e.ComplexityRoot.PostgresAccessConnectionDetails.RelayAccess == nil { + break + } + + return e.ComplexityRoot.PostgresAccessConnectionDetails.RelayAccess(childComplexity), true + + case "PostgresAccessConnectionDetails.relayEndpoint": + if e.ComplexityRoot.PostgresAccessConnectionDetails.RelayEndpoint == nil { + break + } + + return e.ComplexityRoot.PostgresAccessConnectionDetails.RelayEndpoint(childComplexity), true + + case "PostgresAccessConnectionDetails.relayToken": + if e.ComplexityRoot.PostgresAccessConnectionDetails.RelayToken == nil { + break + } + + return e.ComplexityRoot.PostgresAccessConnectionDetails.RelayToken(childComplexity), true + + case "PostgresAccessConnectionDetails.serverName": + if e.ComplexityRoot.PostgresAccessConnectionDetails.ServerName == nil { + break + } + + return e.ComplexityRoot.PostgresAccessConnectionDetails.ServerName(childComplexity), true + + case "PostgresAccessConnectionDetails.username": + if e.ComplexityRoot.PostgresAccessConnectionDetails.Username == nil { + break + } + + return e.ComplexityRoot.PostgresAccessConnectionDetails.Username(childComplexity), true + + case "PostgresBranch.id": + if e.ComplexityRoot.PostgresBranch.ID == nil { + break + } + + return e.ComplexityRoot.PostgresBranch.ID(childComplexity), true + + case "PostgresBranch.labels": + if e.ComplexityRoot.PostgresBranch.Labels == nil { + break + } + + return e.ComplexityRoot.PostgresBranch.Labels(childComplexity), true + + case "PostgresBranch.name": + if e.ComplexityRoot.PostgresBranch.Name == nil { + break + } + + return e.ComplexityRoot.PostgresBranch.Name(childComplexity), true + + case "PostgresBranch.postgres": + if e.ComplexityRoot.PostgresBranch.Postgres == nil { + break + } + + return e.ComplexityRoot.PostgresBranch.Postgres(childComplexity), true + + case "PostgresBranch.state": + if e.ComplexityRoot.PostgresBranch.State == nil { + break + } + + return e.ComplexityRoot.PostgresBranch.State(childComplexity), true + + case "PostgresBranch.team": + if e.ComplexityRoot.PostgresBranch.Team == nil { + break + } + + return e.ComplexityRoot.PostgresBranch.Team(childComplexity), true + + case "PostgresBranch.teamEnvironment": + if e.ComplexityRoot.PostgresBranch.TeamEnvironment == nil { + break + } + + return e.ComplexityRoot.PostgresBranch.TeamEnvironment(childComplexity), true + + case "PostgresBranch.workloads": + if e.ComplexityRoot.PostgresBranch.Workloads == nil { + break + } + + args, err := ec.field_PostgresBranch_workloads_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.PostgresBranch.Workloads(childComplexity, args["first"].(*int), args["after"].(*pagination.Cursor), args["last"].(*int), args["before"].(*pagination.Cursor)), true + + case "PostgresBranchConnection.edges": + if e.ComplexityRoot.PostgresBranchConnection.Edges == nil { + break + } + + return e.ComplexityRoot.PostgresBranchConnection.Edges(childComplexity), true + + case "PostgresBranchConnection.facets": + if e.ComplexityRoot.PostgresBranchConnection.Facets == nil { + break + } + + return e.ComplexityRoot.PostgresBranchConnection.Facets(childComplexity), true + + case "PostgresBranchConnection.nodes": + if e.ComplexityRoot.PostgresBranchConnection.Nodes == nil { + break + } + + return e.ComplexityRoot.PostgresBranchConnection.Nodes(childComplexity), true + + case "PostgresBranchConnection.pageInfo": + if e.ComplexityRoot.PostgresBranchConnection.PageInfo == nil { + break + } + + return e.ComplexityRoot.PostgresBranchConnection.PageInfo(childComplexity), true + + case "PostgresBranchEdge.cursor": + if e.ComplexityRoot.PostgresBranchEdge.Cursor == nil { + break + } + + return e.ComplexityRoot.PostgresBranchEdge.Cursor(childComplexity), true + + case "PostgresBranchEdge.node": + if e.ComplexityRoot.PostgresBranchEdge.Node == nil { + break + } + + return e.ComplexityRoot.PostgresBranchEdge.Node(childComplexity), true + + case "PostgresBranchFacets.environments": + if e.ComplexityRoot.PostgresBranchFacets.Environments == nil { + break + } + + return e.ComplexityRoot.PostgresBranchFacets.Environments(childComplexity), true + + case "PostgresBranchFacets.labels": + if e.ComplexityRoot.PostgresBranchFacets.Labels == nil { + break + } + + return e.ComplexityRoot.PostgresBranchFacets.Labels(childComplexity), true + + case "PostgresBranchFacets.states": + if e.ComplexityRoot.PostgresBranchFacets.States == nil { + break + } + + return e.ComplexityRoot.PostgresBranchFacets.States(childComplexity), true + + case "PostgresBranchStateFacetItem.count": + if e.ComplexityRoot.PostgresBranchStateFacetItem.Count == nil { + break + } + + return e.ComplexityRoot.PostgresBranchStateFacetItem.Count(childComplexity), true + + case "PostgresBranchStateFacetItem.state": + if e.ComplexityRoot.PostgresBranchStateFacetItem.State == nil { + break + } + + return e.ComplexityRoot.PostgresBranchStateFacetItem.State(childComplexity), true + case "PostgresDeletedActivityLogEntry.actor": if e.ComplexityRoot.PostgresDeletedActivityLogEntry.Actor == nil { break @@ -11706,241 +12128,187 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.PostgresGrantAccessActivityLogEntryData.Until(childComplexity), true - case "PostgresInstance.audit": - if e.ComplexityRoot.PostgresInstance.Audit == nil { - break - } - - return e.ComplexityRoot.PostgresInstance.Audit(childComplexity), true - - case "PostgresInstance.highAvailability": - if e.ComplexityRoot.PostgresInstance.HighAvailability == nil { - break - } - - return e.ComplexityRoot.PostgresInstance.HighAvailability(childComplexity), true - - case "PostgresInstance.id": - if e.ComplexityRoot.PostgresInstance.ID == nil { - break - } - - return e.ComplexityRoot.PostgresInstance.ID(childComplexity), true - - case "PostgresInstance.labels": - if e.ComplexityRoot.PostgresInstance.Labels == nil { - break - } - - return e.ComplexityRoot.PostgresInstance.Labels(childComplexity), true - - case "PostgresInstance.maintenanceWindow": - if e.ComplexityRoot.PostgresInstance.MaintenanceWindow == nil { - break - } - - return e.ComplexityRoot.PostgresInstance.MaintenanceWindow(childComplexity), true - - case "PostgresInstance.majorVersion": - if e.ComplexityRoot.PostgresInstance.MajorVersion == nil { - break - } - - return e.ComplexityRoot.PostgresInstance.MajorVersion(childComplexity), true - - case "PostgresInstance.name": - if e.ComplexityRoot.PostgresInstance.Name == nil { - break - } - - return e.ComplexityRoot.PostgresInstance.Name(childComplexity), true - - case "PostgresInstance.resources": - if e.ComplexityRoot.PostgresInstance.Resources == nil { + case "PostgresPersonalAccessConnectionActivityLogEntry.actor": + if e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.Actor == nil { break } - return e.ComplexityRoot.PostgresInstance.Resources(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.Actor(childComplexity), true - case "PostgresInstance.state": - if e.ComplexityRoot.PostgresInstance.State == nil { + case "PostgresPersonalAccessConnectionActivityLogEntry.createdAt": + if e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.CreatedAt == nil { break } - return e.ComplexityRoot.PostgresInstance.State(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.CreatedAt(childComplexity), true - case "PostgresInstance.team": - if e.ComplexityRoot.PostgresInstance.Team == nil { + case "PostgresPersonalAccessConnectionActivityLogEntry.environmentName": + if e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.EnvironmentName == nil { break } - return e.ComplexityRoot.PostgresInstance.Team(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.EnvironmentName(childComplexity), true - case "PostgresInstance.teamEnvironment": - if e.ComplexityRoot.PostgresInstance.TeamEnvironment == nil { + case "PostgresPersonalAccessConnectionActivityLogEntry.gitHubActorClaims": + if e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.GitHubActorClaims == nil { break } - return e.ComplexityRoot.PostgresInstance.TeamEnvironment(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.GitHubActorClaims(childComplexity), true - case "PostgresInstance.workloads": - if e.ComplexityRoot.PostgresInstance.Workloads == nil { + case "PostgresPersonalAccessConnectionActivityLogEntry.id": + if e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.ID == nil { break } - args, err := ec.field_PostgresInstance_workloads_args(ctx, rawArgs) - if err != nil { - return 0, false - } - - return e.ComplexityRoot.PostgresInstance.Workloads(childComplexity, args["first"].(*int), args["after"].(*pagination.Cursor), args["last"].(*int), args["before"].(*pagination.Cursor)), true + return e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.ID(childComplexity), true - case "PostgresInstanceAudit.enabled": - if e.ComplexityRoot.PostgresInstanceAudit.Enabled == nil { + case "PostgresPersonalAccessConnectionActivityLogEntry.message": + if e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.Message == nil { break } - return e.ComplexityRoot.PostgresInstanceAudit.Enabled(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.Message(childComplexity), true - case "PostgresInstanceAudit.statementClasses": - if e.ComplexityRoot.PostgresInstanceAudit.StatementClasses == nil { + case "PostgresPersonalAccessConnectionActivityLogEntry.resourceName": + if e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.ResourceName == nil { break } - return e.ComplexityRoot.PostgresInstanceAudit.StatementClasses(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.ResourceName(childComplexity), true - case "PostgresInstanceAudit.url": - if e.ComplexityRoot.PostgresInstanceAudit.URL == nil { + case "PostgresPersonalAccessConnectionActivityLogEntry.resourceType": + if e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.ResourceType == nil { break } - return e.ComplexityRoot.PostgresInstanceAudit.URL(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.ResourceType(childComplexity), true - case "PostgresInstanceConnection.edges": - if e.ComplexityRoot.PostgresInstanceConnection.Edges == nil { + case "PostgresPersonalAccessConnectionActivityLogEntry.teamSlug": + if e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.TeamSlug == nil { break } - return e.ComplexityRoot.PostgresInstanceConnection.Edges(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessConnectionActivityLogEntry.TeamSlug(childComplexity), true - case "PostgresInstanceConnection.facets": - if e.ComplexityRoot.PostgresInstanceConnection.Facets == nil { + case "PostgresPersonalAccessCreatedActivityLogEntry.actor": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.Actor == nil { break } - return e.ComplexityRoot.PostgresInstanceConnection.Facets(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.Actor(childComplexity), true - case "PostgresInstanceConnection.nodes": - if e.ComplexityRoot.PostgresInstanceConnection.Nodes == nil { + case "PostgresPersonalAccessCreatedActivityLogEntry.createdAt": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.CreatedAt == nil { break } - return e.ComplexityRoot.PostgresInstanceConnection.Nodes(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.CreatedAt(childComplexity), true - case "PostgresInstanceConnection.pageInfo": - if e.ComplexityRoot.PostgresInstanceConnection.PageInfo == nil { + case "PostgresPersonalAccessCreatedActivityLogEntry.data": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.Data == nil { break } - return e.ComplexityRoot.PostgresInstanceConnection.PageInfo(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.Data(childComplexity), true - case "PostgresInstanceEdge.cursor": - if e.ComplexityRoot.PostgresInstanceEdge.Cursor == nil { + case "PostgresPersonalAccessCreatedActivityLogEntry.environmentName": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.EnvironmentName == nil { break } - return e.ComplexityRoot.PostgresInstanceEdge.Cursor(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.EnvironmentName(childComplexity), true - case "PostgresInstanceEdge.node": - if e.ComplexityRoot.PostgresInstanceEdge.Node == nil { + case "PostgresPersonalAccessCreatedActivityLogEntry.gitHubActorClaims": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.GitHubActorClaims == nil { break } - return e.ComplexityRoot.PostgresInstanceEdge.Node(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.GitHubActorClaims(childComplexity), true - case "PostgresInstanceFacets.environments": - if e.ComplexityRoot.PostgresInstanceFacets.Environments == nil { + case "PostgresPersonalAccessCreatedActivityLogEntry.id": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.ID == nil { break } - return e.ComplexityRoot.PostgresInstanceFacets.Environments(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.ID(childComplexity), true - case "PostgresInstanceFacets.highAvailability": - if e.ComplexityRoot.PostgresInstanceFacets.HighAvailability == nil { + case "PostgresPersonalAccessCreatedActivityLogEntry.message": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.Message == nil { break } - return e.ComplexityRoot.PostgresInstanceFacets.HighAvailability(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.Message(childComplexity), true - case "PostgresInstanceFacets.labels": - if e.ComplexityRoot.PostgresInstanceFacets.Labels == nil { + case "PostgresPersonalAccessCreatedActivityLogEntry.resourceName": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.ResourceName == nil { break } - return e.ComplexityRoot.PostgresInstanceFacets.Labels(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.ResourceName(childComplexity), true - case "PostgresInstanceFacets.majorVersions": - if e.ComplexityRoot.PostgresInstanceFacets.MajorVersions == nil { + case "PostgresPersonalAccessCreatedActivityLogEntry.resourceType": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.ResourceType == nil { break } - return e.ComplexityRoot.PostgresInstanceFacets.MajorVersions(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.ResourceType(childComplexity), true - case "PostgresInstanceFacets.states": - if e.ComplexityRoot.PostgresInstanceFacets.States == nil { + case "PostgresPersonalAccessCreatedActivityLogEntry.teamSlug": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.TeamSlug == nil { break } - return e.ComplexityRoot.PostgresInstanceFacets.States(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntry.TeamSlug(childComplexity), true - case "PostgresInstanceMaintenanceWindow.day": - if e.ComplexityRoot.PostgresInstanceMaintenanceWindow.Day == nil { + case "PostgresPersonalAccessCreatedActivityLogEntryData.accessLevel": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntryData.AccessLevel == nil { break } - return e.ComplexityRoot.PostgresInstanceMaintenanceWindow.Day(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntryData.AccessLevel(childComplexity), true - case "PostgresInstanceMaintenanceWindow.hour": - if e.ComplexityRoot.PostgresInstanceMaintenanceWindow.Hour == nil { + case "PostgresPersonalAccessCreatedActivityLogEntryData.expiresAt": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntryData.ExpiresAt == nil { break } - return e.ComplexityRoot.PostgresInstanceMaintenanceWindow.Hour(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntryData.ExpiresAt(childComplexity), true - case "PostgresInstanceResources.cpu": - if e.ComplexityRoot.PostgresInstanceResources.CPU == nil { + case "PostgresPersonalAccessCreatedActivityLogEntryData.reason": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntryData.Reason == nil { break } - return e.ComplexityRoot.PostgresInstanceResources.CPU(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntryData.Reason(childComplexity), true - case "PostgresInstanceResources.diskSize": - if e.ComplexityRoot.PostgresInstanceResources.DiskSize == nil { + case "PostgresPersonalAccessCreatedActivityLogEntryData.username": + if e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntryData.Username == nil { break } - return e.ComplexityRoot.PostgresInstanceResources.DiskSize(childComplexity), true + return e.ComplexityRoot.PostgresPersonalAccessCreatedActivityLogEntryData.Username(childComplexity), true - case "PostgresInstanceResources.memory": - if e.ComplexityRoot.PostgresInstanceResources.Memory == nil { + case "PostgresResources.cpu": + if e.ComplexityRoot.PostgresResources.CPU == nil { break } - return e.ComplexityRoot.PostgresInstanceResources.Memory(childComplexity), true + return e.ComplexityRoot.PostgresResources.CPU(childComplexity), true - case "PostgresInstanceStateFacetItem.count": - if e.ComplexityRoot.PostgresInstanceStateFacetItem.Count == nil { + case "PostgresResources.diskSize": + if e.ComplexityRoot.PostgresResources.DiskSize == nil { break } - return e.ComplexityRoot.PostgresInstanceStateFacetItem.Count(childComplexity), true + return e.ComplexityRoot.PostgresResources.DiskSize(childComplexity), true - case "PostgresInstanceStateFacetItem.state": - if e.ComplexityRoot.PostgresInstanceStateFacetItem.State == nil { + case "PostgresResources.memory": + if e.ComplexityRoot.PostgresResources.Memory == nil { break } - return e.ComplexityRoot.PostgresInstanceStateFacetItem.State(childComplexity), true + return e.ComplexityRoot.PostgresResources.Memory(childComplexity), true case "Price.value": if e.ComplexityRoot.Price.Value == nil { @@ -16115,17 +16483,17 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.Team.OpenSearches(childComplexity, args["first"].(*int), args["after"].(*pagination.Cursor), args["last"].(*int), args["before"].(*pagination.Cursor), args["orderBy"].(*opensearch.OpenSearchOrder), args["filter"].(*opensearch.OpenSearchFilter)), true - case "Team.postgresInstances": - if e.ComplexityRoot.Team.PostgresInstances == nil { + case "Team.postgresBranches": + if e.ComplexityRoot.Team.PostgresBranches == nil { break } - args, err := ec.field_Team_postgresInstances_args(ctx, rawArgs) + args, err := ec.field_Team_postgresBranches_args(ctx, rawArgs) if err != nil { return 0, false } - return e.ComplexityRoot.Team.PostgresInstances(childComplexity, args["first"].(*int), args["after"].(*pagination.Cursor), args["last"].(*int), args["before"].(*pagination.Cursor), args["orderBy"].(*postgres.PostgresInstanceOrder), args["filter"].(*postgres.PostgresInstanceFilter)), true + return e.ComplexityRoot.Team.PostgresBranches(childComplexity, args["first"].(*int), args["after"].(*pagination.Cursor), args["last"].(*int), args["before"].(*pagination.Cursor), args["orderBy"].(*postgres.PostgresBranchOrder), args["filter"].(*postgres.PostgresBranchFilter)), true case "Team.purpose": if e.ComplexityRoot.Team.Purpose == nil { @@ -16824,17 +17192,29 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.TeamEnvironment.OpenSearch(childComplexity, args["name"].(string)), true - case "TeamEnvironment.postgresInstance": - if e.ComplexityRoot.TeamEnvironment.PostgresInstance == nil { + case "TeamEnvironment.postgres": + if e.ComplexityRoot.TeamEnvironment.Postgres == nil { + break + } + + args, err := ec.field_TeamEnvironment_postgres_args(ctx, rawArgs) + if err != nil { + return 0, false + } + + return e.ComplexityRoot.TeamEnvironment.Postgres(childComplexity, args["name"].(string)), true + + case "TeamEnvironment.postgresAccess": + if e.ComplexityRoot.TeamEnvironment.PostgresAccess == nil { break } - args, err := ec.field_TeamEnvironment_postgresInstance_args(ctx, rawArgs) + args, err := ec.field_TeamEnvironment_postgresAccess_args(ctx, rawArgs) if err != nil { return 0, false } - return e.ComplexityRoot.TeamEnvironment.PostgresInstance(childComplexity, args["name"].(string)), true + return e.ComplexityRoot.TeamEnvironment.PostgresAccess(childComplexity, args["name"].(string)), true case "TeamEnvironment.sqlInstance": if e.ComplexityRoot.TeamEnvironment.SQLInstance == nil { @@ -17188,12 +17568,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.TeamInventoryCountOpenSearches.Total(childComplexity), true - case "TeamInventoryCountPostgresInstances.total": - if e.ComplexityRoot.TeamInventoryCountPostgresInstances.Total == nil { + case "TeamInventoryCountPostgresBranches.total": + if e.ComplexityRoot.TeamInventoryCountPostgresBranches.Total == nil { break } - return e.ComplexityRoot.TeamInventoryCountPostgresInstances.Total(childComplexity), true + return e.ComplexityRoot.TeamInventoryCountPostgresBranches.Total(childComplexity), true case "TeamInventoryCountSecrets.total": if e.ComplexityRoot.TeamInventoryCountSecrets.Total == nil { @@ -17265,12 +17645,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.ComplexityRoot.TeamInventoryCounts.OpenSearches(childComplexity), true - case "TeamInventoryCounts.postgresInstances": - if e.ComplexityRoot.TeamInventoryCounts.PostgresInstances == nil { + case "TeamInventoryCounts.postgresBranches": + if e.ComplexityRoot.TeamInventoryCounts.PostgresBranches == nil { break } - return e.ComplexityRoot.TeamInventoryCounts.PostgresInstances(childComplexity), true + return e.ComplexityRoot.TeamInventoryCounts.PostgresBranches(childComplexity), true case "TeamInventoryCounts.sqlInstances": if e.ComplexityRoot.TeamInventoryCounts.SQLInstances == nil { @@ -20522,6 +20902,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputCreateKafkaCredentialsInput, ec.unmarshalInputCreateOpenSearchCredentialsInput, ec.unmarshalInputCreateOpenSearchInput, + ec.unmarshalInputCreatePostgresAccessInput, ec.unmarshalInputCreateSecretInput, ec.unmarshalInputCreateServiceAccountInput, ec.unmarshalInputCreateServiceAccountTokenInput, @@ -20535,7 +20916,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputDeleteJobInput, ec.unmarshalInputDeleteJobRunInput, ec.unmarshalInputDeleteOpenSearchInput, - ec.unmarshalInputDeletePostgresInput, + ec.unmarshalInputDeletePostgresBranchInput, ec.unmarshalInputDeleteSecretInput, ec.unmarshalInputDeleteServiceAccountInput, ec.unmarshalInputDeleteServiceAccountTokenInput, @@ -20567,8 +20948,8 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputOpenSearchAccessOrder, ec.unmarshalInputOpenSearchFilter, ec.unmarshalInputOpenSearchOrder, - ec.unmarshalInputPostgresInstanceFilter, - ec.unmarshalInputPostgresInstanceOrder, + ec.unmarshalInputPostgresBranchFilter, + ec.unmarshalInputPostgresBranchOrder, ec.unmarshalInputReconcilerConfigInput, ec.unmarshalInputRemoveConfigValueInput, ec.unmarshalInputRemoveRepositoryFromTeamInput, @@ -27037,8 +27418,8 @@ type WorkloadLogLine { } `, BuiltIn: false}, {Name: "../schema/postgres.graphqls", Input: `extend type Team { - "Postgres instances owned by the team." - postgresInstances( + "Postgres branches owned by the team." + postgresBranches( "Get the first n items in the connection. This can be used in combination with the after parameter." first: Int @@ -27052,81 +27433,87 @@ type WorkloadLogLine { before: Cursor "Ordering options for items returned from the connection." - orderBy: PostgresInstanceOrder + orderBy: PostgresBranchOrder "Filtering options for items returned from the connection." - filter: PostgresInstanceFilter - ): PostgresInstanceConnection! + filter: PostgresBranchFilter + ): PostgresBranchConnection! } extend type TeamEnvironment { - "Postgres instance in the team environment." - postgresInstance(name: String!): PostgresInstance! + "Postgres in the team environment." + postgres("Name of the Postgres in this team environment." name: String!): Postgres! + """ + EXPERIMENTAL: DO NOT USE + Get a PostgresAccess and its state. Available to authorized team members. + """ + postgresAccess( + "Name of the PostgresAccess in this team environment." + name: String! + ): PostgresAccess! } extend interface Workload { - "Postgres instances referenced by the workload. This does not currently support pagination, but will return all available Postgres instances." - postgresInstances( + "Active PostgresBranches for all Postgres entries in uses.postgres." + postgresBranches( "Ordering options for items returned from the connection." - orderBy: PostgresInstanceOrder - ): PostgresInstanceConnection! + orderBy: PostgresBranchOrder + ): PostgresBranchConnection! } extend type Application { - "Postgres instances referenced by the application. This does not currently support pagination, but will return all available Postgres instances." - postgresInstances( + "Active PostgresBranches for all Postgres entries in uses.postgres." + postgresBranches( "Ordering options for items returned from the connection." - orderBy: PostgresInstanceOrder - ): PostgresInstanceConnection! + orderBy: PostgresBranchOrder + ): PostgresBranchConnection! } extend type Job { - "Postgres instances referenced by the job. This does not currently support pagination, but will return all available Postgres instances." - postgresInstances( + "Active PostgresBranches for all Postgres entries in uses.postgres." + postgresBranches( "Ordering options for items returned from the connection." - orderBy: PostgresInstanceOrder - ): PostgresInstanceConnection! + orderBy: PostgresBranchOrder + ): PostgresBranchConnection! } -input PostgresInstanceOrder { - field: PostgresInstanceOrderField! +input PostgresBranchOrder { + field: PostgresBranchOrderField! direction: OrderDirection! } """ -Input for filtering Postgres instances. +Input for filtering Postgres branches. """ -input PostgresInstanceFilter { - "Filter by the name of the instance." +input PostgresBranchFilter { + "Filter by the name of the branch." name: String "Filter by environments." environments: [String!] - "Filter by instance state." - states: [PostgresInstanceState!] - - "Filter by high availability." - highAvailability: Boolean - - "Filter by major versions." - majorVersions: [String!] + "Filter by branch state." + states: [PostgresBranchState!] "Filter by user-defined labels. All listed labels must match." labels: [LabelFilter!] } -enum PostgresInstanceOrderField { +enum PostgresBranchOrderField { NAME ENVIRONMENT } -type PostgresInstance implements Persistence & Node { +"A named PostgresBranch belonging to a Postgres." +type PostgresBranch implements Persistence & Node { id: ID! + "Local name of this branch within its Postgres." name: String! team: Team! teamEnvironment: TeamEnvironment! - "Workloads that reference the Postgres instance." + "Postgres owning this PostgresBranch." + postgres: Postgres! + "Workloads using this branch while it is active." workloads( "Get the first n items in the connection. This can be used in combination with the after parameter." first: Int @@ -27140,100 +27527,106 @@ type PostgresInstance implements Persistence & Node { "Get items before this cursor." before: Cursor ): WorkloadConnection! - "Resource allocation for the Postgres cluster." - resources: PostgresInstanceResources! - "Major version of PostgreSQL." + "Current observed state of the branch." + state: PostgresBranchState! + "User-defined labels on this branch." + labels: [ResourceLabel!]! +} + +"A Postgres whose active branch can change." +type Postgres implements Node { + "Opaque identifier for this Postgres." + id: ID! + "Name of this Postgres." + name: String! + "Configured PostgreSQL major version." majorVersion: String! - "Audit logging configuration for the Postgres cluster." - audit: PostgresInstanceAudit! - "Indicates whether the Postgres cluster is configured for high availability." + "Whether high availability is configured." highAvailability: Boolean! - "Current state of the Postgres cluster." - state: PostgresInstanceState! - "Maintenance window for the Postgres cluster, if configured." - maintenanceWindow: PostgresInstanceMaintenanceWindow - "User-defined labels attached to this instance." + "Requested CPU, memory and disk size, when present on this Postgres." + resources: PostgresResources! + "Currently active branch, if selected." + activeBranch: PostgresBranch + "Branch with this local name in this Postgres." + branch(name: String!): PostgresBranch! + "Branches belonging to this Postgres." + branches( + first: Int + after: Cursor + last: Int + before: Cursor + orderBy: PostgresBranchOrder + ): PostgresBranchConnection! + "User-defined labels on this Postgres." labels: [ResourceLabel!]! } -enum PostgresInstanceState { +"Resource requests configured on Postgres. Omitted requests are null." +type PostgresResources { + "Requested CPU." + cpu: String + "Requested memory." + memory: String + "Requested disk size." + diskSize: String +} + +"Reconciliation and observed health of a PostgresBranch." +enum PostgresBranchState { + "The branch is healthy and ready." AVAILABLE + "The branch is provisioning or its state has not been observed yet." PROGRESSING + "The branch has reported a failure." DEGRADED } -type PostgresInstanceMaintenanceWindow { - day: Int! - hour: Int! -} - -type PostgresInstanceAudit { - "Indicates whether audit logging is enabled for the Postgres cluster." - enabled: Boolean! - "URL for accessing the audit logs." - url: String - "List of statement classes that are being logged, such as ` + "`" + `ddl` + "`" + `, ` + "`" + `dml` + "`" + `, and ` + "`" + `read` + "`" + `." - statementClasses: [String!] -} - -type PostgresInstanceConnection { +type PostgresBranchConnection { pageInfo: PageInfo! - nodes: [PostgresInstance!]! - edges: [PostgresInstanceEdge!]! + nodes: [PostgresBranch!]! + edges: [PostgresBranchEdge!]! """ - Facets for Postgres instances. Provides distribution counts to help narrow down results. + Facets for Postgres branches. Provides distribution counts to help narrow down results. Facet counts are computed over the full result set (ignoring pagination) but respect the current filter. """ - facets: PostgresInstanceFacets + facets: PostgresBranchFacets } -type PostgresInstanceEdge { +type PostgresBranchEdge { cursor: Cursor! - node: PostgresInstance! + node: PostgresBranch! } """ -Facets for Postgres instances, providing distribution counts across different dimensions. +Facets for Postgres branches, providing distribution counts across different dimensions. """ -type PostgresInstanceFacets { - "Distribution of instances by environment." +type PostgresBranchFacets { + "Distribution of branches by environment." environments: [StringFacetItem!]! - "Distribution of instances by state." - states: [PostgresInstanceStateFacetItem!]! - - "Distribution of instances by high availability." - highAvailability: [BooleanFacetItem!]! - - "Distribution of instances by major version." - majorVersions: [StringFacetItem!]! + "Distribution of branches by state." + states: [PostgresBranchStateFacetItem!]! - "Distribution of instances by user-defined labels." + "Distribution of branches by user-defined labels." labels: [LabelFacetItem!]! } """ -A single facet item for Postgres instance states. +A single facet item for Postgres branch states. """ -type PostgresInstanceStateFacetItem { - "The Postgres instance state." - state: PostgresInstanceState! +type PostgresBranchStateFacetItem { + "The Postgres branch state." + state: PostgresBranchState! - "Number of matching instances." + "Number of matching branches." count: Int! } -type PostgresInstanceResources { - cpu: String! - memory: String! - diskSize: String! -} - -extend union SearchNode = PostgresInstance +extend union SearchNode = PostgresBranch extend enum SearchType { - POSTGRES + POSTGRES_BRANCH } extend enum ActivityLogEntryResourceType { @@ -27280,6 +27673,64 @@ type PostgresGrantAccessActivityLogEntryData { until: Time! } +"An audit-log entry for personal Postgres access created through the API broker." +type PostgresPersonalAccessCreatedActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + "The identity of the actor who created the personal access." + actor: String! + "GitHub Actions OIDC claims when authenticated by a GitHub repository." + gitHubActorClaims: GitHubActorClaims + "Creation time of the entry." + createdAt: Time! + "Message that summarizes the entry." + message: String! + "Type of the affected resource." + resourceType: ActivityLogEntryResourceType! + "Name of the affected Postgres branch." + resourceName: String! + "The team slug that the entry belongs to." + teamSlug: Slug! + "The environment name that the entry belongs to." + environmentName: String + "Personal-access specific audit data." + data: PostgresPersonalAccessCreatedActivityLogEntryData! +} + +"Personal-access-specific audit data." +type PostgresPersonalAccessCreatedActivityLogEntryData { + "Identity that owns the new personal access." + username: String! + "Requested privilege level; null for events recorded before this field was added." + accessLevel: PostgresAccessLevel + "Server-controlled expiry of the access." + expiresAt: Time! + "Caller-provided audit reason." + reason: String! +} + +"An audit-log entry for retrieval of personal Postgres connection materials." +type PostgresPersonalAccessConnectionActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + "Identity that retrieved the connection materials." + actor: String! + "GitHub Actions OIDC claims when authenticated by a GitHub repository." + gitHubActorClaims: GitHubActorClaims + "Creation time of the entry." + createdAt: Time! + "Message that summarizes the entry." + message: String! + "Type of the affected resource." + resourceType: ActivityLogEntryResourceType! + "Name of the affected PostgresAccess resource." + resourceName: String! + "Team slug that the entry belongs to." + teamSlug: Slug! + "Environment name that the entry belongs to." + environmentName: String +} + type PostgresDeletedActivityLogEntry implements ActivityLogEntry & Node { "ID of the entry." id: ID! @@ -27315,7 +27766,15 @@ extend enum ActivityLogActivityType { """ POSTGRES_GRANT_ACCESS """ - A Postgres instance was deleted + A personal Postgres access was created through the API broker + """ + POSTGRES_PERSONAL_ACCESS_CREATED + """ + Personal Postgres connection materials were retrieved + """ + POSTGRES_PERSONAL_ACCESS_CONNECTION + """ + A Postgres branch was deleted """ POSTGRES_DELETED } @@ -27323,8 +27782,14 @@ extend enum ActivityLogActivityType { extend type Mutation { "Grant temporary access to a Postgres cluster." grantPostgresAccess(input: GrantPostgresAccessInput!): GrantPostgresAccessPayload! - "Delete an existing Postgres instance." - deletePostgres(input: DeletePostgresInput!): DeletePostgresPayload! + """ + EXPERIMENTAL: DO NOT USE + Create time-limited personal access to a NAIS Postgres branch through the brokered PostgresAccess and relay flow. + When the access is ready, retrieve its connection materials through PostgresAccess.connection. + """ + createPostgresAccess(input: CreatePostgresAccessInput!): CreatePostgresAccessPayload! + "Delete a PostgresBranch that is not active on its Postgres." + deletePostgresBranch(input: DeletePostgresBranchInput!): DeletePostgresBranchPayload! } type GrantPostgresAccessPayload { @@ -27340,28 +27805,125 @@ input GrantPostgresAccessInput { duration: String! } -input DeletePostgresInput { - "Name of the Postgres instance." +"Result of creating a personal Postgres access." +type CreatePostgresAccessPayload { + "Name of the newly created PostgresAccess resource." name: String! - "The environment name that the Postgres instance belongs to." + "Server-controlled expiry for this personal access." + expiresAt: Time! +} + +"Input for creating a time-limited personal Postgres access." +input CreatePostgresAccessInput { + "Name of the Postgres containing the branch." + postgres: String! + "Local name of the branch to access." + branch: String! + "Team that owns the Postgres branch." + teamSlug: Slug! + "Environment containing the Postgres branch." + environmentName: String! + "Privileges requested for the personal database role." + accessLevel: PostgresAccessLevel! + "Reason for personal database access. Must be at least 10 characters." + reason: String! + "Requested access lifetime (for example '30m' or '1h'). Defaults to '1h' and cannot exceed '1h'." + ttl: String +} + +"Privilege level granted to a personal Postgres database role." +enum PostgresAccessLevel { + "Read data without modifying it." + READ + "Read and modify existing data." + READWRITE + "Read, modify, and create database objects where supported." + READWRITECREATE +} + +input DeletePostgresBranchInput { + "Name of the Postgres containing the branch." + postgres: String! + "Local name of the branch to delete." + branch: String! + "The environment containing the PostgresBranch." environmentName: String! - "The team that owns the Postgres instance." + "The team that owns the PostgresBranch." teamSlug: Slug! } -type DeletePostgresPayload { - "Whether or not the Postgres instance was deleted." - postgresDeleted: Boolean +type DeletePostgresBranchPayload { + "Whether the PostgresBranch was deleted." + postgresBranchDeleted: Boolean } extend type TeamInventoryCounts { - postgresInstances: TeamInventoryCountPostgresInstances! + postgresBranches: TeamInventoryCountPostgresBranches! } -type TeamInventoryCountPostgresInstances { - "Total number of Postgres instances." +type TeamInventoryCountPostgresBranches { + "Total number of Postgres branches." total: Int! } + +"A time-limited personal access request for a Postgres branch." +type PostgresAccess implements Node { + "Opaque ID for this PostgresAccess resource." + id: ID! + "Name of the PostgresAccess resource." + name: String! + "Team that owns the access." + team: Team! + "Environment for the access." + teamEnvironment: TeamEnvironment! + "PostgresBranch selected by this access." + postgresBranch: PostgresBranch! + "Requested access level." + accessLevel: PostgresAccessLevel! + "Server-controlled expiry for this personal access." + expiresAt: Time! + "High-level state of the access." + state: PostgresAccessState! + "Human-readable message for the current state." + message: String + "Name of the controller-owned relay mapping, once created. Contains no credential." + relayAccess: String + """ + EXPERIMENTAL: DO NOT USE + Get connection materials for this ready access. Only its owner can read them. + """ + connection: PostgresAccessConnectionDetails +} + +"High-level reconciliation state of a personal Postgres access." +enum PostgresAccessState { + "The controller has not finished provisioning the access." + PENDING + "The access and its connection materials are ready." + READY + "The controller cannot provision the requested access." + FAILED + "The server-controlled expiry time has passed." + EXPIRED +} + +"Sensitive connection materials for a ready personal Postgres access." +type PostgresAccessConnectionDetails { + "Database username for the caller's personal role." + username: String! + "Short-lived password for the caller's database role." + password: String! + "CA certificate required to verify the PostgreSQL server certificate." + caCertificate: String! + "PostgreSQL server name used for TLS verification." + serverName: String! + "Public HTTP/3 relay endpoint." + relayEndpoint: String! + "Relay-Access header value (namespace/name)." + relayAccess: String! + "Owner-only bearer token for this access; never log it." + relayToken: String! +} `, BuiltIn: false}, {Name: "../schema/price.graphqls", Input: `extend type Query { """ @@ -34902,8 +35464,8 @@ func (ec *executionContext) childFields_Application(ctx context.Context, field g return ec.fieldContext_Application_networkPolicy(ctx, field) case "openSearch": return ec.fieldContext_Application_openSearch(ctx, field) - case "postgresInstances": - return ec.fieldContext_Application_postgresInstances(ctx, field) + case "postgresBranches": + return ec.fieldContext_Application_postgresBranches(ctx, field) case "secrets": return ec.fieldContext_Application_secrets(ctx, field) case "serviceAccount": @@ -35580,6 +36142,16 @@ func (ec *executionContext) childFields_CreateOpenSearchPayload(ctx context.Cont return nil, fmt.Errorf("no field named %q was found under type CreateOpenSearchPayload", field.Name) } +func (ec *executionContext) childFields_CreatePostgresAccessPayload(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "name": + return ec.fieldContext_CreatePostgresAccessPayload_name(ctx, field) + case "expiresAt": + return ec.fieldContext_CreatePostgresAccessPayload_expiresAt(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type CreatePostgresAccessPayload", field.Name) +} + func (ec *executionContext) childFields_CreateSecretPayload(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "secret": @@ -35704,12 +36276,12 @@ func (ec *executionContext) childFields_DeleteOpenSearchPayload(ctx context.Cont return nil, fmt.Errorf("no field named %q was found under type DeleteOpenSearchPayload", field.Name) } -func (ec *executionContext) childFields_DeletePostgresPayload(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { +func (ec *executionContext) childFields_DeletePostgresBranchPayload(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "postgresDeleted": - return ec.fieldContext_DeletePostgresPayload_postgresDeleted(ctx, field) + case "postgresBranchDeleted": + return ec.fieldContext_DeletePostgresBranchPayload_postgresBranchDeleted(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type DeletePostgresPayload", field.Name) + return nil, fmt.Errorf("no field named %q was found under type DeletePostgresBranchPayload", field.Name) } func (ec *executionContext) childFields_DeleteSecretPayload(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { @@ -36414,8 +36986,8 @@ func (ec *executionContext) childFields_Job(ctx context.Context, field graphql.C return ec.fieldContext_Job_networkPolicy(ctx, field) case "openSearch": return ec.fieldContext_Job_openSearch(ctx, field) - case "postgresInstances": - return ec.fieldContext_Job_postgresInstances(ctx, field) + case "postgresBranches": + return ec.fieldContext_Job_postgresBranches(ctx, field) case "secrets": return ec.fieldContext_Job_secrets(ctx, field) case "serviceAccount": @@ -37168,128 +37740,180 @@ func (ec *executionContext) childFields_PageInfo(ctx context.Context, field grap return nil, fmt.Errorf("no field named %q was found under type PageInfo", field.Name) } -func (ec *executionContext) childFields_PostgresGrantAccessActivityLogEntryData(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { +func (ec *executionContext) childFields_Postgres(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "grantee": - return ec.fieldContext_PostgresGrantAccessActivityLogEntryData_grantee(ctx, field) - case "until": - return ec.fieldContext_PostgresGrantAccessActivityLogEntryData_until(ctx, field) + case "id": + return ec.fieldContext_Postgres_id(ctx, field) + case "name": + return ec.fieldContext_Postgres_name(ctx, field) + case "majorVersion": + return ec.fieldContext_Postgres_majorVersion(ctx, field) + case "highAvailability": + return ec.fieldContext_Postgres_highAvailability(ctx, field) + case "resources": + return ec.fieldContext_Postgres_resources(ctx, field) + case "activeBranch": + return ec.fieldContext_Postgres_activeBranch(ctx, field) + case "branch": + return ec.fieldContext_Postgres_branch(ctx, field) + case "branches": + return ec.fieldContext_Postgres_branches(ctx, field) + case "labels": + return ec.fieldContext_Postgres_labels(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type PostgresGrantAccessActivityLogEntryData", field.Name) + return nil, fmt.Errorf("no field named %q was found under type Postgres", field.Name) } -func (ec *executionContext) childFields_PostgresInstance(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { +func (ec *executionContext) childFields_PostgresAccess(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "id": - return ec.fieldContext_PostgresInstance_id(ctx, field) + return ec.fieldContext_PostgresAccess_id(ctx, field) case "name": - return ec.fieldContext_PostgresInstance_name(ctx, field) + return ec.fieldContext_PostgresAccess_name(ctx, field) case "team": - return ec.fieldContext_PostgresInstance_team(ctx, field) + return ec.fieldContext_PostgresAccess_team(ctx, field) case "teamEnvironment": - return ec.fieldContext_PostgresInstance_teamEnvironment(ctx, field) - case "workloads": - return ec.fieldContext_PostgresInstance_workloads(ctx, field) - case "resources": - return ec.fieldContext_PostgresInstance_resources(ctx, field) - case "majorVersion": - return ec.fieldContext_PostgresInstance_majorVersion(ctx, field) - case "audit": - return ec.fieldContext_PostgresInstance_audit(ctx, field) - case "highAvailability": - return ec.fieldContext_PostgresInstance_highAvailability(ctx, field) + return ec.fieldContext_PostgresAccess_teamEnvironment(ctx, field) + case "postgresBranch": + return ec.fieldContext_PostgresAccess_postgresBranch(ctx, field) + case "accessLevel": + return ec.fieldContext_PostgresAccess_accessLevel(ctx, field) + case "expiresAt": + return ec.fieldContext_PostgresAccess_expiresAt(ctx, field) case "state": - return ec.fieldContext_PostgresInstance_state(ctx, field) - case "maintenanceWindow": - return ec.fieldContext_PostgresInstance_maintenanceWindow(ctx, field) - case "labels": - return ec.fieldContext_PostgresInstance_labels(ctx, field) + return ec.fieldContext_PostgresAccess_state(ctx, field) + case "message": + return ec.fieldContext_PostgresAccess_message(ctx, field) + case "relayAccess": + return ec.fieldContext_PostgresAccess_relayAccess(ctx, field) + case "connection": + return ec.fieldContext_PostgresAccess_connection(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type PostgresInstance", field.Name) + return nil, fmt.Errorf("no field named %q was found under type PostgresAccess", field.Name) } -func (ec *executionContext) childFields_PostgresInstanceAudit(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { +func (ec *executionContext) childFields_PostgresAccessConnectionDetails(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "enabled": - return ec.fieldContext_PostgresInstanceAudit_enabled(ctx, field) - case "url": - return ec.fieldContext_PostgresInstanceAudit_url(ctx, field) - case "statementClasses": - return ec.fieldContext_PostgresInstanceAudit_statementClasses(ctx, field) + case "username": + return ec.fieldContext_PostgresAccessConnectionDetails_username(ctx, field) + case "password": + return ec.fieldContext_PostgresAccessConnectionDetails_password(ctx, field) + case "caCertificate": + return ec.fieldContext_PostgresAccessConnectionDetails_caCertificate(ctx, field) + case "serverName": + return ec.fieldContext_PostgresAccessConnectionDetails_serverName(ctx, field) + case "relayEndpoint": + return ec.fieldContext_PostgresAccessConnectionDetails_relayEndpoint(ctx, field) + case "relayAccess": + return ec.fieldContext_PostgresAccessConnectionDetails_relayAccess(ctx, field) + case "relayToken": + return ec.fieldContext_PostgresAccessConnectionDetails_relayToken(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PostgresAccessConnectionDetails", field.Name) +} + +func (ec *executionContext) childFields_PostgresBranch(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_PostgresBranch_id(ctx, field) + case "name": + return ec.fieldContext_PostgresBranch_name(ctx, field) + case "team": + return ec.fieldContext_PostgresBranch_team(ctx, field) + case "teamEnvironment": + return ec.fieldContext_PostgresBranch_teamEnvironment(ctx, field) + case "postgres": + return ec.fieldContext_PostgresBranch_postgres(ctx, field) + case "workloads": + return ec.fieldContext_PostgresBranch_workloads(ctx, field) + case "state": + return ec.fieldContext_PostgresBranch_state(ctx, field) + case "labels": + return ec.fieldContext_PostgresBranch_labels(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type PostgresInstanceAudit", field.Name) + return nil, fmt.Errorf("no field named %q was found under type PostgresBranch", field.Name) } -func (ec *executionContext) childFields_PostgresInstanceConnection(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { +func (ec *executionContext) childFields_PostgresBranchConnection(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "pageInfo": - return ec.fieldContext_PostgresInstanceConnection_pageInfo(ctx, field) + return ec.fieldContext_PostgresBranchConnection_pageInfo(ctx, field) case "nodes": - return ec.fieldContext_PostgresInstanceConnection_nodes(ctx, field) + return ec.fieldContext_PostgresBranchConnection_nodes(ctx, field) case "edges": - return ec.fieldContext_PostgresInstanceConnection_edges(ctx, field) + return ec.fieldContext_PostgresBranchConnection_edges(ctx, field) case "facets": - return ec.fieldContext_PostgresInstanceConnection_facets(ctx, field) + return ec.fieldContext_PostgresBranchConnection_facets(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type PostgresInstanceConnection", field.Name) + return nil, fmt.Errorf("no field named %q was found under type PostgresBranchConnection", field.Name) } -func (ec *executionContext) childFields_PostgresInstanceEdge(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { +func (ec *executionContext) childFields_PostgresBranchEdge(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "cursor": - return ec.fieldContext_PostgresInstanceEdge_cursor(ctx, field) + return ec.fieldContext_PostgresBranchEdge_cursor(ctx, field) case "node": - return ec.fieldContext_PostgresInstanceEdge_node(ctx, field) + return ec.fieldContext_PostgresBranchEdge_node(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type PostgresInstanceEdge", field.Name) + return nil, fmt.Errorf("no field named %q was found under type PostgresBranchEdge", field.Name) } -func (ec *executionContext) childFields_PostgresInstanceFacets(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { +func (ec *executionContext) childFields_PostgresBranchFacets(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "environments": - return ec.fieldContext_PostgresInstanceFacets_environments(ctx, field) + return ec.fieldContext_PostgresBranchFacets_environments(ctx, field) case "states": - return ec.fieldContext_PostgresInstanceFacets_states(ctx, field) - case "highAvailability": - return ec.fieldContext_PostgresInstanceFacets_highAvailability(ctx, field) - case "majorVersions": - return ec.fieldContext_PostgresInstanceFacets_majorVersions(ctx, field) + return ec.fieldContext_PostgresBranchFacets_states(ctx, field) case "labels": - return ec.fieldContext_PostgresInstanceFacets_labels(ctx, field) + return ec.fieldContext_PostgresBranchFacets_labels(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type PostgresInstanceFacets", field.Name) + return nil, fmt.Errorf("no field named %q was found under type PostgresBranchFacets", field.Name) } -func (ec *executionContext) childFields_PostgresInstanceMaintenanceWindow(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { +func (ec *executionContext) childFields_PostgresBranchStateFacetItem(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "day": - return ec.fieldContext_PostgresInstanceMaintenanceWindow_day(ctx, field) - case "hour": - return ec.fieldContext_PostgresInstanceMaintenanceWindow_hour(ctx, field) + case "state": + return ec.fieldContext_PostgresBranchStateFacetItem_state(ctx, field) + case "count": + return ec.fieldContext_PostgresBranchStateFacetItem_count(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type PostgresInstanceMaintenanceWindow", field.Name) + return nil, fmt.Errorf("no field named %q was found under type PostgresBranchStateFacetItem", field.Name) } -func (ec *executionContext) childFields_PostgresInstanceResources(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { +func (ec *executionContext) childFields_PostgresGrantAccessActivityLogEntryData(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "cpu": - return ec.fieldContext_PostgresInstanceResources_cpu(ctx, field) - case "memory": - return ec.fieldContext_PostgresInstanceResources_memory(ctx, field) - case "diskSize": - return ec.fieldContext_PostgresInstanceResources_diskSize(ctx, field) + case "grantee": + return ec.fieldContext_PostgresGrantAccessActivityLogEntryData_grantee(ctx, field) + case "until": + return ec.fieldContext_PostgresGrantAccessActivityLogEntryData_until(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type PostgresInstanceResources", field.Name) + return nil, fmt.Errorf("no field named %q was found under type PostgresGrantAccessActivityLogEntryData", field.Name) } -func (ec *executionContext) childFields_PostgresInstanceStateFacetItem(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { +func (ec *executionContext) childFields_PostgresPersonalAccessCreatedActivityLogEntryData(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "state": - return ec.fieldContext_PostgresInstanceStateFacetItem_state(ctx, field) - case "count": - return ec.fieldContext_PostgresInstanceStateFacetItem_count(ctx, field) + case "username": + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntryData_username(ctx, field) + case "accessLevel": + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntryData_accessLevel(ctx, field) + case "expiresAt": + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntryData_expiresAt(ctx, field) + case "reason": + return ec.fieldContext_PostgresPersonalAccessCreatedActivityLogEntryData_reason(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type PostgresPersonalAccessCreatedActivityLogEntryData", field.Name) +} + +func (ec *executionContext) childFields_PostgresResources(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "cpu": + return ec.fieldContext_PostgresResources_cpu(ctx, field) + case "memory": + return ec.fieldContext_PostgresResources_memory(ctx, field) + case "diskSize": + return ec.fieldContext_PostgresResources_diskSize(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type PostgresInstanceStateFacetItem", field.Name) + return nil, fmt.Errorf("no field named %q was found under type PostgresResources", field.Name) } func (ec *executionContext) childFields_Price(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { @@ -38352,8 +38976,8 @@ func (ec *executionContext) childFields_Team(ctx context.Context, field graphql. return ec.fieldContext_Team_kafkaTopics(ctx, field) case "openSearches": return ec.fieldContext_Team_openSearches(ctx, field) - case "postgresInstances": - return ec.fieldContext_Team_postgresInstances(ctx, field) + case "postgresBranches": + return ec.fieldContext_Team_postgresBranches(ctx, field) case "repositories": return ec.fieldContext_Team_repositories(ctx, field) case "secrets": @@ -38510,8 +39134,10 @@ func (ec *executionContext) childFields_TeamEnvironment(ctx context.Context, fie return ec.fieldContext_TeamEnvironment_kafkaTopic(ctx, field) case "openSearch": return ec.fieldContext_TeamEnvironment_openSearch(ctx, field) - case "postgresInstance": - return ec.fieldContext_TeamEnvironment_postgresInstance(ctx, field) + case "postgres": + return ec.fieldContext_TeamEnvironment_postgres(ctx, field) + case "postgresAccess": + return ec.fieldContext_TeamEnvironment_postgresAccess(ctx, field) case "secret": return ec.fieldContext_TeamEnvironment_secret(ctx, field) case "sqlInstance": @@ -38674,12 +39300,12 @@ func (ec *executionContext) childFields_TeamInventoryCountOpenSearches(ctx conte return nil, fmt.Errorf("no field named %q was found under type TeamInventoryCountOpenSearches", field.Name) } -func (ec *executionContext) childFields_TeamInventoryCountPostgresInstances(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { +func (ec *executionContext) childFields_TeamInventoryCountPostgresBranches(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { case "total": - return ec.fieldContext_TeamInventoryCountPostgresInstances_total(ctx, field) + return ec.fieldContext_TeamInventoryCountPostgresBranches_total(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type TeamInventoryCountPostgresInstances", field.Name) + return nil, fmt.Errorf("no field named %q was found under type TeamInventoryCountPostgresBranches", field.Name) } func (ec *executionContext) childFields_TeamInventoryCountSecrets(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { @@ -38722,8 +39348,8 @@ func (ec *executionContext) childFields_TeamInventoryCounts(ctx context.Context, return ec.fieldContext_TeamInventoryCounts_kafkaTopics(ctx, field) case "openSearches": return ec.fieldContext_TeamInventoryCounts_openSearches(ctx, field) - case "postgresInstances": - return ec.fieldContext_TeamInventoryCounts_postgresInstances(ctx, field) + case "postgresBranches": + return ec.fieldContext_TeamInventoryCounts_postgresBranches(ctx, field) case "secrets": return ec.fieldContext_TeamInventoryCounts_secrets(ctx, field) case "sqlInstances": diff --git a/internal/graph/gengql/schema.generated.go b/internal/graph/gengql/schema.generated.go index b250f46cd..be77e6e4e 100644 --- a/internal/graph/gengql/schema.generated.go +++ b/internal/graph/gengql/schema.generated.go @@ -85,7 +85,8 @@ type MutationResolver interface { DeleteOpenSearch(ctx context.Context, input opensearch.DeleteOpenSearchInput) (*opensearch.DeleteOpenSearchPayload, error) CreateOpenSearchCredentials(ctx context.Context, input opensearch.CreateOpenSearchCredentialsInput) (*opensearch.CreateOpenSearchCredentialsPayload, error) GrantPostgresAccess(ctx context.Context, input postgres.GrantPostgresAccessInput) (*postgres.GrantPostgresAccessPayload, error) - DeletePostgres(ctx context.Context, input postgres.DeletePostgresInput) (*postgres.DeletePostgresPayload, error) + CreatePostgresAccess(ctx context.Context, input postgres.CreatePostgresAccessInput) (*postgres.CreatePostgresAccessPayload, error) + DeletePostgresBranch(ctx context.Context, input postgres.DeletePostgresBranchInput) (*postgres.DeletePostgresBranchPayload, error) EnableReconciler(ctx context.Context, input reconciler.EnableReconcilerInput) (*reconciler.Reconciler, error) DisableReconciler(ctx context.Context, input reconciler.DisableReconcilerInput) (*reconciler.Reconciler, error) ConfigureReconciler(ctx context.Context, input reconciler.ConfigureReconcilerInput) (*reconciler.Reconciler, error) @@ -364,6 +365,20 @@ func (ec *executionContext) field_Mutation_createOpenSearch_args(ctx context.Con return args, nil } +func (ec *executionContext) field_Mutation_createPostgresAccess_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", + func(ctx context.Context, v any) (postgres.CreatePostgresAccessInput, error) { + return ec.unmarshalNCreatePostgresAccessInput2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐCreatePostgresAccessInput(ctx, v) + }) + if err != nil { + return nil, err + } + args["input"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_createSecret_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} @@ -546,12 +561,12 @@ func (ec *executionContext) field_Mutation_deleteOpenSearch_args(ctx context.Con return args, nil } -func (ec *executionContext) field_Mutation_deletePostgres_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { +func (ec *executionContext) field_Mutation_deletePostgresBranch_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} arg0, err := graphql.ProcessArgField(ctx, rawArgs, "input", - func(ctx context.Context, v any) (postgres.DeletePostgresInput, error) { - return ec.unmarshalNDeletePostgresInput2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐDeletePostgresInput(ctx, v) + func(ctx context.Context, v any) (postgres.DeletePostgresBranchInput, error) { + return ec.unmarshalNDeletePostgresBranchInput2githubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐDeletePostgresBranchInput(ctx, v) }) if err != nil { return nil, err @@ -2680,34 +2695,78 @@ func (ec *executionContext) fieldContext_Mutation_grantPostgresAccess(ctx contex return fc, nil } -func (ec *executionContext) _Mutation_deletePostgres(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { +func (ec *executionContext) _Mutation_createPostgresAccess(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_Mutation_createPostgresAccess(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.Mutation().CreatePostgresAccess(ctx, fc.Args["input"].(postgres.CreatePostgresAccessInput)) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *postgres.CreatePostgresAccessPayload) graphql.Marshaler { + return ec.marshalNCreatePostgresAccessPayload2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐCreatePostgresAccessPayload(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_Mutation_createPostgresAccess(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Mutation", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_CreatePostgresAccessPayload(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Mutation_createPostgresAccess_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _Mutation_deletePostgresBranch(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Mutation_deletePostgres(ctx, field) + return ec.fieldContext_Mutation_deletePostgresBranch(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Mutation().DeletePostgres(ctx, fc.Args["input"].(postgres.DeletePostgresInput)) + return ec.Resolvers.Mutation().DeletePostgresBranch(ctx, fc.Args["input"].(postgres.DeletePostgresBranchInput)) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *postgres.DeletePostgresPayload) graphql.Marshaler { - return ec.marshalNDeletePostgresPayload2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐDeletePostgresPayload(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *postgres.DeletePostgresBranchPayload) graphql.Marshaler { + return ec.marshalNDeletePostgresBranchPayload2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐDeletePostgresBranchPayload(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Mutation_deletePostgres(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Mutation_deletePostgresBranch(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Mutation", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_DeletePostgresPayload(ctx, field) + return ec.childFields_DeletePostgresBranchPayload(ctx, field) }, } defer func() { @@ -2717,7 +2776,7 @@ func (ec *executionContext) fieldContext_Mutation_deletePostgres(ctx context.Con } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Mutation_deletePostgres_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Mutation_deletePostgresBranch_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -6525,13 +6584,20 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._PrometheusAlert(ctx, sel, obj) - case postgres.PostgresInstance: - return ec._PostgresInstance(ctx, sel, &obj) - case *postgres.PostgresInstance: + case postgres.PostgresPersonalAccessCreatedActivityLogEntry: + return ec._PostgresPersonalAccessCreatedActivityLogEntry(ctx, sel, &obj) + case *postgres.PostgresPersonalAccessCreatedActivityLogEntry: + if obj == nil { + return graphql.Null + } + return ec._PostgresPersonalAccessCreatedActivityLogEntry(ctx, sel, obj) + case postgres.PostgresPersonalAccessConnectionActivityLogEntry: + return ec._PostgresPersonalAccessConnectionActivityLogEntry(ctx, sel, &obj) + case *postgres.PostgresPersonalAccessConnectionActivityLogEntry: if obj == nil { return graphql.Null } - return ec._PostgresInstance(ctx, sel, obj) + return ec._PostgresPersonalAccessConnectionActivityLogEntry(ctx, sel, obj) case postgres.PostgresGrantAccessActivityLogEntry: return ec._PostgresGrantAccessActivityLogEntry(ctx, sel, &obj) case *postgres.PostgresGrantAccessActivityLogEntry: @@ -6546,6 +6612,13 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._PostgresDeletedActivityLogEntry(ctx, sel, obj) + case postgres.PostgresBranch: + return ec._PostgresBranch(ctx, sel, &obj) + case *postgres.PostgresBranch: + if obj == nil { + return graphql.Null + } + return ec._PostgresBranch(ctx, sel, obj) case opensearch.OpenSearchUpdatedActivityLogEntry: return ec._OpenSearchUpdatedActivityLogEntry(ctx, sel, &obj) case *opensearch.OpenSearchUpdatedActivityLogEntry: @@ -6913,6 +6986,20 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj return graphql.Null } return ec._ReconcilerError(ctx, sel, obj) + case postgres.PostgresAccess: + return ec._PostgresAccess(ctx, sel, &obj) + case *postgres.PostgresAccess: + if obj == nil { + return graphql.Null + } + return ec._PostgresAccess(ctx, sel, obj) + case postgres.Postgres: + return ec._Postgres(ctx, sel, &obj) + case *postgres.Postgres: + if obj == nil { + return graphql.Null + } + return ec._Postgres(ctx, sel, obj) case persistence.Persistence: if obj == nil { return graphql.Null @@ -7241,9 +7328,16 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) if out.Values[i] == graphql.Null { out.Invalids++ } - case "deletePostgres": + case "createPostgresAccess": + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_createPostgresAccess(ctx, field) + }) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "deletePostgresBranch": out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { - return ec._Mutation_deletePostgres(ctx, field) + return ec._Mutation_deletePostgresBranch(ctx, field) }) if out.Values[i] == graphql.Null { out.Invalids++ diff --git a/internal/graph/gengql/search.generated.go b/internal/graph/gengql/search.generated.go index d845b9660..60e696125 100644 --- a/internal/graph/gengql/search.generated.go +++ b/internal/graph/gengql/search.generated.go @@ -271,11 +271,11 @@ func (ec *executionContext) _SearchNode(ctx context.Context, sel ast.SelectionSe return graphql.Null } return ec._SqlInstance(ctx, sel, obj) - case *postgres.PostgresInstance: + case *postgres.PostgresBranch: if obj == nil { return graphql.Null } - return ec._PostgresInstance(ctx, sel, obj) + return ec._PostgresBranch(ctx, sel, obj) case kafkatopic.KafkaTopic: return ec._KafkaTopic(ctx, sel, &obj) case *kafkatopic.KafkaTopic: diff --git a/internal/graph/gengql/teams.generated.go b/internal/graph/gengql/teams.generated.go index 093689558..12009886e 100644 --- a/internal/graph/gengql/teams.generated.go +++ b/internal/graph/gengql/teams.generated.go @@ -74,7 +74,7 @@ type TeamResolver interface { Jobs(ctx context.Context, obj *team.Team, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *job.JobOrder, filter *job.TeamJobsFilter) (*pagination.FacetableConnection[*job.Job, *job.TeamJobsFilter], error) KafkaTopics(ctx context.Context, obj *team.Team, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *kafkatopic.KafkaTopicOrder, filter *kafkatopic.KafkaTopicFilter) (*pagination.FacetableConnection[*kafkatopic.KafkaTopic, *kafkatopic.KafkaTopicFilter], error) OpenSearches(ctx context.Context, obj *team.Team, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *opensearch.OpenSearchOrder, filter *opensearch.OpenSearchFilter) (*pagination.FacetableConnection[*opensearch.OpenSearch, *opensearch.OpenSearchFilter], error) - PostgresInstances(ctx context.Context, obj *team.Team, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *postgres.PostgresInstanceOrder, filter *postgres.PostgresInstanceFilter) (*pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter], error) + PostgresBranches(ctx context.Context, obj *team.Team, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *postgres.PostgresBranchOrder, filter *postgres.PostgresBranchFilter) (*pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter], error) Repositories(ctx context.Context, obj *team.Team, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *repository.RepositoryOrder, filter *repository.TeamRepositoryFilter) (*pagination.Connection[*repository.Repository], error) Secrets(ctx context.Context, obj *team.Team, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *secret.SecretOrder, filter *secret.SecretFilter) (*pagination.FacetableConnection[*secret.Secret, *secret.SecretFilter], error) ServiceAccounts(ctx context.Context, obj *team.Team, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) (*pagination.Connection[*serviceaccount.ServiceAccount], error) @@ -105,7 +105,8 @@ type TeamEnvironmentResolver interface { Job(ctx context.Context, obj *team.TeamEnvironment, name string) (*job.Job, error) KafkaTopic(ctx context.Context, obj *team.TeamEnvironment, name string) (*kafkatopic.KafkaTopic, error) OpenSearch(ctx context.Context, obj *team.TeamEnvironment, name string) (*opensearch.OpenSearch, error) - PostgresInstance(ctx context.Context, obj *team.TeamEnvironment, name string) (*postgres.PostgresInstance, error) + Postgres(ctx context.Context, obj *team.TeamEnvironment, name string) (*postgres.Postgres, error) + PostgresAccess(ctx context.Context, obj *team.TeamEnvironment, name string) (*postgres.PostgresAccess, error) Secret(ctx context.Context, obj *team.TeamEnvironment, name string) (*secret.Secret, error) SQLInstance(ctx context.Context, obj *team.TeamEnvironment, name string) (*sqlinstance.SQLInstance, error) Tunnel(ctx context.Context, obj *team.TeamEnvironment, name string) (*tunnel.Tunnel, error) @@ -120,7 +121,7 @@ type TeamInventoryCountsResolver interface { Jobs(ctx context.Context, obj *team.TeamInventoryCounts) (*job.TeamInventoryCountJobs, error) KafkaTopics(ctx context.Context, obj *team.TeamInventoryCounts) (*kafkatopic.TeamInventoryCountKafkaTopics, error) OpenSearches(ctx context.Context, obj *team.TeamInventoryCounts) (*opensearch.TeamInventoryCountOpenSearches, error) - PostgresInstances(ctx context.Context, obj *team.TeamInventoryCounts) (*postgres.TeamInventoryCountPostgresInstances, error) + PostgresBranches(ctx context.Context, obj *team.TeamInventoryCounts) (*postgres.TeamInventoryCountPostgresBranches, error) Secrets(ctx context.Context, obj *team.TeamInventoryCounts) (*secret.TeamInventoryCountSecrets, error) SQLInstances(ctx context.Context, obj *team.TeamInventoryCounts) (*sqlinstance.TeamInventoryCountSQLInstances, error) Valkeys(ctx context.Context, obj *team.TeamInventoryCounts) (*valkey.TeamInventoryCountValkeys, error) @@ -289,7 +290,21 @@ func (ec *executionContext) field_TeamEnvironment_openSearch_args(ctx context.Co return args, nil } -func (ec *executionContext) field_TeamEnvironment_postgresInstance_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { +func (ec *executionContext) field_TeamEnvironment_postgresAccess_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { + var err error + args := map[string]any{} + arg0, err := graphql.ProcessArgField(ctx, rawArgs, "name", + func(ctx context.Context, v any) (string, error) { + return ec.unmarshalNString2string(ctx, v) + }) + if err != nil { + return nil, err + } + args["name"] = arg0 + return args, nil +} + +func (ec *executionContext) field_TeamEnvironment_postgres_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} arg0, err := graphql.ProcessArgField(ctx, rawArgs, "name", @@ -1045,7 +1060,7 @@ func (ec *executionContext) field_Team_openSearches_args(ctx context.Context, ra return args, nil } -func (ec *executionContext) field_Team_postgresInstances_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { +func (ec *executionContext) field_Team_postgresBranches_args(ctx context.Context, rawArgs map[string]any) (map[string]any, error) { var err error args := map[string]any{} arg0, err := graphql.ProcessArgField(ctx, rawArgs, "first", @@ -1081,16 +1096,16 @@ func (ec *executionContext) field_Team_postgresInstances_args(ctx context.Contex } args["before"] = arg3 arg4, err := graphql.ProcessArgField(ctx, rawArgs, "orderBy", - func(ctx context.Context, v any) (*postgres.PostgresInstanceOrder, error) { - return ec.unmarshalOPostgresInstanceOrder2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceOrder(ctx, v) + func(ctx context.Context, v any) (*postgres.PostgresBranchOrder, error) { + return ec.unmarshalOPostgresBranchOrder2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchOrder(ctx, v) }) if err != nil { return nil, err } args["orderBy"] = arg4 arg5, err := graphql.ProcessArgField(ctx, rawArgs, "filter", - func(ctx context.Context, v any) (*postgres.PostgresInstanceFilter, error) { - return ec.unmarshalOPostgresInstanceFilter2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstanceFilter(ctx, v) + func(ctx context.Context, v any) (*postgres.PostgresBranchFilter, error) { + return ec.unmarshalOPostgresBranchFilter2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresBranchFilter(ctx, v) }) if err != nil { return nil, err @@ -2730,34 +2745,34 @@ func (ec *executionContext) fieldContext_Team_openSearches(ctx context.Context, return fc, nil } -func (ec *executionContext) _Team_postgresInstances(ctx context.Context, field graphql.CollectedField, obj *team.Team) (ret graphql.Marshaler) { +func (ec *executionContext) _Team_postgresBranches(ctx context.Context, field graphql.CollectedField, obj *team.Team) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_Team_postgresInstances(ctx, field) + return ec.fieldContext_Team_postgresBranches(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.Team().PostgresInstances(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*pagination.Cursor), fc.Args["last"].(*int), fc.Args["before"].(*pagination.Cursor), fc.Args["orderBy"].(*postgres.PostgresInstanceOrder), fc.Args["filter"].(*postgres.PostgresInstanceFilter)) + return ec.Resolvers.Team().PostgresBranches(ctx, obj, fc.Args["first"].(*int), fc.Args["after"].(*pagination.Cursor), fc.Args["last"].(*int), fc.Args["before"].(*pagination.Cursor), fc.Args["orderBy"].(*postgres.PostgresBranchOrder), fc.Args["filter"].(*postgres.PostgresBranchFilter)) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter]) graphql.Marshaler { - return ec.marshalNPostgresInstanceConnection2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐFacetableConnection(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) graphql.Marshaler { + return ec.marshalNPostgresBranchConnection2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋgraphᚋpaginationᚐFacetableConnection(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_Team_postgresInstances(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_Team_postgresBranches(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "Team", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresInstanceConnection(ctx, field) + return ec.childFields_PostgresBranchConnection(ctx, field) }, } defer func() { @@ -2767,7 +2782,7 @@ func (ec *executionContext) fieldContext_Team_postgresInstances(ctx context.Cont } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_Team_postgresInstances_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_Team_postgresBranches_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -4840,34 +4855,34 @@ func (ec *executionContext) fieldContext_TeamEnvironment_openSearch(ctx context. return fc, nil } -func (ec *executionContext) _TeamEnvironment_postgresInstance(ctx context.Context, field graphql.CollectedField, obj *team.TeamEnvironment) (ret graphql.Marshaler) { +func (ec *executionContext) _TeamEnvironment_postgres(ctx context.Context, field graphql.CollectedField, obj *team.TeamEnvironment) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_TeamEnvironment_postgresInstance(ctx, field) + return ec.fieldContext_TeamEnvironment_postgres(ctx, field) }, func(ctx context.Context) (any, error) { fc := graphql.GetFieldContext(ctx) - return ec.Resolvers.TeamEnvironment().PostgresInstance(ctx, obj, fc.Args["name"].(string)) + return ec.Resolvers.TeamEnvironment().Postgres(ctx, obj, fc.Args["name"].(string)) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresInstance) graphql.Marshaler { - return ec.marshalNPostgresInstance2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresInstance(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *postgres.Postgres) graphql.Marshaler { + return ec.marshalNPostgres2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgres(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_TeamEnvironment_postgresInstance(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TeamEnvironment_postgres(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "TeamEnvironment", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_PostgresInstance(ctx, field) + return ec.childFields_Postgres(ctx, field) }, } defer func() { @@ -4877,7 +4892,51 @@ func (ec *executionContext) fieldContext_TeamEnvironment_postgresInstance(ctx co } }() ctx = graphql.WithFieldContext(ctx, fc) - if fc.Args, err = ec.field_TeamEnvironment_postgresInstance_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + if fc.Args, err = ec.field_TeamEnvironment_postgres_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + +func (ec *executionContext) _TeamEnvironment_postgresAccess(ctx context.Context, field graphql.CollectedField, obj *team.TeamEnvironment) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.fieldContext_TeamEnvironment_postgresAccess(ctx, field) + }, + func(ctx context.Context) (any, error) { + fc := graphql.GetFieldContext(ctx) + return ec.Resolvers.TeamEnvironment().PostgresAccess(ctx, obj, fc.Args["name"].(string)) + }, + nil, + func(ctx context.Context, selections ast.SelectionSet, v *postgres.PostgresAccess) graphql.Marshaler { + return ec.marshalNPostgresAccess2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐPostgresAccess(ctx, selections, v) + }, + true, + true, + ) +} +func (ec *executionContext) fieldContext_TeamEnvironment_postgresAccess(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "TeamEnvironment", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return ec.childFields_PostgresAccess(ctx, field) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_TeamEnvironment_postgresAccess_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { ec.Error(ctx, err) return fc, err } @@ -5906,33 +5965,33 @@ func (ec *executionContext) fieldContext_TeamInventoryCounts_openSearches(_ cont return fc, nil } -func (ec *executionContext) _TeamInventoryCounts_postgresInstances(ctx context.Context, field graphql.CollectedField, obj *team.TeamInventoryCounts) (ret graphql.Marshaler) { +func (ec *executionContext) _TeamInventoryCounts_postgresBranches(ctx context.Context, field graphql.CollectedField, obj *team.TeamInventoryCounts) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, ec.OperationContext, field, func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.fieldContext_TeamInventoryCounts_postgresInstances(ctx, field) + return ec.fieldContext_TeamInventoryCounts_postgresBranches(ctx, field) }, func(ctx context.Context) (any, error) { - return ec.Resolvers.TeamInventoryCounts().PostgresInstances(ctx, obj) + return ec.Resolvers.TeamInventoryCounts().PostgresBranches(ctx, obj) }, nil, - func(ctx context.Context, selections ast.SelectionSet, v *postgres.TeamInventoryCountPostgresInstances) graphql.Marshaler { - return ec.marshalNTeamInventoryCountPostgresInstances2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐTeamInventoryCountPostgresInstances(ctx, selections, v) + func(ctx context.Context, selections ast.SelectionSet, v *postgres.TeamInventoryCountPostgresBranches) graphql.Marshaler { + return ec.marshalNTeamInventoryCountPostgresBranches2ᚖgithubᚗcomᚋnaisᚋapiᚋinternalᚋpersistenceᚋpostgresᚐTeamInventoryCountPostgresBranches(ctx, selections, v) }, true, true, ) } -func (ec *executionContext) fieldContext_TeamInventoryCounts_postgresInstances(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_TeamInventoryCounts_postgresBranches(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "TeamInventoryCounts", Field: field, IsMethod: true, IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - return ec.childFields_TeamInventoryCountPostgresInstances(ctx, field) + return ec.childFields_TeamInventoryCountPostgresBranches(ctx, field) }, } return fc, nil @@ -9161,7 +9220,7 @@ func (ec *executionContext) _Team(ctx context.Context, sel ast.SelectionSet, obj } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "postgresInstances": + case "postgresBranches": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -9170,7 +9229,7 @@ func (ec *executionContext) _Team(ctx context.Context, sel ast.SelectionSet, obj ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._Team_postgresInstances(ctx, field, obj) + res = ec._Team_postgresBranches(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -10620,7 +10679,43 @@ func (ec *executionContext) _TeamEnvironment(ctx context.Context, sel ast.Select } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "postgresInstance": + case "postgres": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._TeamEnvironment_postgres(ctx, field, obj) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + if field.Deferrable != nil { + dfs, ok := deferred[field.Deferrable.Label] + di := 0 + if ok { + dfs.AddField(field) + di = len(dfs.Values) - 1 + } else { + dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) + deferred[field.Deferrable.Label] = dfs + } + dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { + return innerFunc(ctx, dfs) + }) + + // don't run the out.Concurrently() call below + out.Values[i] = graphql.Null + continue + } + + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + case "postgresAccess": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -10629,7 +10724,7 @@ func (ec *executionContext) _TeamEnvironment(ctx context.Context, sel ast.Select ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._TeamEnvironment_postgresInstance(ctx, field, obj) + res = ec._TeamEnvironment_postgresAccess(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -11440,7 +11535,7 @@ func (ec *executionContext) _TeamInventoryCounts(ctx context.Context, sel ast.Se } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "postgresInstances": + case "postgresBranches": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -11449,7 +11544,7 @@ func (ec *executionContext) _TeamInventoryCounts(ctx context.Context, sel ast.Se ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._TeamInventoryCounts_postgresInstances(ctx, field, obj) + res = ec._TeamInventoryCounts_postgresBranches(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } diff --git a/internal/graph/postgres.resolvers.go b/internal/graph/postgres.resolvers.go index ae99730ab..cedffc90f 100644 --- a/internal/graph/postgres.resolvers.go +++ b/internal/graph/postgres.resolvers.go @@ -13,94 +13,121 @@ import ( "github.com/nais/api/internal/workload/job" ) -func (r *applicationResolver) PostgresInstances(ctx context.Context, obj *application.Application, orderBy *postgres.PostgresInstanceOrder) (*pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter], error) { - if obj.Spec.Postgres == nil || obj.Spec.Postgres.ClusterName == "" { - return pagination.NewFacetableConnection(pagination.EmptyConnection[*postgres.PostgresInstance](), nil, (*postgres.PostgresInstanceFilter)(nil)), nil +func (r *applicationResolver) PostgresBranches(ctx context.Context, obj *application.Application, orderBy *postgres.PostgresBranchOrder) (*pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter], error) { + if obj.Spec == nil || obj.Spec.Uses == nil { + return pagination.NewFacetableConnection(pagination.EmptyConnection[*postgres.PostgresBranch](), nil, (*postgres.PostgresBranchFilter)(nil)), nil } - - instance, err := postgres.GetForWorkload(ctx, obj.TeamSlug, obj.EnvironmentName, obj.Spec.Postgres.ClusterName) + instances, err := postgres.ListForWorkload(ctx, obj.TeamSlug, obj.EnvironmentName, obj.Spec.Uses.Postgres) if err != nil { return nil, err } - - if instance == nil { - return pagination.NewFacetableConnection(pagination.EmptyConnection[*postgres.PostgresInstance](), nil, (*postgres.PostgresInstanceFilter)(nil)), nil - } - - instances := []*postgres.PostgresInstance{instance} - return pagination.NewFacetableConnection(pagination.NewConnectionWithoutPagination(instances), instances, (*postgres.PostgresInstanceFilter)(nil)), nil + return pagination.NewFacetableConnection(pagination.NewConnectionWithoutPagination(instances), instances, (*postgres.PostgresBranchFilter)(nil)), nil } -func (r *jobResolver) PostgresInstances(ctx context.Context, obj *job.Job, orderBy *postgres.PostgresInstanceOrder) (*pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter], error) { - if obj.Spec.Postgres == nil || obj.Spec.Postgres.ClusterName == "" { - return pagination.NewFacetableConnection(pagination.EmptyConnection[*postgres.PostgresInstance](), nil, (*postgres.PostgresInstanceFilter)(nil)), nil +func (r *jobResolver) PostgresBranches(ctx context.Context, obj *job.Job, orderBy *postgres.PostgresBranchOrder) (*pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter], error) { + if obj.Spec == nil || obj.Spec.Uses == nil { + return pagination.NewFacetableConnection(pagination.EmptyConnection[*postgres.PostgresBranch](), nil, (*postgres.PostgresBranchFilter)(nil)), nil } - - instance, err := postgres.GetForWorkload(ctx, obj.TeamSlug, obj.EnvironmentName, obj.Spec.Postgres.ClusterName) + instances, err := postgres.ListForWorkload(ctx, obj.TeamSlug, obj.EnvironmentName, obj.Spec.Uses.Postgres) if err != nil { return nil, err } - - if instance == nil { - return pagination.NewFacetableConnection(pagination.EmptyConnection[*postgres.PostgresInstance](), nil, (*postgres.PostgresInstanceFilter)(nil)), nil - } - - instances := []*postgres.PostgresInstance{instance} - return pagination.NewFacetableConnection(pagination.NewConnectionWithoutPagination(instances), instances, (*postgres.PostgresInstanceFilter)(nil)), nil + return pagination.NewFacetableConnection(pagination.NewConnectionWithoutPagination(instances), instances, (*postgres.PostgresBranchFilter)(nil)), nil } func (r *mutationResolver) GrantPostgresAccess(ctx context.Context, input postgres.GrantPostgresAccessInput) (*postgres.GrantPostgresAccessPayload, error) { if err := authz.CanGrantPostgresAccess(ctx, input.TeamSlug); err != nil { return nil, err } - if err := postgres.GrantZalandoPostgresAccess(ctx, input); err != nil { return nil, err } + return &postgres.GrantPostgresAccessPayload{Error: new(string)}, nil +} - return &postgres.GrantPostgresAccessPayload{ - Error: new(string), - }, nil +func (r *mutationResolver) CreatePostgresAccess(ctx context.Context, input postgres.CreatePostgresAccessInput) (*postgres.CreatePostgresAccessPayload, error) { + if err := authz.CanGrantPostgresAccess(ctx, input.TeamSlug); err != nil { + return nil, err + } + + return postgres.CreatePostgresAccess(ctx, input) } -func (r *mutationResolver) DeletePostgres(ctx context.Context, input postgres.DeletePostgresInput) (*postgres.DeletePostgresPayload, error) { +func (r *mutationResolver) DeletePostgresBranch(ctx context.Context, input postgres.DeletePostgresBranchInput) (*postgres.DeletePostgresBranchPayload, error) { if err := authz.CanDeletePostgres(ctx, input.TeamSlug); err != nil { return nil, err } return postgres.Delete(ctx, input) } -func (r *postgresInstanceResolver) Team(ctx context.Context, obj *postgres.PostgresInstance) (*team.Team, error) { +func (r *postgresResolver) ActiveBranch(ctx context.Context, obj *postgres.Postgres) (*postgres.PostgresBranch, error) { + if obj.ActiveBranch == nil { + return nil, nil + } + return postgres.GetPostgresBranch(ctx, obj.TeamSlug, obj.EnvironmentName, obj.Name, *obj.ActiveBranch) +} + +func (r *postgresResolver) Branch(ctx context.Context, obj *postgres.Postgres, name string) (*postgres.PostgresBranch, error) { + return postgres.GetPostgresBranch(ctx, obj.TeamSlug, obj.EnvironmentName, obj.Name, name) +} + +func (r *postgresResolver) Branches(ctx context.Context, obj *postgres.Postgres, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *postgres.PostgresBranchOrder) (*pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter], error) { + page, err := pagination.ParsePage(first, after, last, before) + if err != nil { + return nil, err + } + return postgres.ListForPostgres(ctx, obj, page, orderBy), nil +} + +func (r *postgresAccessResolver) Team(ctx context.Context, obj *postgres.PostgresAccess) (*team.Team, error) { + return team.Get(ctx, obj.TeamSlug) +} + +func (r *postgresAccessResolver) TeamEnvironment(ctx context.Context, obj *postgres.PostgresAccess) (*team.TeamEnvironment, error) { + return team.GetTeamEnvironment(ctx, obj.TeamSlug, obj.EnvironmentName) +} + +func (r *postgresAccessResolver) PostgresBranch(ctx context.Context, obj *postgres.PostgresAccess) (*postgres.PostgresBranch, error) { + return postgres.GetPostgresBranchByObjectName(ctx, obj.TeamSlug, obj.EnvironmentName, obj.PostgresBranchName) +} + +func (r *postgresAccessResolver) Connection(ctx context.Context, obj *postgres.PostgresAccess) (*postgres.PostgresAccessConnectionDetails, error) { + return postgres.GetPostgresAccessConnection(ctx, postgres.PostgresAccessConnectionInput{ + Name: obj.Name, TeamSlug: obj.TeamSlug, EnvironmentName: obj.EnvironmentName, + }) +} + +func (r *postgresBranchResolver) Team(ctx context.Context, obj *postgres.PostgresBranch) (*team.Team, error) { return team.Get(ctx, obj.TeamSlug) } -func (r *postgresInstanceResolver) TeamEnvironment(ctx context.Context, obj *postgres.PostgresInstance) (*team.TeamEnvironment, error) { +func (r *postgresBranchResolver) TeamEnvironment(ctx context.Context, obj *postgres.PostgresBranch) (*team.TeamEnvironment, error) { return team.GetTeamEnvironment(ctx, obj.TeamSlug, obj.EnvironmentName) } -func (r *postgresInstanceResolver) Workloads(ctx context.Context, obj *postgres.PostgresInstance, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) (*pagination.Connection[workload.Workload], error) { +func (r *postgresBranchResolver) Postgres(ctx context.Context, obj *postgres.PostgresBranch) (*postgres.Postgres, error) { + return postgres.GetPostgres(ctx, obj.TeamSlug, obj.EnvironmentName, obj.PostgresName) +} + +func (r *postgresBranchResolver) Workloads(ctx context.Context, obj *postgres.PostgresBranch, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor) (*pagination.Connection[workload.Workload], error) { page, err := pagination.ParsePage(first, after, last, before) if err != nil { return nil, err } - workloads := postgres.WorkloadsForInstance(ctx, obj.TeamSlug, obj.EnvironmentName, obj.Name) + workloads := postgres.WorkloadsForInstance(ctx, obj.TeamSlug, obj.EnvironmentName, obj.PostgresName, obj.Name) return pagination.NewConnection(pagination.Slice(workloads, page), page, len(workloads)), nil } -func (r *postgresInstanceAuditResolver) URL(ctx context.Context, obj *postgres.PostgresInstanceAudit) (*string, error) { - return postgres.GetAuditURL(ctx, obj) -} - -func (r *postgresInstanceConnectionResolver) Facets(ctx context.Context, obj *pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter]) (*postgres.PostgresInstanceFacets, error) { - return &postgres.PostgresInstanceFacets{ +func (r *postgresBranchConnectionResolver) Facets(ctx context.Context, obj *pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter]) (*postgres.PostgresBranchFacets, error) { + return &postgres.PostgresBranchFacets{ AllInstances: obj.GetAllItems(), Filter: obj.GetFilter(), }, nil } -func (r *teamResolver) PostgresInstances(ctx context.Context, obj *team.Team, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *postgres.PostgresInstanceOrder, filter *postgres.PostgresInstanceFilter) (*pagination.FacetableConnection[*postgres.PostgresInstance, *postgres.PostgresInstanceFilter], error) { +func (r *teamResolver) PostgresBranches(ctx context.Context, obj *team.Team, first *int, after *pagination.Cursor, last *int, before *pagination.Cursor, orderBy *postgres.PostgresBranchOrder, filter *postgres.PostgresBranchFilter) (*pagination.FacetableConnection[*postgres.PostgresBranch, *postgres.PostgresBranchFilter], error) { page, err := pagination.ParsePage(first, after, last, before) if err != nil { return nil, err @@ -109,30 +136,33 @@ func (r *teamResolver) PostgresInstances(ctx context.Context, obj *team.Team, fi return postgres.ListForTeam(ctx, obj.Slug, page, orderBy, filter) } -func (r *teamEnvironmentResolver) PostgresInstance(ctx context.Context, obj *team.TeamEnvironment, name string) (*postgres.PostgresInstance, error) { - return postgres.GetZalandoPostgres(ctx, obj.TeamSlug, obj.EnvironmentName, name) +func (r *teamEnvironmentResolver) Postgres(ctx context.Context, obj *team.TeamEnvironment, name string) (*postgres.Postgres, error) { + return postgres.GetPostgres(ctx, obj.TeamSlug, obj.EnvironmentName, name) } -func (r *teamInventoryCountsResolver) PostgresInstances(ctx context.Context, obj *team.TeamInventoryCounts) (*postgres.TeamInventoryCountPostgresInstances, error) { - return &postgres.TeamInventoryCountPostgresInstances{ +func (r *teamEnvironmentResolver) PostgresAccess(ctx context.Context, obj *team.TeamEnvironment, name string) (*postgres.PostgresAccess, error) { + return postgres.GetPostgresAccess(ctx, name, obj.TeamSlug, obj.EnvironmentName) +} + +func (r *teamInventoryCountsResolver) PostgresBranches(ctx context.Context, obj *team.TeamInventoryCounts) (*postgres.TeamInventoryCountPostgresBranches, error) { + return &postgres.TeamInventoryCountPostgresBranches{ Total: postgres.CountForTeam(ctx, obj.TeamSlug), }, nil } -func (r *Resolver) PostgresInstance() gengql.PostgresInstanceResolver { - return &postgresInstanceResolver{r} -} +func (r *Resolver) Postgres() gengql.PostgresResolver { return &postgresResolver{r} } -func (r *Resolver) PostgresInstanceAudit() gengql.PostgresInstanceAuditResolver { - return &postgresInstanceAuditResolver{r} -} +func (r *Resolver) PostgresAccess() gengql.PostgresAccessResolver { return &postgresAccessResolver{r} } + +func (r *Resolver) PostgresBranch() gengql.PostgresBranchResolver { return &postgresBranchResolver{r} } -func (r *Resolver) PostgresInstanceConnection() gengql.PostgresInstanceConnectionResolver { - return &postgresInstanceConnectionResolver{r} +func (r *Resolver) PostgresBranchConnection() gengql.PostgresBranchConnectionResolver { + return &postgresBranchConnectionResolver{r} } type ( - postgresInstanceResolver struct{ *Resolver } - postgresInstanceAuditResolver struct{ *Resolver } - postgresInstanceConnectionResolver struct{ *Resolver } + postgresResolver struct{ *Resolver } + postgresAccessResolver struct{ *Resolver } + postgresBranchResolver struct{ *Resolver } + postgresBranchConnectionResolver struct{ *Resolver } ) diff --git a/internal/graph/schema/postgres.graphqls b/internal/graph/schema/postgres.graphqls index 07b96706a..c99cc8e59 100644 --- a/internal/graph/schema/postgres.graphqls +++ b/internal/graph/schema/postgres.graphqls @@ -1,6 +1,6 @@ extend type Team { - "Postgres instances owned by the team." - postgresInstances( + "Postgres branches owned by the team." + postgresBranches( "Get the first n items in the connection. This can be used in combination with the after parameter." first: Int @@ -14,81 +14,87 @@ extend type Team { before: Cursor "Ordering options for items returned from the connection." - orderBy: PostgresInstanceOrder + orderBy: PostgresBranchOrder "Filtering options for items returned from the connection." - filter: PostgresInstanceFilter - ): PostgresInstanceConnection! + filter: PostgresBranchFilter + ): PostgresBranchConnection! } extend type TeamEnvironment { - "Postgres instance in the team environment." - postgresInstance(name: String!): PostgresInstance! + "Postgres in the team environment." + postgres("Name of the Postgres in this team environment." name: String!): Postgres! + """ + EXPERIMENTAL: DO NOT USE + Get a PostgresAccess and its state. Available to authorized team members. + """ + postgresAccess( + "Name of the PostgresAccess in this team environment." + name: String! + ): PostgresAccess! } extend interface Workload { - "Postgres instances referenced by the workload. This does not currently support pagination, but will return all available Postgres instances." - postgresInstances( + "Active PostgresBranches for all Postgres entries in uses.postgres." + postgresBranches( "Ordering options for items returned from the connection." - orderBy: PostgresInstanceOrder - ): PostgresInstanceConnection! + orderBy: PostgresBranchOrder + ): PostgresBranchConnection! } extend type Application { - "Postgres instances referenced by the application. This does not currently support pagination, but will return all available Postgres instances." - postgresInstances( + "Active PostgresBranches for all Postgres entries in uses.postgres." + postgresBranches( "Ordering options for items returned from the connection." - orderBy: PostgresInstanceOrder - ): PostgresInstanceConnection! + orderBy: PostgresBranchOrder + ): PostgresBranchConnection! } extend type Job { - "Postgres instances referenced by the job. This does not currently support pagination, but will return all available Postgres instances." - postgresInstances( + "Active PostgresBranches for all Postgres entries in uses.postgres." + postgresBranches( "Ordering options for items returned from the connection." - orderBy: PostgresInstanceOrder - ): PostgresInstanceConnection! + orderBy: PostgresBranchOrder + ): PostgresBranchConnection! } -input PostgresInstanceOrder { - field: PostgresInstanceOrderField! +input PostgresBranchOrder { + field: PostgresBranchOrderField! direction: OrderDirection! } """ -Input for filtering Postgres instances. +Input for filtering Postgres branches. """ -input PostgresInstanceFilter { - "Filter by the name of the instance." +input PostgresBranchFilter { + "Filter by the name of the branch." name: String "Filter by environments." environments: [String!] - "Filter by instance state." - states: [PostgresInstanceState!] - - "Filter by high availability." - highAvailability: Boolean - - "Filter by major versions." - majorVersions: [String!] + "Filter by branch state." + states: [PostgresBranchState!] "Filter by user-defined labels. All listed labels must match." labels: [LabelFilter!] } -enum PostgresInstanceOrderField { +enum PostgresBranchOrderField { NAME ENVIRONMENT } -type PostgresInstance implements Persistence & Node { +"A named PostgresBranch belonging to a Postgres." +type PostgresBranch implements Persistence & Node { id: ID! + "Local name of this branch within its Postgres." name: String! team: Team! teamEnvironment: TeamEnvironment! - "Workloads that reference the Postgres instance." + "Postgres owning this PostgresBranch." + postgres: Postgres! + "Workloads using this branch while it is active." workloads( "Get the first n items in the connection. This can be used in combination with the after parameter." first: Int @@ -102,100 +108,106 @@ type PostgresInstance implements Persistence & Node { "Get items before this cursor." before: Cursor ): WorkloadConnection! - "Resource allocation for the Postgres cluster." - resources: PostgresInstanceResources! - "Major version of PostgreSQL." + "Current observed state of the branch." + state: PostgresBranchState! + "User-defined labels on this branch." + labels: [ResourceLabel!]! +} + +"A Postgres whose active branch can change." +type Postgres implements Node { + "Opaque identifier for this Postgres." + id: ID! + "Name of this Postgres." + name: String! + "Configured PostgreSQL major version." majorVersion: String! - "Audit logging configuration for the Postgres cluster." - audit: PostgresInstanceAudit! - "Indicates whether the Postgres cluster is configured for high availability." + "Whether high availability is configured." highAvailability: Boolean! - "Current state of the Postgres cluster." - state: PostgresInstanceState! - "Maintenance window for the Postgres cluster, if configured." - maintenanceWindow: PostgresInstanceMaintenanceWindow - "User-defined labels attached to this instance." + "Requested CPU, memory and disk size, when present on this Postgres." + resources: PostgresResources! + "Currently active branch, if selected." + activeBranch: PostgresBranch + "Branch with this local name in this Postgres." + branch(name: String!): PostgresBranch! + "Branches belonging to this Postgres." + branches( + first: Int + after: Cursor + last: Int + before: Cursor + orderBy: PostgresBranchOrder + ): PostgresBranchConnection! + "User-defined labels on this Postgres." labels: [ResourceLabel!]! } -enum PostgresInstanceState { +"Resource requests configured on Postgres. Omitted requests are null." +type PostgresResources { + "Requested CPU." + cpu: String + "Requested memory." + memory: String + "Requested disk size." + diskSize: String +} + +"Reconciliation and observed health of a PostgresBranch." +enum PostgresBranchState { + "The branch is healthy and ready." AVAILABLE + "The branch is provisioning or its state has not been observed yet." PROGRESSING + "The branch has reported a failure." DEGRADED } -type PostgresInstanceMaintenanceWindow { - day: Int! - hour: Int! -} - -type PostgresInstanceAudit { - "Indicates whether audit logging is enabled for the Postgres cluster." - enabled: Boolean! - "URL for accessing the audit logs." - url: String - "List of statement classes that are being logged, such as `ddl`, `dml`, and `read`." - statementClasses: [String!] -} - -type PostgresInstanceConnection { +type PostgresBranchConnection { pageInfo: PageInfo! - nodes: [PostgresInstance!]! - edges: [PostgresInstanceEdge!]! + nodes: [PostgresBranch!]! + edges: [PostgresBranchEdge!]! """ - Facets for Postgres instances. Provides distribution counts to help narrow down results. + Facets for Postgres branches. Provides distribution counts to help narrow down results. Facet counts are computed over the full result set (ignoring pagination) but respect the current filter. """ - facets: PostgresInstanceFacets + facets: PostgresBranchFacets } -type PostgresInstanceEdge { +type PostgresBranchEdge { cursor: Cursor! - node: PostgresInstance! + node: PostgresBranch! } """ -Facets for Postgres instances, providing distribution counts across different dimensions. +Facets for Postgres branches, providing distribution counts across different dimensions. """ -type PostgresInstanceFacets { - "Distribution of instances by environment." +type PostgresBranchFacets { + "Distribution of branches by environment." environments: [StringFacetItem!]! - "Distribution of instances by state." - states: [PostgresInstanceStateFacetItem!]! - - "Distribution of instances by high availability." - highAvailability: [BooleanFacetItem!]! - - "Distribution of instances by major version." - majorVersions: [StringFacetItem!]! + "Distribution of branches by state." + states: [PostgresBranchStateFacetItem!]! - "Distribution of instances by user-defined labels." + "Distribution of branches by user-defined labels." labels: [LabelFacetItem!]! } """ -A single facet item for Postgres instance states. +A single facet item for Postgres branch states. """ -type PostgresInstanceStateFacetItem { - "The Postgres instance state." - state: PostgresInstanceState! +type PostgresBranchStateFacetItem { + "The Postgres branch state." + state: PostgresBranchState! - "Number of matching instances." + "Number of matching branches." count: Int! } -type PostgresInstanceResources { - cpu: String! - memory: String! - diskSize: String! -} - -extend union SearchNode = PostgresInstance +extend union SearchNode = PostgresBranch extend enum SearchType { - POSTGRES + POSTGRES_BRANCH } extend enum ActivityLogEntryResourceType { @@ -242,6 +254,64 @@ type PostgresGrantAccessActivityLogEntryData { until: Time! } +"An audit-log entry for personal Postgres access created through the API broker." +type PostgresPersonalAccessCreatedActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + "The identity of the actor who created the personal access." + actor: String! + "GitHub Actions OIDC claims when authenticated by a GitHub repository." + gitHubActorClaims: GitHubActorClaims + "Creation time of the entry." + createdAt: Time! + "Message that summarizes the entry." + message: String! + "Type of the affected resource." + resourceType: ActivityLogEntryResourceType! + "Name of the affected Postgres branch." + resourceName: String! + "The team slug that the entry belongs to." + teamSlug: Slug! + "The environment name that the entry belongs to." + environmentName: String + "Personal-access specific audit data." + data: PostgresPersonalAccessCreatedActivityLogEntryData! +} + +"Personal-access-specific audit data." +type PostgresPersonalAccessCreatedActivityLogEntryData { + "Identity that owns the new personal access." + username: String! + "Requested privilege level; null for events recorded before this field was added." + accessLevel: PostgresAccessLevel + "Server-controlled expiry of the access." + expiresAt: Time! + "Caller-provided audit reason." + reason: String! +} + +"An audit-log entry for retrieval of personal Postgres connection materials." +type PostgresPersonalAccessConnectionActivityLogEntry implements ActivityLogEntry & Node { + "ID of the entry." + id: ID! + "Identity that retrieved the connection materials." + actor: String! + "GitHub Actions OIDC claims when authenticated by a GitHub repository." + gitHubActorClaims: GitHubActorClaims + "Creation time of the entry." + createdAt: Time! + "Message that summarizes the entry." + message: String! + "Type of the affected resource." + resourceType: ActivityLogEntryResourceType! + "Name of the affected PostgresAccess resource." + resourceName: String! + "Team slug that the entry belongs to." + teamSlug: Slug! + "Environment name that the entry belongs to." + environmentName: String +} + type PostgresDeletedActivityLogEntry implements ActivityLogEntry & Node { "ID of the entry." id: ID! @@ -277,7 +347,15 @@ extend enum ActivityLogActivityType { """ POSTGRES_GRANT_ACCESS """ - A Postgres instance was deleted + A personal Postgres access was created through the API broker + """ + POSTGRES_PERSONAL_ACCESS_CREATED + """ + Personal Postgres connection materials were retrieved + """ + POSTGRES_PERSONAL_ACCESS_CONNECTION + """ + A Postgres branch was deleted """ POSTGRES_DELETED } @@ -285,8 +363,14 @@ extend enum ActivityLogActivityType { extend type Mutation { "Grant temporary access to a Postgres cluster." grantPostgresAccess(input: GrantPostgresAccessInput!): GrantPostgresAccessPayload! - "Delete an existing Postgres instance." - deletePostgres(input: DeletePostgresInput!): DeletePostgresPayload! + """ + EXPERIMENTAL: DO NOT USE + Create time-limited personal access to a NAIS Postgres branch through the brokered PostgresAccess and relay flow. + When the access is ready, retrieve its connection materials through PostgresAccess.connection. + """ + createPostgresAccess(input: CreatePostgresAccessInput!): CreatePostgresAccessPayload! + "Delete a PostgresBranch that is not active on its Postgres." + deletePostgresBranch(input: DeletePostgresBranchInput!): DeletePostgresBranchPayload! } type GrantPostgresAccessPayload { @@ -302,25 +386,122 @@ input GrantPostgresAccessInput { duration: String! } -input DeletePostgresInput { - "Name of the Postgres instance." +"Result of creating a personal Postgres access." +type CreatePostgresAccessPayload { + "Name of the newly created PostgresAccess resource." name: String! - "The environment name that the Postgres instance belongs to." + "Server-controlled expiry for this personal access." + expiresAt: Time! +} + +"Input for creating a time-limited personal Postgres access." +input CreatePostgresAccessInput { + "Name of the Postgres containing the branch." + postgres: String! + "Local name of the branch to access." + branch: String! + "Team that owns the Postgres branch." + teamSlug: Slug! + "Environment containing the Postgres branch." environmentName: String! - "The team that owns the Postgres instance." + "Privileges requested for the personal database role." + accessLevel: PostgresAccessLevel! + "Reason for personal database access. Must be at least 10 characters." + reason: String! + "Requested access lifetime (for example '30m' or '1h'). Defaults to '1h' and cannot exceed '1h'." + ttl: String +} + +"Privilege level granted to a personal Postgres database role." +enum PostgresAccessLevel { + "Read data without modifying it." + READ + "Read and modify existing data." + READWRITE + "Read, modify, and create database objects where supported." + READWRITECREATE +} + +input DeletePostgresBranchInput { + "Name of the Postgres containing the branch." + postgres: String! + "Local name of the branch to delete." + branch: String! + "The environment containing the PostgresBranch." + environmentName: String! + "The team that owns the PostgresBranch." teamSlug: Slug! } -type DeletePostgresPayload { - "Whether or not the Postgres instance was deleted." - postgresDeleted: Boolean +type DeletePostgresBranchPayload { + "Whether the PostgresBranch was deleted." + postgresBranchDeleted: Boolean } extend type TeamInventoryCounts { - postgresInstances: TeamInventoryCountPostgresInstances! + postgresBranches: TeamInventoryCountPostgresBranches! } -type TeamInventoryCountPostgresInstances { - "Total number of Postgres instances." +type TeamInventoryCountPostgresBranches { + "Total number of Postgres branches." total: Int! } + +"A time-limited personal access request for a Postgres branch." +type PostgresAccess implements Node { + "Opaque ID for this PostgresAccess resource." + id: ID! + "Name of the PostgresAccess resource." + name: String! + "Team that owns the access." + team: Team! + "Environment for the access." + teamEnvironment: TeamEnvironment! + "PostgresBranch selected by this access." + postgresBranch: PostgresBranch! + "Requested access level." + accessLevel: PostgresAccessLevel! + "Server-controlled expiry for this personal access." + expiresAt: Time! + "High-level state of the access." + state: PostgresAccessState! + "Human-readable message for the current state." + message: String + "Name of the controller-owned relay mapping, once created. Contains no credential." + relayAccess: String + """ + EXPERIMENTAL: DO NOT USE + Get connection materials for this ready access. Only its owner can read them. + """ + connection: PostgresAccessConnectionDetails +} + +"High-level reconciliation state of a personal Postgres access." +enum PostgresAccessState { + "The controller has not finished provisioning the access." + PENDING + "The access and its connection materials are ready." + READY + "The controller cannot provision the requested access." + FAILED + "The server-controlled expiry time has passed." + EXPIRED +} + +"Sensitive connection materials for a ready personal Postgres access." +type PostgresAccessConnectionDetails { + "Database username for the caller's personal role." + username: String! + "Short-lived password for the caller's database role." + password: String! + "CA certificate required to verify the PostgreSQL server certificate." + caCertificate: String! + "PostgreSQL server name used for TLS verification." + serverName: String! + "Public HTTP/3 relay endpoint." + relayEndpoint: String! + "Relay-Access header value (namespace/name)." + relayAccess: String! + "Owner-only bearer token for this access; never log it." + relayToken: String! +} diff --git a/internal/grpc/grpc.go b/internal/grpc/grpc.go index 132362244..f3df1ef3c 100644 --- a/internal/grpc/grpc.go +++ b/internal/grpc/grpc.go @@ -20,7 +20,7 @@ import ( "google.golang.org/grpc" ) -func Run(ctx context.Context, listenAddress string, pool *pgxpool.Pool, sqlDatabaseWatcher *watchers.SqlDatabaseWatcher, zalandoPostgresWatcher *watchers.ZalandoPostgresWatcher, log logrus.FieldLogger) error { +func Run(ctx context.Context, listenAddress string, pool *pgxpool.Pool, sqlDatabaseWatcher *watchers.SqlDatabaseWatcher, postgresBranchWatcher *watchers.PostgresBranchWatcher, log logrus.FieldLogger) error { log.Info("GRPC serving on ", listenAddress) lis, err := net.Listen("tcp", listenAddress) if err != nil { @@ -36,7 +36,7 @@ func Run(ctx context.Context, listenAddress string, pool *pgxpool.Pool, sqlDatab protoapi.RegisterUsersServer(s, grpcuser.NewServer(pool)) protoapi.RegisterReconcilersServer(s, grpcreconciler.NewServer(pool)) protoapi.RegisterDeploymentsServer(s, grpcdeployment.NewServer(pool)) - protoapi.RegisterDatabasesServer(s, grpcdatabase.NewServer(sqlDatabaseWatcher, zalandoPostgresWatcher)) + protoapi.RegisterDatabasesServer(s, grpcdatabase.NewServer(sqlDatabaseWatcher, postgresBranchWatcher)) g, ctx := errgroup.WithContext(ctx) g.Go(func() error { return s.Serve(lis) }) diff --git a/internal/grpc/grpcdatabase/postgres_branch_test.go b/internal/grpc/grpcdatabase/postgres_branch_test.go new file mode 100644 index 000000000..c9b10d676 --- /dev/null +++ b/internal/grpc/grpcdatabase/postgres_branch_test.go @@ -0,0 +1,24 @@ +package grpcdatabase + +import ( + "testing" + + "github.com/nais/api/internal/persistence/postgres" +) + +func TestPostgresBranchDatabaseName(t *testing.T) { + for _, tt := range []struct { + branch string + want string + }{ + {branch: "main", want: "orders"}, + {branch: "recovered", want: "orders/recovered"}, + } { + t.Run(tt.branch, func(t *testing.T) { + got := postgresBranchToProto(&postgres.PostgresBranch{PostgresName: "orders", Name: tt.branch}) + if got.Name != tt.want { + t.Errorf("database name = %q, want %q", got.Name, tt.want) + } + }) + } +} diff --git a/internal/grpc/grpcdatabase/server.go b/internal/grpc/grpcdatabase/server.go index c70509d92..86e70b4a8 100644 --- a/internal/grpc/grpcdatabase/server.go +++ b/internal/grpc/grpcdatabase/server.go @@ -14,28 +14,28 @@ import ( ) type Server struct { - sqlDatabaseWatcher *watchers.SqlDatabaseWatcher - zalandoPostgresWatcher *watchers.ZalandoPostgresWatcher + sqlDatabaseWatcher *watchers.SqlDatabaseWatcher + postgresBranchWatcher *watchers.PostgresBranchWatcher protoapi.UnimplementedDatabasesServer } -func NewServer(sqlDatabaseWatcher *watchers.SqlDatabaseWatcher, zalandoPostgresWatcher *watchers.ZalandoPostgresWatcher) *Server { +func NewServer(sqlDatabaseWatcher *watchers.SqlDatabaseWatcher, postgresBranchWatcher *watchers.PostgresBranchWatcher) *Server { return &Server{ - sqlDatabaseWatcher: sqlDatabaseWatcher, - zalandoPostgresWatcher: zalandoPostgresWatcher, + sqlDatabaseWatcher: sqlDatabaseWatcher, + postgresBranchWatcher: postgresBranchWatcher, } } func (s *Server) List(_ context.Context, r *protoapi.ListDatabasesRequest) (*protoapi.ListDatabasesResponse, error) { sqlDatabases := watcher.Objects(s.sqlDatabaseWatcher.GetByNamespace(r.TeamSlug)) - postgresInstances := watcher.Objects(s.zalandoPostgresWatcher.GetByNamespace(r.TeamSlug)) + postgresBranches := watcher.Objects(s.postgresBranchWatcher.GetByNamespace(r.TeamSlug)) - all := make([]*protoapi.Database, 0, len(sqlDatabases)+len(postgresInstances)) + all := make([]*protoapi.Database, 0, len(sqlDatabases)+len(postgresBranches)) for _, d := range sqlDatabases { all = append(all, sqlDatabaseToProto(d)) } - for _, p := range postgresInstances { - all = append(all, postgresInstanceToProto(p)) + for _, p := range postgresBranches { + all = append(all, postgresBranchToProto(p)) } // Sort by (type, environment, name, database) for deterministic pagination. @@ -77,12 +77,16 @@ func sqlDatabaseToProto(d *sqlinstance.SQLDatabase) *protoapi.Database { } } -func postgresInstanceToProto(p *postgres.PostgresInstance) *protoapi.Database { +func postgresBranchToProto(p *postgres.PostgresBranch) *protoapi.Database { + name := p.PostgresName + if p.Name != "main" { + name += "/" + p.Name + } return &protoapi.Database{ - Name: p.Name, + Name: name, Database: "app", Environment: p.EnvironmentName, TeamSlug: p.TeamSlug.String(), - Type: protoapi.DatabaseType_ZALANDO_POSTGRES, + Type: protoapi.DatabaseType_NAIS_POSTGRES, } } diff --git a/internal/grpc/grpcdatabase/server_test.go b/internal/grpc/grpcdatabase/server_test.go index ffd895629..2c075af51 100644 --- a/internal/grpc/grpcdatabase/server_test.go +++ b/internal/grpc/grpcdatabase/server_test.go @@ -28,8 +28,8 @@ func TestDatabasesServer_List(t *testing.T) { // CLOUD_SQL dev-gcp instance-a db-a // CLOUD_SQL dev-gcp instance-a db-b // CLOUD_SQL prod-gcp instance-a db-a - // ZALANDO_POSTGRES dev-gcp pg-a app - // ZALANDO_POSTGRES dev-gcp pg-b app + // NAIS_POSTGRES dev-gcp pg-a app + // NAIS_POSTGRES dev-gcp pg-b app // // Fixtures live under testdata/ — the fake client harness // (internal/kubernetes/fake) sets the object namespace from the parent @@ -78,8 +78,8 @@ func TestDatabasesServer_List(t *testing.T) { {typ: protoapi.DatabaseType_CLOUD_SQL, environment: "dev-gcp", name: "instance-a", database: "db-a"}, {typ: protoapi.DatabaseType_CLOUD_SQL, environment: "dev-gcp", name: "instance-a", database: "db-b"}, {typ: protoapi.DatabaseType_CLOUD_SQL, environment: "prod-gcp", name: "instance-a", database: "db-a"}, - {typ: protoapi.DatabaseType_ZALANDO_POSTGRES, environment: "dev-gcp", name: "pg-a", database: "app"}, - {typ: protoapi.DatabaseType_ZALANDO_POSTGRES, environment: "dev-gcp", name: "pg-b", database: "app"}, + {typ: protoapi.DatabaseType_NAIS_POSTGRES, environment: "dev-gcp", name: "pg-a", database: "app"}, + {typ: protoapi.DatabaseType_NAIS_POSTGRES, environment: "dev-gcp", name: "pg-b", database: "app"}, } // Repeated calls must return the same order. @@ -203,7 +203,7 @@ func newServer(t *testing.T, ctx context.Context) *grpcdatabase.Server { t.Cleanup(mgr.Stop) sqlDatabaseWatcher := sqlinstance.NewDatabaseWatcher(ctx, mgr) - zalandoPostgresWatcher := postgres.NewZalandoPostgresWatcher(ctx, mgr) + postgresBranchWatcher := postgres.NewPostgresBranchWatcher(ctx, mgr) ctxWait, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() @@ -213,6 +213,6 @@ func newServer(t *testing.T, ctx context.Context) *grpcdatabase.Server { return grpcdatabase.NewServer( (*watchers.SqlDatabaseWatcher)(sqlDatabaseWatcher), - (*watchers.ZalandoPostgresWatcher)(zalandoPostgresWatcher), + (*watchers.PostgresBranchWatcher)(postgresBranchWatcher), ) } diff --git a/internal/grpc/grpcdatabase/testdata/dev-gcp/myteam/postgres.yaml b/internal/grpc/grpcdatabase/testdata/dev-gcp/myteam/postgres.yaml index ea34f1c0a..447d43d4e 100644 --- a/internal/grpc/grpcdatabase/testdata/dev-gcp/myteam/postgres.yaml +++ b/internal/grpc/grpcdatabase/testdata/dev-gcp/myteam/postgres.yaml @@ -1,23 +1,54 @@ -apiVersion: data.nais.io/v1 +--- +apiVersion: nais.io/v1 kind: Postgres metadata: name: pg-b + namespace: myteam +spec: + majorVersion: "17" +status: + activeBranch: main +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: pg-b-main-4a8c4391 + namespace: myteam spec: - cluster: - majorVersion: "17" - resources: - diskSize: "2Gi" - cpu: "100m" - memory: "1Gi" + postgres: pg-b + branchName: main +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" --- -apiVersion: data.nais.io/v1 +apiVersion: nais.io/v1 kind: Postgres metadata: name: pg-a + namespace: myteam +spec: + majorVersion: "17" +status: + activeBranch: main +--- +apiVersion: nais.io/v1 +kind: PostgresBranch +metadata: + name: pg-a-main-fb72f9ba + namespace: myteam spec: - cluster: - majorVersion: "17" - resources: - diskSize: "2Gi" - cpu: "100m" - memory: "1Gi" + postgres: pg-a + branchName: main +status: + reconcilePhase: Completed + conditions: + - type: cluster.postgresql.cnpg.io/ObservedState + status: "True" + reason: Reconciled + message: "Cluster is in phase: Cluster in healthy state" + lastTransitionTime: "2026-09-17T00:00:00Z" diff --git a/internal/kubernetes/fake/fake.go b/internal/kubernetes/fake/fake.go index bb0e3848a..e71300572 100644 --- a/internal/kubernetes/fake/fake.go +++ b/internal/kubernetes/fake/fake.go @@ -15,7 +15,6 @@ import ( "github.com/nais/api/internal/kubernetes/watcher" liberator_aiven_io_v1alpha1 "github.com/nais/liberator/pkg/apis/aiven.io/v1alpha1" nais_io_v1alpha1 "github.com/nais/liberator/pkg/apis/nais.io/v1alpha1" - data_nais_io_v1 "github.com/nais/pgrator/pkg/api/datav1" mapperatorv1 "github.com/nais/pgrator/pkg/api/v1" unleash_nais_io_v1 "github.com/nais/unleasherator/api/v1" "k8s.io/apimachinery/pkg/api/meta" @@ -192,6 +191,8 @@ func depluralized(s string) string { return "remoteunleashes" case "postgreses": return "postgres" + case "postgresbranchs": + return "postgresbranches" } return s @@ -216,7 +217,9 @@ func NewDynamicClient(scheme *runtime.Scheme) *dynfake.FakeDynamicClient { liberator_aiven_io_v1alpha1.GroupVersion.WithResource("opensearches"): "OpenSearchList", unleash_nais_io_v1.GroupVersion.WithResource("unleashes"): "UnleashList", unleash_nais_io_v1.GroupVersion.WithResource("remoteunleashes"): "RemoteUnleashList", - data_nais_io_v1.GroupVersion.WithResource("postgres"): "PostgresList", + mapperatorv1.GroupVersion.WithResource("postgres"): "PostgresList", + {Group: "data.nais.io", Version: "v1", Resource: "postgres"}: "PostgresList", + mapperatorv1.GroupVersion.WithResource("postgresbranches"): "PostgresBranchList", nais_io_v1alpha1.GroupVersion.WithResource("tunnels"): "TunnelList", mapperatorv1.GroupVersion.WithResource("valkeys"): "ValkeyList", mapperatorv1.GroupVersion.WithResource("opensearches"): "OpenSearchList", diff --git a/internal/kubernetes/fake/postgres_fixtures_test.go b/internal/kubernetes/fake/postgres_fixtures_test.go new file mode 100644 index 000000000..7352b72bb --- /dev/null +++ b/internal/kubernetes/fake/postgres_fixtures_test.go @@ -0,0 +1,56 @@ +package fake_test + +import ( + "os" + "testing" + + "github.com/nais/api/internal/kubernetes" + "github.com/nais/api/internal/kubernetes/fake" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func TestPostgresBranchKindResolver(t *testing.T) { + _, kinds, _, err := fake.Clients(nil)("dev") + if err != nil { + t.Fatal(err) + } + resolved, err := kinds.KindsFor(schema.GroupVersionResource{Group: "nais.io", Version: "v1", Resource: "postgresbranches"}) + if err != nil || len(resolved) != 1 || resolved[0].Kind != "PostgresBranch" { + guessed, _ := meta.UnsafeGuessKindToResource(schema.GroupVersion{Group: "nais.io", Version: "v1"}.WithKind("PostgresBranch")) + t.Fatalf("resolving PostgresBranch resource: %v, %v (guessed %s)", resolved, err, guessed.Resource) + } +} + +func TestPostgresFixturesUseRegisteredV1Kinds(t *testing.T) { + scheme, err := kubernetes.NewScheme() + if err != nil { + t.Fatal(err) + } + // Each directory is a separate integration suite; fixtures in different + // suites may intentionally describe the same resource. + for _, path := range []string{ + "../../../integration_tests/k8s_resources/create_postgres_access", + "../../../integration_tests/k8s_resources/postgres_branches", + "../../../integration_tests/k8s_resources/postgres_workloads", + "../../../integration_tests/k8s_resources/postgres_branch_delete", + "../../../integration_tests/k8s_resources/postgres_audit_log", + "../../../integration_tests/k8s_resources/label_selectors", + "../../../data/k8s", + } { + resources, err := fake.ParseResources(scheme, os.DirFS(path)) + if err != nil { + t.Fatalf("%s: %v", path, err) + } + if len(resources) == 0 { + t.Errorf("%s: no fixtures", path) + } + // Parsing alone does not detect two files declaring the same resource. + // One fake tracker per cluster and suite catches duplicate identities + // across all files belonging to that cluster, as in the Lua runner. + for _, objects := range resources { + client := fake.NewDynamicClient(scheme) + fake.AddObjectToDynamicClient(scheme, client, objects...) + } + } +} diff --git a/internal/kubernetes/scheme.go b/internal/kubernetes/scheme.go index 60e0a63e2..f1d2662df 100644 --- a/internal/kubernetes/scheme.go +++ b/internal/kubernetes/scheme.go @@ -9,7 +9,6 @@ import ( kafka_nais_io_v1 "github.com/nais/liberator/pkg/apis/kafka.nais.io/v1" nais_io_v1 "github.com/nais/liberator/pkg/apis/nais.io/v1" nais_io_v1alpha1 "github.com/nais/liberator/pkg/apis/nais.io/v1alpha1" - data_nais_io_v1 "github.com/nais/pgrator/pkg/api/datav1" mapperatorv1 "github.com/nais/pgrator/pkg/api/v1" unleash_nais_io_v1 "github.com/nais/unleasherator/api/v1" appsv1 "k8s.io/api/apps/v1" @@ -18,7 +17,9 @@ import ( corev1 "k8s.io/api/core/v1" netv1 "k8s.io/api/networking/v1" rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" ) func NewScheme() (*runtime.Scheme, error) { @@ -39,7 +40,6 @@ func NewScheme() (*runtime.Scheme, error) { unleash_nais_io_v1.AddToScheme, batchv1.AddToScheme, aiven_nais_io_v1.AddToScheme, - data_nais_io_v1.AddToScheme, authorizationv1.AddToScheme, mapperatorv1.AddToScheme, } @@ -50,5 +50,18 @@ func NewScheme() (*runtime.Scheme, error) { } } + // The API reads these CRDs through dynamic clients without importing their + // controller implementations. Register their GVKs for local fake clients. + for _, gv := range []struct { + group, version, kind string + }{ + {"nais.io", "v1alpha1", "RelayAccess"}, + {"postgresql.cnpg.io", "v1", "Cluster"}, + {"data.nais.io", "v1", "Postgres"}, // Legacy port-forward grant validation. + } { + version := schema.GroupVersion{Group: gv.group, Version: gv.version} + scheme.AddKnownTypeWithName(version.WithKind(gv.kind), &unstructured.Unstructured{}) + scheme.AddKnownTypeWithName(version.WithKind(gv.kind+"List"), &unstructured.UnstructuredList{}) + } return scheme, nil } diff --git a/internal/kubernetes/watchers/watchers.go b/internal/kubernetes/watchers/watchers.go index e1655ee26..2bae5f017 100644 --- a/internal/kubernetes/watchers/watchers.go +++ b/internal/kubernetes/watchers/watchers.go @@ -29,51 +29,51 @@ import ( ) type ( - AppWatcher = watcher.Watcher[*nais_io_v1alpha1.Application] - JobWatcher = watcher.Watcher[*nais_io_v1.Naisjob] - RunWatcher = watcher.Watcher[*batchv1.Job] - BqWatcher = watcher.Watcher[*bigquery.BigQueryDataset] - ValkeyWatcher = watcher.Watcher[*valkey.Valkey] - OpenSearchWatcher = watcher.Watcher[*opensearch.OpenSearch] - NaisOpenSearchWatcher = watcher.Watcher[*opensearch.OpenSearch] - BucketWatcher = watcher.Watcher[*bucket.Bucket] - SqlDatabaseWatcher = watcher.Watcher[*sqlinstance.SQLDatabase] - SqlInstanceWatcher = watcher.Watcher[*sqlinstance.SQLInstance] - ZalandoPostgresWatcher = watcher.Watcher[*postgres.PostgresInstance] - KafkaTopicWatcher = watcher.Watcher[*kafkatopic.KafkaTopic] - PodWatcher = watcher.Watcher[*v1.Pod] - IngressWatcher = watcher.Watcher[*netv1.Ingress] - NamespaceWatcher = watcher.Watcher[*v1.Namespace] - UnleashWatcher = watcher.Watcher[*unleash.UnleashInstance] - SecretWatcher = watcher.Watcher[*secret.Secret] - ConfigWatcher = watcher.Watcher[*config.Config] - ReplicaSetWatcher = watcher.Watcher[*appsv1.ReplicaSet] - TunnelWatcher = watcher.Watcher[*tunnel.Tunnel] - NaisValkeyWatcher = watcher.Watcher[*valkey.Valkey] + AppWatcher = watcher.Watcher[*nais_io_v1alpha1.Application] + JobWatcher = watcher.Watcher[*nais_io_v1.Naisjob] + RunWatcher = watcher.Watcher[*batchv1.Job] + BqWatcher = watcher.Watcher[*bigquery.BigQueryDataset] + ValkeyWatcher = watcher.Watcher[*valkey.Valkey] + OpenSearchWatcher = watcher.Watcher[*opensearch.OpenSearch] + NaisOpenSearchWatcher = watcher.Watcher[*opensearch.OpenSearch] + BucketWatcher = watcher.Watcher[*bucket.Bucket] + SqlDatabaseWatcher = watcher.Watcher[*sqlinstance.SQLDatabase] + SqlInstanceWatcher = watcher.Watcher[*sqlinstance.SQLInstance] + PostgresBranchWatcher = watcher.Watcher[*postgres.PostgresBranch] + KafkaTopicWatcher = watcher.Watcher[*kafkatopic.KafkaTopic] + PodWatcher = watcher.Watcher[*v1.Pod] + IngressWatcher = watcher.Watcher[*netv1.Ingress] + NamespaceWatcher = watcher.Watcher[*v1.Namespace] + UnleashWatcher = watcher.Watcher[*unleash.UnleashInstance] + SecretWatcher = watcher.Watcher[*secret.Secret] + ConfigWatcher = watcher.Watcher[*config.Config] + ReplicaSetWatcher = watcher.Watcher[*appsv1.ReplicaSet] + TunnelWatcher = watcher.Watcher[*tunnel.Tunnel] + NaisValkeyWatcher = watcher.Watcher[*valkey.Valkey] ) type Watchers struct { - AppWatcher *AppWatcher - JobWatcher *JobWatcher - RunWatcher *RunWatcher - BqWatcher *BqWatcher - ValkeyWatcher *ValkeyWatcher - OpenSearchWatcher *OpenSearchWatcher - NaisOpenSearchWatcher *NaisOpenSearchWatcher - BucketWatcher *BucketWatcher - SqlDatabaseWatcher *SqlDatabaseWatcher - SqlInstanceWatcher *SqlInstanceWatcher - ZalandoPostgresWatcher *ZalandoPostgresWatcher - KafkaTopicWatcher *KafkaTopicWatcher - PodWatcher *PodWatcher - IngressWatcher *IngressWatcher - NamespaceWatcher *NamespaceWatcher - UnleashWatcher *UnleashWatcher - SecretWatcher *SecretWatcher - ConfigWatcher *ConfigWatcher - ReplicaSetWatcher *ReplicaSetWatcher - TunnelWatcher *TunnelWatcher - NaisValkeyWatcher *NaisValkeyWatcher + AppWatcher *AppWatcher + JobWatcher *JobWatcher + RunWatcher *RunWatcher + BqWatcher *BqWatcher + ValkeyWatcher *ValkeyWatcher + OpenSearchWatcher *OpenSearchWatcher + NaisOpenSearchWatcher *NaisOpenSearchWatcher + BucketWatcher *BucketWatcher + SqlDatabaseWatcher *SqlDatabaseWatcher + SqlInstanceWatcher *SqlInstanceWatcher + PostgresBranchWatcher *PostgresBranchWatcher + KafkaTopicWatcher *KafkaTopicWatcher + PodWatcher *PodWatcher + IngressWatcher *IngressWatcher + NamespaceWatcher *NamespaceWatcher + UnleashWatcher *UnleashWatcher + SecretWatcher *SecretWatcher + ConfigWatcher *ConfigWatcher + ReplicaSetWatcher *ReplicaSetWatcher + TunnelWatcher *TunnelWatcher + NaisValkeyWatcher *NaisValkeyWatcher } func SetupWatchers( @@ -83,26 +83,26 @@ func SetupWatchers( unleashEnabled bool, ) *Watchers { ret := &Watchers{ - AppWatcher: application.NewWatcher(ctx, watcherMgr), - JobWatcher: job.NewWatcher(ctx, watcherMgr), - RunWatcher: job.NewRunWatcher(ctx, watcherMgr), - BqWatcher: bigquery.NewWatcher(ctx, watcherMgr), - ValkeyWatcher: valkey.NewWatcher(ctx, watcherMgr), - OpenSearchWatcher: opensearch.NewWatcher(ctx, watcherMgr), - NaisOpenSearchWatcher: opensearch.NewNaisOpenSearchWatcher(ctx, watcherMgr), - BucketWatcher: bucket.NewWatcher(ctx, watcherMgr), - SqlDatabaseWatcher: sqlinstance.NewDatabaseWatcher(ctx, watcherMgr), - SqlInstanceWatcher: sqlinstance.NewInstanceWatcher(ctx, watcherMgr), - ZalandoPostgresWatcher: postgres.NewZalandoPostgresWatcher(ctx, watcherMgr), - KafkaTopicWatcher: kafkatopic.NewWatcher(ctx, watcherMgr), - PodWatcher: workload.NewWatcher(ctx, watcherMgr), - IngressWatcher: application.NewIngressWatcher(ctx, watcherMgr), - NamespaceWatcher: team.NewNamespaceWatcher(ctx, watcherMgr), - SecretWatcher: secret.NewWatcher(ctx, watcherMgr), - ConfigWatcher: config.NewWatcher(ctx, watcherMgr), - ReplicaSetWatcher: instancegroup.NewWatcher(ctx, watcherMgr), - TunnelWatcher: tunnel.NewWatcher(ctx, watcherMgr), - NaisValkeyWatcher: valkey.NewNaisValkeyWatcher(ctx, watcherMgr), + AppWatcher: application.NewWatcher(ctx, watcherMgr), + JobWatcher: job.NewWatcher(ctx, watcherMgr), + RunWatcher: job.NewRunWatcher(ctx, watcherMgr), + BqWatcher: bigquery.NewWatcher(ctx, watcherMgr), + ValkeyWatcher: valkey.NewWatcher(ctx, watcherMgr), + OpenSearchWatcher: opensearch.NewWatcher(ctx, watcherMgr), + NaisOpenSearchWatcher: opensearch.NewNaisOpenSearchWatcher(ctx, watcherMgr), + BucketWatcher: bucket.NewWatcher(ctx, watcherMgr), + SqlDatabaseWatcher: sqlinstance.NewDatabaseWatcher(ctx, watcherMgr), + SqlInstanceWatcher: sqlinstance.NewInstanceWatcher(ctx, watcherMgr), + PostgresBranchWatcher: postgres.NewPostgresBranchWatcher(ctx, watcherMgr), + KafkaTopicWatcher: kafkatopic.NewWatcher(ctx, watcherMgr), + PodWatcher: workload.NewWatcher(ctx, watcherMgr), + IngressWatcher: application.NewIngressWatcher(ctx, watcherMgr), + NamespaceWatcher: team.NewNamespaceWatcher(ctx, watcherMgr), + SecretWatcher: secret.NewWatcher(ctx, watcherMgr), + ConfigWatcher: config.NewWatcher(ctx, watcherMgr), + ReplicaSetWatcher: instancegroup.NewWatcher(ctx, watcherMgr), + TunnelWatcher: tunnel.NewWatcher(ctx, watcherMgr), + NaisValkeyWatcher: valkey.NewNaisValkeyWatcher(ctx, watcherMgr), } if unleashEnabled { ret.UnleashWatcher = unleash.NewWatcher(ctx, mgmtWatcherMgr) diff --git a/internal/persistence/bigquery/models.go b/internal/persistence/bigquery/models.go index e9637a511..22f35ca9e 100644 --- a/internal/persistence/bigquery/models.go +++ b/internal/persistence/bigquery/models.go @@ -52,7 +52,6 @@ type BigQueryDataset struct { TeamSlug slug.Slug `json:"-"` EnvironmentName string `json:"-"` WorkloadReference *workload.Reference `json:"-"` - ProjectID string `json:"-"` K8sResourceName string `json:"-"` } @@ -195,7 +194,6 @@ func toBigQueryDataset(u *unstructured.Unstructured, environmentName string) (*B TeamSlug: slug.Slug(obj.GetNamespace()), EnvironmentName: environmentName, WorkloadReference: workload.ReferenceFromOwnerReferences(obj.GetOwnerReferences()), - ProjectID: obj.Spec.Project, } if obj.Spec.Description != "" { diff --git a/internal/persistence/postgres/activitylog.go b/internal/persistence/postgres/activitylog.go index 44371b3b6..1c6999c74 100644 --- a/internal/persistence/postgres/activitylog.go +++ b/internal/persistence/postgres/activitylog.go @@ -8,7 +8,9 @@ import ( ) const ( - activityLogEntryActionGrantAccess activitylog.ActivityLogEntryAction = "GRANT_ACCESS" + activityLogEntryActionGrantAccess activitylog.ActivityLogEntryAction = "GRANT_ACCESS" + activityLogEntryActionCreatePersonalAccess activitylog.ActivityLogEntryAction = "CREATE_PERSONAL_ACCESS" + activityLogEntryActionGetPersonalAccessConnection activitylog.ActivityLogEntryAction = "GET_PERSONAL_ACCESS_CONNECTION" activityLogEntryResourceTypePostgres activitylog.ActivityLogEntryResourceType = "POSTGRES" ) @@ -35,12 +37,31 @@ func init() { GenericActivityLogEntry: entry.WithMessage(fmt.Sprintf("Granted access to %s until %s", data.Grantee, data.Until)), Data: data, }, nil + case activityLogEntryActionCreatePersonalAccess: + data, err := activitylog.UnmarshalData[PostgresPersonalAccessCreatedActivityLogEntryData](entry) + if err != nil { + return nil, fmt.Errorf("transforming postgres personal access activity log entry data: %w", err) + } + message := fmt.Sprintf("Created personal Postgres access for %s until %s", data.Username, data.ExpiresAt) + if data.AccessLevel != nil { + message = fmt.Sprintf("Requested %s personal Postgres access for %s until %s", *data.AccessLevel, data.Username, data.ExpiresAt) + } + return PostgresPersonalAccessCreatedActivityLogEntry{ + GenericActivityLogEntry: entry.WithMessage(message), + Data: data, + }, nil + case activityLogEntryActionGetPersonalAccessConnection: + return PostgresPersonalAccessConnectionActivityLogEntry{ + GenericActivityLogEntry: entry.WithMessage("Retrieved personal Postgres connection materials"), + }, nil default: return nil, fmt.Errorf("unsupported postgres activity log entry action: %q", entry.Action) } }) activitylog.RegisterFilter("POSTGRES_GRANT_ACCESS", activityLogEntryActionGrantAccess, activityLogEntryResourceTypePostgres) + activitylog.RegisterFilter("POSTGRES_PERSONAL_ACCESS_CREATED", activityLogEntryActionCreatePersonalAccess, activityLogEntryResourceTypePostgres) + activitylog.RegisterFilter("POSTGRES_PERSONAL_ACCESS_CONNECTION", activityLogEntryActionGetPersonalAccessConnection, activityLogEntryResourceTypePostgres) activitylog.RegisterFilter("POSTGRES_DELETED", activitylog.ActivityLogEntryActionDeleted, activityLogEntryResourceTypePostgres) } @@ -58,3 +79,22 @@ type PostgresGrantAccessActivityLogEntryData struct { Grantee string `json:"grantee,string"` Until time.Time `json:"until"` } + +type PostgresPersonalAccessCreatedActivityLogEntry struct { + activitylog.GenericActivityLogEntry + + Data *PostgresPersonalAccessCreatedActivityLogEntryData `json:"data"` +} + +type PostgresPersonalAccessCreatedActivityLogEntryData struct { + Username string `json:"username"` + AccessLevel *PostgresAccessLevel `json:"accessLevel,omitempty"` + ExpiresAt time.Time `json:"expiresAt"` + Reason string `json:"reason"` +} + +type PostgresPersonalAccessConnectionActivityLogEntry struct { + activitylog.GenericActivityLogEntry +} + +type PostgresPersonalAccessConnectionActivityLogEntryData struct{} diff --git a/internal/persistence/postgres/connection.go b/internal/persistence/postgres/connection.go new file mode 100644 index 000000000..ef508c3f4 --- /dev/null +++ b/internal/persistence/postgres/connection.go @@ -0,0 +1,114 @@ +package postgres + +import ( + "context" + "fmt" + "strings" + + "github.com/nais/api/internal/graph/apierror" + "github.com/nais/api/internal/kubernetes/watcher" + corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// All connection resources are read on demand after the caller has been +// authorized as the PostgresAccess owner. No credentials enter the watch cache. +func loadPostgresAccessConnection(ctx context.Context, access *unstructured.Unstructured, input PostgresAccessConnectionInput, connection *PostgresAccessConnectionDetails, tokenSecretName string) error { + if access.GetUID() == "" { + return apierror.Errorf("PostgresAccess %q is not ready", access.GetName()) + } + namespace := input.TeamSlug.String() + tokenSecret, err := getAccessResource(ctx, input.EnvironmentName, namespace, tokenSecretName, schema.GroupVersionResource{Version: "v1", Resource: "secrets"}) + if err != nil { + return err + } + token, err := accessSecretData(tokenSecret, access, "token") + if err != nil { + return err + } + // The broker creates short names (postgres-access-), so this is the + // pgrator-owned credential Secret for this access, not a client-supplied name. + credential, err := getAccessResource(ctx, input.EnvironmentName, namespace, access.GetName()+"-credentials", schema.GroupVersionResource{Version: "v1", Resource: "secrets"}) + if err != nil { + return err + } + password, err := accessSecretData(credential, access, corev1.BasicAuthPasswordKey) + if err != nil { + return err + } + username, err := accessSecretData(credential, access, corev1.BasicAuthUsernameKey) + if err != nil { + return err + } + role, _, err := unstructured.NestedString(access.Object, "status", "databaseRole") + if err != nil || role != username { + return apierror.Errorf("credentials for PostgresAccess %q are not available", access.GetName()) + } + + serverCA, _, err := unstructured.NestedString(access.Object, "status", "serverCASecret") + if err != nil || serverCA == "" { + return apierror.Errorf("PostgresAccess %q is not ready", access.GetName()) + } + caSecret, err := getAccessResource(ctx, input.EnvironmentName, namespace, serverCA, schema.GroupVersionResource{Version: "v1", Resource: "secrets"}) + if err != nil { + return err + } + var ca corev1.Secret + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(caSecret.Object, &ca); err != nil { + return fmt.Errorf("reading server CA for PostgresAccess %q: %w", access.GetName(), err) + } + if len(ca.Data["ca.crt"]) == 0 { + return apierror.Errorf("server CA for PostgresAccess %q is not available", access.GetName()) + } + + connection.Username = username + connection.Password = password + connection.CACertificate = string(ca.Data["ca.crt"]) + serverName, _, err := unstructured.NestedString(access.Object, "status", "serverName") + if err != nil || serverName == "" { + return apierror.Errorf("PostgresAccess %q is not ready", access.GetName()) + } + connection.ServerName = serverName + connection.RelayEndpoint = fmt.Sprintf("https://relay.external.%s.%s.cloud.nais.io:8443", input.EnvironmentName, fromContext(ctx).tenantName) + relayName, _, err := unstructured.NestedString(access.Object, "status", "relayAccess") + if err != nil || relayName == "" { + return apierror.Errorf("PostgresAccess %q is not ready", access.GetName()) + } + connection.RelayAccess = namespace + "/" + relayName + connection.RelayToken = token + return nil +} + +func accessSecretData(secret, access *unstructured.Unstructured, key string) (string, error) { + if !metav1.IsControlledBy(secret, access) || secret.GetDeletionTimestamp() != nil { + return "", apierror.Errorf("credentials for PostgresAccess %q are not available", access.GetName()) + } + var typed corev1.Secret + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(secret.Object, &typed); err != nil { + return "", fmt.Errorf("reading credentials for PostgresAccess %q: %w", access.GetName(), err) + } + value := string(typed.Data[key]) + if strings.TrimSpace(value) == "" { + return "", apierror.Errorf("credentials for PostgresAccess %q are incomplete", access.GetName()) + } + return value, nil +} + +func getAccessResource(ctx context.Context, environment, namespace, name string, gvr schema.GroupVersionResource) (*unstructured.Unstructured, error) { + client, err := fromContext(ctx).postgresBranchWatcher.SystemAuthenticatedClient(ctx, environment, watcher.WithImpersonatedClientGVR(gvr)) + if err != nil { + return nil, fmt.Errorf("creating %s client: %w", gvr.Resource, err) + } + resource, err := client.Namespace(namespace).Get(ctx, name, metav1.GetOptions{}) + if k8serrors.IsNotFound(err) { + return nil, apierror.Errorf("%s for PostgresAccess is not available", gvr.Resource) + } + if err != nil { + return nil, fmt.Errorf("getting %s for PostgresAccess: %w", gvr.Resource, err) + } + return resource, nil +} diff --git a/internal/persistence/postgres/connection_test.go b/internal/persistence/postgres/connection_test.go new file mode 100644 index 000000000..dc24632be --- /dev/null +++ b/internal/persistence/postgres/connection_test.go @@ -0,0 +1,48 @@ +package postgres + +import ( + "strings" + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" +) + +func TestAccessSecretDataRequiresAccessOwnership(t *testing.T) { + access := &unstructured.Unstructured{} + access.SetName("personal-access") + access.SetUID(types.UID("original-access")) + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "personal-access-relay-token", + OwnerReferences: []metav1.OwnerReference{{ + APIVersion: "nais.io/v1", Kind: "PostgresAccess", Name: "personal-access", + UID: types.UID("original-access"), Controller: new(true), + }}, + }, + Data: map[string][]byte{"token": []byte("private-proof")}, + } + check := func() (string, error) { + t.Helper() + obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(secret) + if err != nil { + t.Fatal(err) + } + return accessSecretData(&unstructured.Unstructured{Object: obj}, access, "token") + } + if got, err := check(); err != nil || got != "private-proof" { + t.Fatalf("owned token = %q, %v", got, err) + } + secret.OwnerReferences[0].UID = types.UID("replaced-access") + if _, err := check(); err == nil || !strings.Contains(err.Error(), "not available") { + t.Fatalf("replaced owner error = %v", err) + } + secret.OwnerReferences[0].UID = access.GetUID() + delete(secret.Data, "token") + if _, err := check(); err == nil || !strings.Contains(err.Error(), "incomplete") { + t.Fatalf("missing token error = %v", err) + } +} diff --git a/internal/persistence/postgres/dataloader.go b/internal/persistence/postgres/dataloader.go index 0282dcb72..3e9845d0c 100644 --- a/internal/persistence/postgres/dataloader.go +++ b/internal/persistence/postgres/dataloader.go @@ -6,6 +6,7 @@ import ( "github.com/nais/api/internal/kubernetes/watcher" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" ) type ctxKey int @@ -14,28 +15,36 @@ const loadersKey ctxKey = iota func NewLoaderContext( ctx context.Context, - zalandoPostgresWatcher *watcher.Watcher[*PostgresInstance], + postgresBranchWatcher *watcher.Watcher[*PostgresBranch], auditLogProjectID string, auditLogLocation string, + tenantName string, + clients map[string]dynamic.Interface, ) context.Context { - return context.WithValue(ctx, loadersKey, newLoaders(zalandoPostgresWatcher, auditLogProjectID, auditLogLocation)) + return context.WithValue(ctx, loadersKey, newLoaders(postgresBranchWatcher, auditLogProjectID, auditLogLocation, tenantName, clients)) } type loaders struct { - zalandoPostgresWatcher *watcher.Watcher[*PostgresInstance] - auditLogProjectID string - auditLogLocation string + postgresBranchWatcher *watcher.Watcher[*PostgresBranch] + auditLogProjectID string + auditLogLocation string + tenantName string + clients map[string]dynamic.Interface } func newLoaders( - zalandoPostgresWatcher *watcher.Watcher[*PostgresInstance], + postgresBranchWatcher *watcher.Watcher[*PostgresBranch], auditLogProjectID string, auditLogLocation string, + tenantName string, + clients map[string]dynamic.Interface, ) *loaders { return &loaders{ - zalandoPostgresWatcher: zalandoPostgresWatcher, - auditLogProjectID: auditLogProjectID, - auditLogLocation: auditLogLocation, + postgresBranchWatcher: postgresBranchWatcher, + auditLogProjectID: auditLogProjectID, + auditLogLocation: auditLogLocation, + tenantName: tenantName, + clients: clients, } } @@ -45,17 +54,17 @@ func GetAuditLogConfig(ctx context.Context) (projectID, location string) { return loaders.auditLogProjectID, loaders.auditLogLocation } -func NewZalandoPostgresWatcher(ctx context.Context, mgr *watcher.Manager) *watcher.Watcher[*PostgresInstance] { - w := watcher.Watch(mgr, &PostgresInstance{}, watcher.WithConverter(func(o *unstructured.Unstructured, environmentName string) (obj any, ok bool) { - ret, err := toPostgres(o, environmentName) +func NewPostgresBranchWatcher(ctx context.Context, mgr *watcher.Manager) *watcher.Watcher[*PostgresBranch] { + w := watcher.Watch(mgr, &PostgresBranch{}, watcher.WithConverter(func(o *unstructured.Unstructured, environmentName string) (obj any, ok bool) { + ret, err := toPostgresBranch(o, environmentName) if err != nil { return nil, false } return ret, true }), watcher.WithGVR(schema.GroupVersionResource{ - Group: "data.nais.io", + Group: "nais.io", Version: "v1", - Resource: "postgres", + Resource: "postgresbranches", })) w.Start(ctx) return w diff --git a/internal/persistence/postgres/delete_test.go b/internal/persistence/postgres/delete_test.go new file mode 100644 index 000000000..2d70b8c77 --- /dev/null +++ b/internal/persistence/postgres/delete_test.go @@ -0,0 +1,186 @@ +package postgres + +import ( + "context" + "errors" + "os" + "strings" + "testing" + "time" + + "github.com/nais/api/internal/kubernetes" + "github.com/nais/api/internal/kubernetes/fake" + "github.com/nais/api/internal/kubernetes/watcher" + "github.com/nais/api/internal/slug" + "github.com/nais/api/internal/workload/application" + "github.com/nais/api/internal/workload/job" + "github.com/sirupsen/logrus/hooks/test" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func TestDeletePostgresBranchRequiresInactiveBranch(t *testing.T) { + tests := []struct { + name string + postgres map[string]any + wantDenied bool + }{ + {"selected in status", map[string]any{"status": map[string]any{"activeBranch": "restored"}}, true}, + {"selected in spec", map[string]any{"spec": map[string]any{"activeBranch": "restored"}}, true}, + {"pending switch", map[string]any{"spec": map[string]any{"activeBranch": "restored"}, "status": map[string]any{"activeBranch": "original"}}, true}, + {"inactive", map[string]any{"status": map[string]any{"activeBranch": "original"}}, false}, + {"default active", map[string]any{}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + postgres := &unstructured.Unstructured{Object: tt.postgres} + postgres.SetName("orders") + requested := "restored" + if tt.name == "default active" { + requested = "main" + } + err := ensureInstanceMayBeDeleted(requested, postgres) + if (err != nil) != tt.wantDenied { + t.Errorf("deletion error = %v; denied = %v", err, tt.wantDenied) + } + }) + } +} + +func TestWorkloadUsesMultiplePostgresResources(t *testing.T) { + scheme, err := kubernetes.NewScheme() + if err != nil { + t.Fatal(err) + } + clusters, err := kubernetes.CreateClusterConfigMap("nav", []string{"dev"}, nil) + if err != nil { + t.Fatal(err) + } + log, _ := test.NewNullLogger() + mgr, err := watcher.NewManager(scheme, clusters, log, watcher.WithClientCreator(fake.Clients(os.DirFS("../../../integration_tests/k8s_resources/postgres_workloads")))) + if err != nil { + t.Fatal(err) + } + t.Cleanup(mgr.Stop) + ctx := context.Background() + postgresBranchWatcher := NewPostgresBranchWatcher(ctx, mgr) + appWatcher := application.NewWatcher(ctx, mgr) + jobWatcher := job.NewWatcher(ctx, mgr) + wait, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + if !mgr.WaitForReady(wait) { + t.Fatal("watchers did not synchronize") + } + ctx = NewLoaderContext(ctx, postgresBranchWatcher, "", "", "nav", mgr.GetDynamicClients()) + ctx = application.NewLoaderContext(ctx, appWatcher, nil, log) + ctx = job.NewLoaderContext(ctx, jobWatcher, nil) + team := slug.Slug("postgres-workload-team") + apps := application.ListAllForTeamInEnvironment(ctx, team, "dev") + if len(apps) != 1 || apps[0].Spec.Uses == nil { + t.Fatalf("application uses not loaded: %+v", apps) + } + instances, err := ListForWorkload(ctx, team, "dev", apps[0].Spec.Uses.Postgres) + if err != nil || len(instances) != 2 || instances[0].Name != "green" || instances[1].Name != "recovered" { + t.Fatalf("selected instances = %+v, error = %v", instances, err) + } + jobs := job.ListAllForTeamInEnvironment(ctx, team, "dev") + if len(jobs) != 1 || jobs[0].Spec.Uses == nil { + t.Fatalf("job uses not loaded: %+v", jobs) + } + instances, err = ListForWorkload(ctx, team, "dev", jobs[0].Spec.Uses.Postgres) + if err != nil || len(instances) != 2 || instances[0].Name != "green" || instances[1].Name != "recovered" { + t.Fatalf("job selected instances = %+v, error = %v", instances, err) + } + for _, entry := range []struct{ postgres, branch string }{{"orders", "green"}, {"reports", "recovered"}} { + workloads := WorkloadsForInstance(ctx, team, "dev", entry.postgres, entry.branch) + if len(workloads) != 2 || workloads[0].GetName() != "consumer" || workloads[1].GetName() != "scheduled-reader" { + t.Errorf("workloads for %q = %+v", entry.branch, workloads) + } + } +} + +func TestReadyPostgresBranchUsesConcreteName(t *testing.T) { + scheme, err := kubernetes.NewScheme() + if err != nil { + t.Fatal(err) + } + clusters, err := kubernetes.CreateClusterConfigMap("nav", []string{"dev"}, nil) + if err != nil { + t.Fatal(err) + } + log, _ := test.NewNullLogger() + mgr, err := watcher.NewManager(scheme, clusters, log, watcher.WithClientCreator(fake.Clients(os.DirFS("../../../integration_tests/k8s_resources/create_postgres_access")))) + if err != nil { + t.Fatal(err) + } + t.Cleanup(mgr.Stop) + ctx := context.Background() + postgresBranchWatcher := NewPostgresBranchWatcher(ctx, mgr) + wait, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + if !mgr.WaitForReady(wait) { + t.Fatal("PostgresBranch watcher did not synchronize") + } + ctx = NewLoaderContext(ctx, postgresBranchWatcher, "", "", "nav", mgr.GetDynamicClients()) + team := slug.Slug("someteamname") + for _, name := range []string{"main", "recovered"} { + instance, err := GetReadyPostgresBranch(ctx, team, "dev", "foobar", name) + if err != nil || instance.State != PostgresBranchStateAvailable || instance.Name != name || instance.PostgresName != "foobar" { + t.Errorf("%s: got instance %+v, error %v", name, instance, err) + } + } + instance, err := GetReadyPostgresBranch(ctx, team, "dev", "progressing", "main") + if err != nil || instance.State == PostgresBranchStateAvailable { + t.Errorf("progressing instance = %+v, error %v", instance, err) + } + _, err = GetReadyPostgresBranch(ctx, team, "dev", "foobar", "missing") + if !errors.Is(err, &watcher.ErrorNotFound{}) || strings.Contains(err.Error(), "foobar-missing-") { + t.Errorf("missing instance error = %v; want local branch name only", err) + } + _, err = GetPostgresBranch(ctx, team, "dev", "progressing", "recovered") + if !errors.Is(err, &watcher.ErrorNotFound{}) { + t.Errorf("branch in wrong Postgres error = %v", err) + } + pg, err := GetPostgres(ctx, team, "dev", "foobar") + if err != nil { + t.Fatal(err) + } + branches := ListForPostgres(ctx, pg, nil, nil) + nodes := branches.Nodes() + if len(nodes) != 2 || nodes[0].Name != "main" || nodes[1].Name != "recovered" { + t.Errorf("Postgres branches = %+v", nodes) + } +} + +func TestCreatePostgresAccessRejectsMissingInstance(t *testing.T) { + scheme, err := kubernetes.NewScheme() + if err != nil { + t.Fatal(err) + } + clusters, err := kubernetes.CreateClusterConfigMap("nav", []string{"dev"}, nil) + if err != nil { + t.Fatal(err) + } + log, _ := test.NewNullLogger() + mgr, err := watcher.NewManager(scheme, clusters, log, watcher.WithClientCreator(fake.Clients(nil))) + if err != nil { + t.Fatal(err) + } + t.Cleanup(mgr.Stop) + ctx := context.Background() + postgresBranchWatcher := NewPostgresBranchWatcher(ctx, mgr) + wait, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + if !mgr.WaitForReady(wait) { + t.Fatal("PostgresBranch watcher did not synchronize") + } + ctx = NewLoaderContext(ctx, postgresBranchWatcher, "", "", "nav", mgr.GetDynamicClients()) + input := CreatePostgresAccessInput{ + Postgres: "foobar", Branch: "missing", TeamSlug: slug.Slug("myteam"), + EnvironmentName: "dev", AccessLevel: PostgresAccessLevelRead, + Reason: "Investigating missing instance", + } + err = input.Validate(ctx) + if err == nil || !strings.Contains(err.Error(), `Could not find PostgresBranch "missing"`) { + t.Errorf("validation error = %v, want named missing instance", err) + } +} diff --git a/internal/persistence/postgres/facets.go b/internal/persistence/postgres/facets.go index 797cad459..0ce32c5c4 100644 --- a/internal/persistence/postgres/facets.go +++ b/internal/persistence/postgres/facets.go @@ -9,24 +9,24 @@ import ( ) // Filtered returns the filtered Postgres instances, computing it exactly once per request. -func (f *PostgresInstanceFacets) Filtered(ctx context.Context) []*PostgresInstance { +func (f *PostgresBranchFacets) Filtered(ctx context.Context) []*PostgresBranch { f.filteredOnce.Do(func() { - f.filteredInstances = SortFilterPostgresInstance.Filter(ctx, f.AllInstances, f.Filter) + f.filteredInstances = SortFilterPostgresBranch.Filter(ctx, f.AllInstances, f.Filter) }) return f.filteredInstances } // Environments computes environments facets for a Postgres query. -func (f *PostgresInstanceFacets) Environments(ctx context.Context) []model.StringFacetItem { +func (f *PostgresBranchFacets) Environments(ctx context.Context) []model.StringFacetItem { filtered := f.Filtered(ctx) - return model.ComputeEnvironmentsFacet(f.AllInstances, filtered, func(inst *PostgresInstance) string { + return model.ComputeEnvironmentsFacet(f.AllInstances, filtered, func(inst *PostgresBranch) string { return inst.EnvironmentName }) } // States computes states facets for a Postgres query. -func (f *PostgresInstanceFacets) States(ctx context.Context) []PostgresInstanceStateFacetItem { - stateCounts := map[PostgresInstanceState]int{} +func (f *PostgresBranchFacets) States(ctx context.Context) []PostgresBranchStateFacetItem { + stateCounts := map[PostgresBranchState]int{} for _, inst := range f.AllInstances { stateCounts[inst.State] = 0 } @@ -36,72 +36,24 @@ func (f *PostgresInstanceFacets) States(ctx context.Context) []PostgresInstanceS stateCounts[inst.State]++ } - states := make([]PostgresInstanceStateFacetItem, 0, len(stateCounts)) + states := make([]PostgresBranchStateFacetItem, 0, len(stateCounts)) for state, count := range stateCounts { - states = append(states, PostgresInstanceStateFacetItem{ + states = append(states, PostgresBranchStateFacetItem{ State: state, Count: count, }) } - slices.SortFunc(states, func(a, b PostgresInstanceStateFacetItem) int { + slices.SortFunc(states, func(a, b PostgresBranchStateFacetItem) int { return strings.Compare(a.State.String(), b.State.String()) }) return states } -// HighAvailability computes high availability facets for a Postgres query. -func (f *PostgresInstanceFacets) HighAvailability(ctx context.Context) []model.BooleanFacetItem { - haCounts := map[bool]int{} - for _, inst := range f.AllInstances { - haCounts[inst.HighAvailability] = 0 - } - - filtered := f.Filtered(ctx) - for _, inst := range filtered { - haCounts[inst.HighAvailability]++ - } - - ha := make([]model.BooleanFacetItem, 0, len(haCounts)) - for val, count := range haCounts { - ha = append(ha, model.BooleanFacetItem{ - Value: val, - Count: count, - }) - } - model.SortBooleanFacetItems(ha) - - return ha -} - -// MajorVersions computes major version facets for a Postgres query. -func (f *PostgresInstanceFacets) MajorVersions(ctx context.Context) []model.StringFacetItem { - versionCounts := map[string]int{} - for _, inst := range f.AllInstances { - versionCounts[inst.MajorVersion] = 0 - } - - filtered := f.Filtered(ctx) - for _, inst := range filtered { - versionCounts[inst.MajorVersion]++ - } - - versions := make([]model.StringFacetItem, 0, len(versionCounts)) - for val, count := range versionCounts { - versions = append(versions, model.StringFacetItem{ - Value: val, - Count: count, - }) - } - model.SortStringFacetItems(versions) - - return versions -} - // Labels computes labels facets for a Postgres query. -func (f *PostgresInstanceFacets) Labels(ctx context.Context) []model.LabelFacetItem { +func (f *PostgresBranchFacets) Labels(ctx context.Context) []model.LabelFacetItem { filtered := f.Filtered(ctx) - return model.ComputeLabelsFacet(f.AllInstances, filtered, func(inst *PostgresInstance) []*model.ResourceLabel { + return model.ComputeLabelsFacet(f.AllInstances, filtered, func(inst *PostgresBranch) []*model.ResourceLabel { return inst.Labels }) } diff --git a/internal/persistence/postgres/facets_test.go b/internal/persistence/postgres/facets_test.go index 9859fd851..5db7cd727 100644 --- a/internal/persistence/postgres/facets_test.go +++ b/internal/persistence/postgres/facets_test.go @@ -6,234 +6,32 @@ import ( "testing" "github.com/nais/api/internal/graph/model" - "github.com/nais/api/internal/slug" ) func TestComputeFacets(t *testing.T) { - boolPtr := func(v bool) *bool { return &v } - - allInstances := []*PostgresInstance{ - { - Name: "app-db-1", - EnvironmentName: "dev", - TeamSlug: slug.Slug("my-team"), - MajorVersion: "15", - HighAvailability: false, - State: PostgresInstanceStateAvailable, - }, - { - Name: "app-db-2", - EnvironmentName: "dev", - TeamSlug: slug.Slug("my-team"), - MajorVersion: "16", - HighAvailability: true, - State: PostgresInstanceStateProgressing, - }, - { - Name: "app-db-3", - EnvironmentName: "prod", - TeamSlug: slug.Slug("my-team"), - MajorVersion: "15", - HighAvailability: true, - State: PostgresInstanceStateAvailable, - }, - { - Name: "app-db-4", - EnvironmentName: "prod", - TeamSlug: slug.Slug("my-team"), - MajorVersion: "17", - HighAvailability: false, - State: PostgresInstanceStateDegraded, - }, + all := []*PostgresBranch{ + {Name: "first", EnvironmentName: "dev", State: PostgresBranchStateAvailable}, + {Name: "second", EnvironmentName: "dev", State: PostgresBranchStateProgressing}, + {Name: "third", EnvironmentName: "prod", State: PostgresBranchStateDegraded}, } - tests := []struct { - name string - instances []*PostgresInstance - filter *PostgresInstanceFilter - wantEnvironments []model.StringFacetItem - wantStates []PostgresInstanceStateFacetItem - wantHA []model.BooleanFacetItem - wantMajorVersions []model.StringFacetItem + name string + filter *PostgresBranchFilter + wantEnvironments []model.StringFacetItem + wantStates []PostgresBranchStateFacetItem }{ - { - name: "no filter counts all instances", - instances: allInstances, - filter: nil, - wantEnvironments: []model.StringFacetItem{ - {Value: "dev", Count: 2}, - {Value: "prod", Count: 2}, - }, - wantStates: []PostgresInstanceStateFacetItem{ - {State: PostgresInstanceStateAvailable, Count: 2}, - {State: PostgresInstanceStateDegraded, Count: 1}, - {State: PostgresInstanceStateProgressing, Count: 1}, - }, - wantHA: []model.BooleanFacetItem{ - {Value: false, Count: 2}, - {Value: true, Count: 2}, - }, - wantMajorVersions: []model.StringFacetItem{ - {Value: "15", Count: 2}, - {Value: "16", Count: 1}, - {Value: "17", Count: 1}, - }, - }, - { - name: "filter by environment counts only matching but seeds all", - instances: allInstances, - filter: &PostgresInstanceFilter{Environments: []string{"dev"}}, - wantEnvironments: []model.StringFacetItem{ - {Value: "dev", Count: 2}, - {Value: "prod", Count: 0}, - }, - wantStates: []PostgresInstanceStateFacetItem{ - {State: PostgresInstanceStateAvailable, Count: 1}, - {State: PostgresInstanceStateDegraded, Count: 0}, - {State: PostgresInstanceStateProgressing, Count: 1}, - }, - wantHA: []model.BooleanFacetItem{ - {Value: false, Count: 1}, - {Value: true, Count: 1}, - }, - wantMajorVersions: []model.StringFacetItem{ - {Value: "15", Count: 1}, - {Value: "16", Count: 1}, - {Value: "17", Count: 0}, - }, - }, - { - name: "filter by state counts only matching state", - instances: allInstances, - filter: &PostgresInstanceFilter{States: []PostgresInstanceState{PostgresInstanceStateAvailable}}, - wantEnvironments: []model.StringFacetItem{ - {Value: "dev", Count: 1}, - {Value: "prod", Count: 1}, - }, - wantStates: []PostgresInstanceStateFacetItem{ - {State: PostgresInstanceStateAvailable, Count: 2}, - {State: PostgresInstanceStateDegraded, Count: 0}, - {State: PostgresInstanceStateProgressing, Count: 0}, - }, - wantHA: []model.BooleanFacetItem{ - {Value: false, Count: 1}, - {Value: true, Count: 1}, - }, - wantMajorVersions: []model.StringFacetItem{ - {Value: "15", Count: 2}, - {Value: "16", Count: 0}, - {Value: "17", Count: 0}, - }, - }, - { - name: "filter by high availability", - instances: allInstances, - filter: &PostgresInstanceFilter{HighAvailability: boolPtr(true)}, - wantEnvironments: []model.StringFacetItem{ - {Value: "dev", Count: 1}, - {Value: "prod", Count: 1}, - }, - wantStates: []PostgresInstanceStateFacetItem{ - {State: PostgresInstanceStateAvailable, Count: 1}, - {State: PostgresInstanceStateDegraded, Count: 0}, - {State: PostgresInstanceStateProgressing, Count: 1}, - }, - wantHA: []model.BooleanFacetItem{ - {Value: false, Count: 0}, - {Value: true, Count: 2}, - }, - wantMajorVersions: []model.StringFacetItem{ - {Value: "15", Count: 1}, - {Value: "16", Count: 1}, - {Value: "17", Count: 0}, - }, - }, - { - name: "filter by major version", - instances: allInstances, - filter: &PostgresInstanceFilter{MajorVersions: []string{"15"}}, - wantEnvironments: []model.StringFacetItem{ - {Value: "dev", Count: 1}, - {Value: "prod", Count: 1}, - }, - wantStates: []PostgresInstanceStateFacetItem{ - {State: PostgresInstanceStateAvailable, Count: 2}, - {State: PostgresInstanceStateDegraded, Count: 0}, - {State: PostgresInstanceStateProgressing, Count: 0}, - }, - wantHA: []model.BooleanFacetItem{ - {Value: false, Count: 1}, - {Value: true, Count: 1}, - }, - wantMajorVersions: []model.StringFacetItem{ - {Value: "15", Count: 2}, - {Value: "16", Count: 0}, - {Value: "17", Count: 0}, - }, - }, - { - name: "combined filter environment and state", - instances: allInstances, - filter: &PostgresInstanceFilter{ - Environments: []string{"prod"}, - States: []PostgresInstanceState{PostgresInstanceStateAvailable}, - }, - wantEnvironments: []model.StringFacetItem{ - {Value: "dev", Count: 0}, - {Value: "prod", Count: 1}, - }, - wantStates: []PostgresInstanceStateFacetItem{ - {State: PostgresInstanceStateAvailable, Count: 1}, - {State: PostgresInstanceStateDegraded, Count: 0}, - {State: PostgresInstanceStateProgressing, Count: 0}, - }, - wantHA: []model.BooleanFacetItem{ - {Value: false, Count: 0}, - {Value: true, Count: 1}, - }, - wantMajorVersions: []model.StringFacetItem{ - {Value: "15", Count: 1}, - {Value: "16", Count: 0}, - {Value: "17", Count: 0}, - }, - }, - { - name: "empty input returns empty facets", - instances: nil, - filter: nil, - wantEnvironments: []model.StringFacetItem{}, - wantStates: []PostgresInstanceStateFacetItem{}, - wantHA: []model.BooleanFacetItem{}, - wantMajorVersions: []model.StringFacetItem{}, - }, + {"all", nil, []model.StringFacetItem{{Value: "dev", Count: 2}, {Value: "prod", Count: 1}}, []PostgresBranchStateFacetItem{{State: PostgresBranchStateAvailable, Count: 1}, {State: PostgresBranchStateDegraded, Count: 1}, {State: PostgresBranchStateProgressing, Count: 1}}}, + {"filter by environment", &PostgresBranchFilter{Environments: []string{"dev"}}, []model.StringFacetItem{{Value: "dev", Count: 2}, {Value: "prod", Count: 0}}, []PostgresBranchStateFacetItem{{State: PostgresBranchStateAvailable, Count: 1}, {State: PostgresBranchStateDegraded, Count: 0}, {State: PostgresBranchStateProgressing, Count: 1}}}, + {"filter by state", &PostgresBranchFilter{States: []PostgresBranchState{PostgresBranchStateAvailable}}, []model.StringFacetItem{{Value: "dev", Count: 1}, {Value: "prod", Count: 0}}, []PostgresBranchStateFacetItem{{State: PostgresBranchStateAvailable, Count: 1}, {State: PostgresBranchStateDegraded, Count: 0}, {State: PostgresBranchStateProgressing, Count: 0}}}, } - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ctx := context.Background() - got := &PostgresInstanceFacets{ - AllInstances: tt.instances, - Filter: tt.filter, - } - - gotEnvironments := got.Environments(ctx) - if !reflect.DeepEqual(gotEnvironments, tt.wantEnvironments) { - t.Errorf("Environments =\n %v\nwant\n %v", gotEnvironments, tt.wantEnvironments) + f := &PostgresBranchFacets{AllInstances: all, Filter: tt.filter} + if got := f.Environments(context.Background()); !reflect.DeepEqual(got, tt.wantEnvironments) { + t.Errorf("environments=%v want %v", got, tt.wantEnvironments) } - - gotStates := got.States(ctx) - if !reflect.DeepEqual(gotStates, tt.wantStates) { - t.Errorf("States =\n %v\nwant\n %v", gotStates, tt.wantStates) - } - - gotHA := got.HighAvailability(ctx) - if !reflect.DeepEqual(gotHA, tt.wantHA) { - t.Errorf("HighAvailability =\n %v\nwant\n %v", gotHA, tt.wantHA) - } - - gotVersions := got.MajorVersions(ctx) - if !reflect.DeepEqual(gotVersions, tt.wantMajorVersions) { - t.Errorf("MajorVersions =\n %v\nwant\n %v", gotVersions, tt.wantMajorVersions) + if got := f.States(context.Background()); !reflect.DeepEqual(got, tt.wantStates) { + t.Errorf("states=%v want %v", got, tt.wantStates) } }) } diff --git a/internal/persistence/postgres/grant.go b/internal/persistence/postgres/grant.go new file mode 100644 index 000000000..b66157d3e --- /dev/null +++ b/internal/persistence/postgres/grant.go @@ -0,0 +1,138 @@ +package postgres + +import ( + "context" + "fmt" + "hash/crc32" + "strconv" + "time" + + "github.com/nais/api/internal/activitylog" + "github.com/nais/api/internal/auth/authz" + "github.com/nais/api/internal/environmentmapper" + "github.com/nais/api/internal/kubernetes" + "github.com/nais/api/internal/kubernetes/watcher" + "github.com/nais/api/internal/slug" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" +) + +// Keep the legacy Zalando port-forward grant independent of the PostgresBranch CRD. +// It uses API's existing system clients; only this mutation reads data.nais.io. +func legacyPostgresClient(ctx context.Context, environment string, gvr schema.GroupVersionResource) (dynamic.NamespaceableResourceInterface, error) { + client, ok := fromContext(ctx).clients[environmentmapper.ClusterName(environment)] + if !ok { + return nil, &watcher.ErrorUnknownEnvironment{Environment: environment} + } + return client.Resource(gvr), nil +} + +func getLegacyPostgres(ctx context.Context, input GrantPostgresAccessInput) error { + gvr := schema.GroupVersionResource{Group: "data.nais.io", Version: "v1", Resource: "postgres"} + client, err := legacyPostgresClient(ctx, input.EnvironmentName, gvr) + if err != nil { + return err + } + _, err = client.Namespace(input.TeamSlug.String()).Get(ctx, input.ClusterName, metav1.GetOptions{}) + return err +} + +func GrantZalandoPostgresAccess(ctx context.Context, input GrantPostgresAccessInput) error { + if err := input.Validate(ctx); err != nil { + return err + } + + namespace := fmt.Sprintf("pg-%s", input.TeamSlug.String()) + name := resourceNamer(input.TeamSlug, input.Grantee, input.ClusterName) + d, err := time.ParseDuration(input.Duration) + if err != nil { + return fmt.Errorf("parsing TTL: %w", err) + } + until := time.Now().Add(d) + labels := map[string]string{ + "euthanaisa.nais.io/kill-after": strconv.FormatInt(until.Unix(), 10), + "postgres.data.nais.io/name": input.ClusterName, + } + + if err := createGrantRole(ctx, input, name, namespace, labels); err != nil { + return err + } + if err := createGrantRoleBinding(ctx, input, name, namespace, labels); err != nil { + return err + } + + return activitylog.Create(ctx, activitylog.CreateInput{ + Action: activityLogEntryActionGrantAccess, + Actor: authz.ActorFromContext(ctx).User, + ResourceType: activityLogEntryResourceTypePostgres, + ResourceName: input.ClusterName, + EnvironmentName: new(input.EnvironmentName), + TeamSlug: new(input.TeamSlug), + Data: PostgresGrantAccessActivityLogEntryData{ + Grantee: input.Grantee, + Until: until, + }, + }) +} + +func createGrantRoleBinding(ctx context.Context, input GrantPostgresAccessInput, name, namespace string, labels map[string]string) error { + gvr := schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"} + client, err := legacyPostgresClient(ctx, input.EnvironmentName, gvr) + if err != nil { + return err + } + res := &unstructured.Unstructured{} + res.SetAPIVersion(gvr.GroupVersion().String()) + res.SetKind("RoleBinding") + res.SetName(name) + res.SetNamespace(namespace) + res.SetAnnotations(kubernetes.WithCommonAnnotations(nil, authz.ActorFromContext(ctx).User.Identity())) + res.SetLabels(labels) + kubernetes.SetManagedByConsoleLabel(res) + res.Object["roleRef"] = map[string]any{ + "apiGroup": "rbac.authorization.k8s.io", + "kind": "Role", + "name": name, + } + res.Object["subjects"] = []any{map[string]any{"kind": "User", "name": input.Grantee}} + return createOrUpdateGrantResource(ctx, res, client.Namespace(namespace)) +} + +func createGrantRole(ctx context.Context, input GrantPostgresAccessInput, name, namespace string, labels map[string]string) error { + gvr := schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "roles"} + client, err := legacyPostgresClient(ctx, input.EnvironmentName, gvr) + if err != nil { + return err + } + res := &unstructured.Unstructured{} + res.SetAPIVersion(gvr.GroupVersion().String()) + res.SetKind("Role") + res.SetName(name) + res.SetNamespace(namespace) + res.SetAnnotations(kubernetes.WithCommonAnnotations(nil, authz.ActorFromContext(ctx).User.Identity())) + res.SetLabels(labels) + kubernetes.SetManagedByConsoleLabel(res) + pods := []any{fmt.Sprintf("%s-0", input.ClusterName), fmt.Sprintf("%s-1", input.ClusterName), fmt.Sprintf("%s-2", input.ClusterName)} + res.Object["rules"] = []any{ + map[string]any{"apiGroups": []any{""}, "resources": []any{"pods"}, "verbs": []any{"get", "list", "watch"}, "resourceNames": pods}, + map[string]any{"apiGroups": []any{""}, "resources": []any{"pods/portforward"}, "verbs": []any{"get", "list", "watch", "create"}, "resourceNames": pods}, + } + return createOrUpdateGrantResource(ctx, res, client.Namespace(namespace)) +} + +func createOrUpdateGrantResource(ctx context.Context, res *unstructured.Unstructured, client dynamic.ResourceInterface) error { + _, err := client.Create(ctx, res, metav1.CreateOptions{}) + if k8serrors.IsAlreadyExists(err) { + _, err = client.Update(ctx, res, metav1.UpdateOptions{}) + } + return err +} + +func resourceNamer(teamSlug slug.Slug, grantee, name string) string { + hasher := crc32.NewIEEE() + fmt.Fprintf(hasher, "%s-%s-%s", teamSlug.String(), grantee, name) + return fmt.Sprintf("pg-grant-%08x", hasher.Sum32()) +} diff --git a/internal/persistence/postgres/grant_test.go b/internal/persistence/postgres/grant_test.go new file mode 100644 index 000000000..7d12ccaa6 --- /dev/null +++ b/internal/persistence/postgres/grant_test.go @@ -0,0 +1,50 @@ +package postgres + +import ( + "context" + "os" + "testing" + "time" + + "github.com/nais/api/internal/kubernetes" + "github.com/nais/api/internal/kubernetes/fake" + "github.com/nais/api/internal/kubernetes/watcher" + "github.com/nais/api/internal/slug" + "github.com/sirupsen/logrus/hooks/test" +) + +func TestLegacyPostgresGrantValidatesTheOldCluster(t *testing.T) { + scheme, err := kubernetes.NewScheme() + if err != nil { + t.Fatal(err) + } + clusters, err := kubernetes.CreateClusterConfigMap("nav", []string{"dev"}, nil) + if err != nil { + t.Fatal(err) + } + log, _ := test.NewNullLogger() + mgr, err := watcher.NewManager(scheme, clusters, log, watcher.WithClientCreator(fake.Clients(os.DirFS("../../../integration_tests/k8s_resources/grant_zalando_postgres_access")))) + if err != nil { + t.Fatal(err) + } + t.Cleanup(mgr.Stop) + ctx := context.Background() + wait, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + if !mgr.WaitForReady(wait) { + t.Fatal("Postgres watcher did not synchronize") + } + // The legacy grant must work even when the PostgresBranch CRD is absent. + ctx = NewLoaderContext(ctx, nil, "", "", "nav", mgr.GetDynamicClients()) + input := GrantPostgresAccessInput{ + ClusterName: "foobar", TeamSlug: slug.Slug("someteamname"), EnvironmentName: "dev", + Grantee: "someone@example.com", Duration: "30m", + } + if err := input.Validate(ctx); err != nil { + t.Fatalf("existing legacy cluster rejected: %v", err) + } + input.ClusterName = "missing" + if err := input.Validate(ctx); err == nil { + t.Fatal("missing legacy cluster accepted") + } +} diff --git a/internal/persistence/postgres/models.go b/internal/persistence/postgres/models.go index 2a6ea10fc..e2ad01ba7 100644 --- a/internal/persistence/postgres/models.go +++ b/internal/persistence/postgres/models.go @@ -17,102 +17,114 @@ import ( "github.com/nais/api/internal/kubernetes/watcher" "github.com/nais/api/internal/slug" "github.com/nais/api/internal/validate" - "github.com/nais/api/internal/workload" - data_nais_io_v1 "github.com/nais/pgrator/pkg/api/datav1" - "k8s.io/apimachinery/pkg/api/meta" + nais_io_v1 "github.com/nais/pgrator/pkg/api/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ) -type PostgresInstanceEdge = pagination.Edge[*PostgresInstance] +type PostgresBranchEdge = pagination.Edge[*PostgresBranch] -type PostgresInstanceFilter struct { - Name string `json:"name"` - Environments []string `json:"environments"` - States []PostgresInstanceState `json:"states"` - HighAvailability *bool `json:"highAvailability"` - MajorVersions []string `json:"majorVersions"` - Labels model.LabelFilters `json:"labels,omitempty"` +type PostgresBranchFilter struct { + Name string `json:"name"` + Environments []string `json:"environments"` + States []PostgresBranchState `json:"states"` + Labels model.LabelFilters `json:"labels,omitempty"` } -type PostgresInstanceConnection = pagination.FacetableConnection[*PostgresInstance, *PostgresInstanceFilter] +type PostgresBranchConnection = pagination.FacetableConnection[*PostgresBranch, *PostgresBranchFilter] -type PostgresInstanceFacets struct { - AllInstances []*PostgresInstance - Filter *PostgresInstanceFilter +type PostgresBranchFacets struct { + AllInstances []*PostgresBranch + Filter *PostgresBranchFilter filteredOnce sync.Once - filteredInstances []*PostgresInstance + filteredInstances []*PostgresBranch } -type PostgresInstanceStateFacetItem struct { - State PostgresInstanceState `json:"state"` - Count int `json:"count"` +type PostgresBranchStateFacetItem struct { + State PostgresBranchState `json:"state"` + Count int `json:"count"` } -type PostgresInstance struct { - Name string `json:"name"` - EnvironmentName string `json:"-"` - WorkloadReference *workload.Reference `json:"-"` - TeamSlug slug.Slug `json:"-"` - Resources *PostgresInstanceResources `json:"resources"` - MajorVersion string `json:"majorVersion"` - Audit PostgresInstanceAudit `json:"audit"` - MaintenanceWindow *PostgresInstanceMaintenanceWindow `json:"maintenanceWindow,omitempty"` - HighAvailability bool `json:"highAvailability"` - State PostgresInstanceState `json:"state"` - Labels []*model.ResourceLabel `json:"labels"` +// PostgresBranch represents an independently running database instance. +type PostgresBranch struct { + Name string `json:"name"` + EnvironmentName string `json:"-"` + TeamSlug slug.Slug `json:"-"` + PostgresName string `json:"postgres"` + State PostgresBranchState `json:"state"` + Labels []*model.ResourceLabel `json:"labels"` } -type PostgresInstanceState string +// Postgres selects the instance that workloads use. +type Postgres struct { + Name string `json:"name"` + EnvironmentName string `json:"-"` + TeamSlug slug.Slug `json:"-"` + ActiveBranch *string `json:"activeBranch,omitempty"` + MajorVersion string `json:"majorVersion"` + HighAvailability bool `json:"highAvailability"` + Resources PostgresResources `json:"resources"` + Labels []*model.ResourceLabel `json:"labels"` +} -const ( - PostgresInstanceStateAvailable PostgresInstanceState = "AVAILABLE" - PostgresInstanceStateProgressing PostgresInstanceState = "PROGRESSING" - PostgresInstanceStateDegraded PostgresInstanceState = "DEGRADED" +// PostgresResources contains only resource requests observed on the Postgres CR. +type PostgresResources struct { + CPU *string `json:"cpu,omitempty"` + Memory *string `json:"memory,omitempty"` + DiskSize *string `json:"diskSize,omitempty"` +} - postgresConditionTypeAvailable = "Available" - postgresConditionTypeProgressing = "Progressing" - postgresConditionTypeDegraded = "Degraded" +func (Postgres) IsNode() {} +func (p *Postgres) ID() ident.Ident { return newPostgresIdent(p.TeamSlug, p.EnvironmentName, p.Name) } + +type PostgresBranchState string + +const ( + PostgresBranchStateAvailable PostgresBranchState = "AVAILABLE" + PostgresBranchStateProgressing PostgresBranchState = "PROGRESSING" + PostgresBranchStateDegraded PostgresBranchState = "DEGRADED" ) -var AllPostgresInstanceState = []PostgresInstanceState{ - PostgresInstanceStateAvailable, - PostgresInstanceStateProgressing, - PostgresInstanceStateDegraded, +var AllPostgresBranchState = []PostgresBranchState{ + PostgresBranchStateAvailable, + PostgresBranchStateProgressing, + PostgresBranchStateDegraded, } -func (e PostgresInstanceState) IsValid() bool { +func (e PostgresBranchState) IsValid() bool { switch e { - case PostgresInstanceStateAvailable, PostgresInstanceStateProgressing, PostgresInstanceStateDegraded: + case PostgresBranchStateAvailable, PostgresBranchStateProgressing, PostgresBranchStateDegraded: return true } return false } -func (e PostgresInstanceState) String() string { +func (e PostgresBranchState) String() string { return string(e) } -func (e *PostgresInstanceState) UnmarshalGQL(v any) error { +func (e *PostgresBranchState) UnmarshalGQL(v any) error { str, ok := v.(string) if !ok { return fmt.Errorf("enums must be strings") } - *e = PostgresInstanceState(str) + *e = PostgresBranchState(str) if !e.IsValid() { - return fmt.Errorf("%s is not a valid PostgresInstanceState", str) + return fmt.Errorf("%s is not a valid PostgresBranchState", str) } return nil } -func (e PostgresInstanceState) MarshalGQL(w io.Writer) { +func (e PostgresBranchState) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } -func (e *PostgresInstanceState) UnmarshalJSON(b []byte) error { +func (e *PostgresBranchState) UnmarshalJSON(b []byte) error { s, err := strconv.Unquote(string(b)) if err != nil { return err @@ -120,54 +132,40 @@ func (e *PostgresInstanceState) UnmarshalJSON(b []byte) error { return e.UnmarshalGQL(s) } -func (e PostgresInstanceState) MarshalJSON() ([]byte, error) { +func (e PostgresBranchState) MarshalJSON() ([]byte, error) { var buf bytes.Buffer e.MarshalGQL(&buf) return buf.Bytes(), nil } -type PostgresInstanceAudit struct { - Enabled bool `json:"enabled"` - StatementClasses []string `json:"statementClasses,omitempty"` - TeamSlug slug.Slug `json:"-"` - EnvironmentName string `json:"-"` - InstanceName string `json:"-"` -} - -type PostgresInstanceMaintenanceWindow struct { - Day int `json:"day"` - Hour int `json:"hour"` -} +func (PostgresBranch) IsPersistence() {} -func (PostgresInstance) IsPersistence() {} +func (PostgresBranch) IsNode() {} -func (PostgresInstance) IsNode() {} +func (PostgresBranch) IsSearchNode() {} -func (PostgresInstance) IsSearchNode() {} - -type PostgresInstanceResources struct { - CPU string `json:"cpu"` - Memory string `json:"memory"` - DiskSize string `json:"diskSize"` -} - -type DeletePostgresInput struct { - Name string `json:"name"` +type DeletePostgresBranchInput struct { + Postgres string `json:"postgres"` + Branch string `json:"branch"` EnvironmentName string `json:"environmentName"` TeamSlug slug.Slug `json:"teamSlug"` } -func (i *DeletePostgresInput) Validate(ctx context.Context) error { +func (i *DeletePostgresBranchInput) Validate(ctx context.Context) error { return i.ValidationErrors(ctx).NilIfEmpty() } -func (i *DeletePostgresInput) ValidationErrors(_ context.Context) *validate.ValidationErrors { +func (i *DeletePostgresBranchInput) ValidationErrors(_ context.Context) *validate.ValidationErrors { verr := validate.New() - i.Name = strings.TrimSpace(i.Name) + i.Postgres = strings.TrimSpace(i.Postgres) + i.Branch = strings.TrimSpace(i.Branch) i.EnvironmentName = strings.TrimSpace(i.EnvironmentName) - if i.Name == "" { - verr.Add("name", "Name must not be empty.") + if i.Postgres == "" { + verr.Add("postgres", "Postgres must not be empty.") + } + if i.Branch == "" { + verr.Add("branch", "Branch must not be empty.") } if i.EnvironmentName == "" { verr.Add("environmentName", "Environment name must not be empty.") @@ -179,10 +177,11 @@ func (i *DeletePostgresInput) ValidationErrors(_ context.Context) *validate.Vali return verr } -type DeletePostgresPayload struct { - PostgresDeleted *bool `json:"postgresDeleted,omitempty"` +type DeletePostgresBranchPayload struct { + PostgresBranchDeleted *bool `json:"postgresBranchDeleted,omitempty"` } +// GrantPostgresAccessInput retains the temporary port-forward grant for legacy Zalando Postgres clusters. type GrantPostgresAccessInput struct { ClusterName string `json:"clusterName"` TeamSlug slug.Slug `json:"teamSlug"` @@ -217,18 +216,19 @@ func (i *GrantPostgresAccessInput) ValidationErrors(ctx context.Context) *valida if err != nil { verr.Add("duration", "%s", err) } else if duration > 4*time.Hour { - verr.Add("duration", "Duration \"%s\" is out-of-bounds. Must be less than 4 hours.", i.Duration) + verr.Add("duration", "Duration %q is out-of-bounds. Must be less than 4 hours.", i.Duration) } - _, err = GetZalandoPostgres(ctx, i.TeamSlug, i.EnvironmentName, i.ClusterName) - if err != nil { - if errors.Is(err, &watcher.ErrorNotFound{}) { - verr.Add("clusterName", "Could not find postgres cluster named \"%s\"", i.ClusterName) + if i.ClusterName == "" || i.EnvironmentName == "" || i.TeamSlug == "" { + return verr + } + if err := getLegacyPostgres(ctx, *i); err != nil { + if k8serrors.IsNotFound(err) { + verr.Add("clusterName", "Could not find postgres cluster named %q", i.ClusterName) } else { verr.Add("clusterName", "%s", err) } } - return verr } @@ -236,150 +236,340 @@ type GrantPostgresAccessPayload struct { Error *string `json:"error,omitempty"` } -func (p *PostgresInstance) GetObjectKind() schema.ObjectKind { +// CreatePostgresAccessInput requests a new, time-limited personal database access. +// The authenticated actor and final expiry are server-controlled. +type CreatePostgresAccessInput struct { + Postgres string `json:"postgres"` + Branch string `json:"branch"` + TeamSlug slug.Slug `json:"teamSlug"` + EnvironmentName string `json:"environmentName"` + AccessLevel PostgresAccessLevel `json:"accessLevel"` + Reason string `json:"reason"` + TTL string `json:"ttl"` +} + +func (i *CreatePostgresAccessInput) Validate(ctx context.Context) error { + return i.ValidationErrors(ctx).NilIfEmpty() +} + +func (i *CreatePostgresAccessInput) ValidationErrors(ctx context.Context) *validate.ValidationErrors { + verr := validate.New() + i.Postgres = strings.TrimSpace(i.Postgres) + i.Branch = strings.TrimSpace(i.Branch) + i.EnvironmentName = strings.TrimSpace(i.EnvironmentName) + i.Reason = strings.TrimSpace(i.Reason) + i.TTL = strings.TrimSpace(i.TTL) + + if i.Postgres == "" { + verr.Add("postgres", "Postgres must not be empty.") + } + if i.Branch == "" { + verr.Add("branch", "Branch must not be empty.") + } + if i.EnvironmentName == "" { + verr.Add("environmentName", "Environment name must not be empty.") + } + if i.TeamSlug == "" { + verr.Add("teamSlug", "Team slug must not be empty.") + } + if !i.AccessLevel.IsValid() { + verr.Add("accessLevel", "Access level %q is not valid.", i.AccessLevel) + } + if len(i.Reason) < 10 { + verr.Add("reason", "Reason must be at least 10 characters.") + } + if _, err := i.accessTTL(); err != nil { + verr.Add("ttl", "%s", err) + } + + if i.Postgres == "" || i.Branch == "" || i.EnvironmentName == "" || i.TeamSlug == "" { + return verr + } + + instance, err := GetReadyPostgresBranch(ctx, i.TeamSlug, i.EnvironmentName, i.Postgres, i.Branch) + if err != nil { + if k8serrors.IsNotFound(err) || errors.Is(err, &watcher.ErrorNotFound{}) { + verr.Add("branch", "Could not find PostgresBranch %q in Postgres %q", i.Branch, i.Postgres) + } else { + verr.Add("branch", "%s", err) + } + } else if instance.State != PostgresBranchStateAvailable { + verr.Add("branch", "Postgres branch %q is not available.", i.Branch) + } + + return verr +} + +type CreatePostgresAccessPayload struct { + Name string `json:"name"` + ExpiresAt time.Time `json:"expiresAt"` +} + +type PostgresAccessLevel string + +const ( + PostgresAccessLevelRead PostgresAccessLevel = "READ" + PostgresAccessLevelReadWrite PostgresAccessLevel = "READWRITE" + PostgresAccessLevelReadWriteCreate PostgresAccessLevel = "READWRITECREATE" +) + +func (e PostgresAccessLevel) IsValid() bool { + switch e { + case PostgresAccessLevelRead, PostgresAccessLevelReadWrite, PostgresAccessLevelReadWriteCreate: + return true + } + return false +} + +func (e PostgresAccessLevel) String() string { return string(e) } + +func (e PostgresAccessLevel) CRDValue() string { return strings.ToLower(string(e)) } + +func (e *PostgresAccessLevel) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + *e = PostgresAccessLevel(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid PostgresAccessLevel", str) + } + return nil +} + +func (e PostgresAccessLevel) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + +func (p *PostgresBranch) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind } -func (p *PostgresInstance) DeepCopyObject() runtime.Object { +func (p *PostgresBranch) DeepCopyObject() runtime.Object { return p } -func (p *PostgresInstance) GetName() string { - return p.Name +func (p *PostgresBranch) GetName() string { + return nais_io_v1.PostgresBranchObjectName(p.PostgresName, p.Name) } -func (p *PostgresInstance) GetNamespace() string { +func (p *PostgresBranch) SearchName() string { + return p.PostgresName + "/" + p.Name +} + +func (p *PostgresBranch) GetNamespace() string { return p.TeamSlug.String() } -func (p *PostgresInstance) GetLabels() map[string]string { +func (p *PostgresBranch) GetLabels() map[string]string { return nil } -func (p *PostgresInstance) ID() ident.Ident { - return newIdent(p.TeamSlug, p.EnvironmentName, p.Name) +func (p *PostgresBranch) ID() ident.Ident { + return newIdent(p.TeamSlug, p.EnvironmentName, p.PostgresName, p.Name) } -func toPostgres(u *unstructured.Unstructured, environmentName string) (*PostgresInstance, error) { - obj := &data_nais_io_v1.Postgres{} +func toPostgresBranch(u *unstructured.Unstructured, environmentName string) (*PostgresBranch, error) { + obj := &nais_io_v1.PostgresBranch{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, obj); err != nil { + return nil, fmt.Errorf("converting PostgresBranch: %w", err) + } + if obj.Spec.Postgres == "" || obj.Spec.BranchName == "" || obj.Name != nais_io_v1.PostgresBranchObjectName(obj.Spec.Postgres, obj.Spec.BranchName) { + return nil, fmt.Errorf("PostgresBranch %q has invalid postgres or branchName", obj.Name) + } + state := PostgresBranchStateProgressing + if obj.Status != nil { + state = postgresStateFromConditions(obj.Status.Conditions, obj.Status.ReconcilePhase == "Completed" && obj.Status.ObservedGeneration >= obj.Generation) + } + return &PostgresBranch{Name: obj.Spec.BranchName, EnvironmentName: environmentName, TeamSlug: slug.Slug(obj.Namespace), PostgresName: obj.Spec.Postgres, State: state, Labels: model.UserLabels(obj.Labels)}, nil +} +func toPostgres(u *unstructured.Unstructured, environmentName string) (*Postgres, error) { + obj := &nais_io_v1.Postgres{} if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, obj); err != nil { - return nil, fmt.Errorf("converting to Postgres: %w", err) - } - - audit := false - statementClasses := []string(nil) - if obj.Spec.Cluster.Audit != nil { - audit = obj.Spec.Cluster.Audit.Enabled - if len(obj.Spec.Cluster.Audit.StatementClasses) > 0 { - statementClasses = make([]string, 0, len(obj.Spec.Cluster.Audit.StatementClasses)) - for _, statementClass := range obj.Spec.Cluster.Audit.StatementClasses { - statementClasses = append(statementClasses, string(statementClass)) - } + return nil, fmt.Errorf("converting Postgres: %w", err) + } + var active *string + if obj.Status != nil && obj.Status.ActiveBranch != "" { + active = &obj.Status.ActiveBranch + } + quantity := func(value resource.Quantity) *string { + if value.IsZero() { + return nil } + text := value.String() + return &text } - - state := PostgresInstanceStateAvailable - if obj.Status != nil { - state = postgresStateFromConditions(obj.Status.Conditions) - } - - return &PostgresInstance{ - Name: obj.GetName(), - EnvironmentName: environmentName, - TeamSlug: slug.Slug(obj.GetNamespace()), - WorkloadReference: workload.ReferenceFromOwnerReferences(obj.GetOwnerReferences()), - Resources: &PostgresInstanceResources{ - CPU: obj.Spec.Cluster.Resources.Cpu.String(), - Memory: obj.Spec.Cluster.Resources.Memory.String(), - DiskSize: obj.Spec.Cluster.Resources.DiskSize.String(), - }, - MajorVersion: obj.Spec.Cluster.MajorVersion, - Audit: PostgresInstanceAudit{ - Enabled: audit, - StatementClasses: statementClasses, - TeamSlug: slug.Slug(obj.GetNamespace()), - EnvironmentName: environmentName, - InstanceName: obj.GetName(), + return &Postgres{ + Name: obj.Name, EnvironmentName: environmentName, TeamSlug: slug.Slug(obj.Namespace), + ActiveBranch: active, MajorVersion: obj.Spec.MajorVersion, HighAvailability: obj.Spec.HighAvailability, + Resources: PostgresResources{ + CPU: quantity(obj.Spec.Resources.Cpu), Memory: quantity(obj.Spec.Resources.Memory), + DiskSize: quantity(obj.Spec.Resources.DiskSize), }, - HighAvailability: obj.Spec.Cluster.HighAvailability, - MaintenanceWindow: func() *PostgresInstanceMaintenanceWindow { - if obj.Spec.MaintenanceWindow == nil { - return nil - } - hour := 0 - if obj.Spec.MaintenanceWindow.Hour != nil { - hour = *obj.Spec.MaintenanceWindow.Hour - } - return &PostgresInstanceMaintenanceWindow{ - Day: obj.Spec.MaintenanceWindow.Day, - Hour: hour, - } - }(), - State: state, - Labels: model.UserLabels(obj.GetLabels()), + Labels: model.UserLabels(obj.Labels), }, nil } -func postgresStateFromConditions(conditions []metav1.Condition) PostgresInstanceState { - if meta.IsStatusConditionTrue(conditions, postgresConditionTypeDegraded) { - return PostgresInstanceStateDegraded +// postgresStateFromConditions interprets the CNPG phase mirrored by pgrator. +// ObservedState=False means no phase has been observed, not a failed cluster. +func postgresStateFromConditions(conditions []metav1.Condition, reconciled bool) PostgresBranchState { + if !reconciled { + return PostgresBranchStateProgressing } + for _, condition := range conditions { + if condition.Type != "cluster.postgresql.cnpg.io/ObservedState" || condition.Status != metav1.ConditionTrue { + continue + } + phase, ok := strings.CutPrefix(condition.Message, "Cluster is in phase: ") + if !ok { + continue + } + switch phase { + case "Cluster in healthy state": + return PostgresBranchStateAvailable + case "Cluster is unrecoverable and needs manual intervention", + "Cluster cannot proceed to reconciliation due to an unknown plugin being required", + "Cluster cannot proceed to reconciliation due to an error while interacting with plugins", + "Cluster has incomplete or invalid image catalog": + return PostgresBranchStateDegraded + } + } + return PostgresBranchStateProgressing +} + +type PostgresBranchOrder struct { + Field PostgresBranchOrderField `json:"field"` + Direction model.OrderDirection `json:"direction"` +} + +type PostgresBranchOrderField string + +const ( + PostgresBranchOrderFieldName PostgresBranchOrderField = "NAME" + PostgresBranchOrderFieldEnvironment PostgresBranchOrderField = "ENVIRONMENT" +) + +var AllPostgresBranchOrderField = []PostgresBranchOrderField{ + PostgresBranchOrderFieldName, + PostgresBranchOrderFieldEnvironment, +} - if meta.IsStatusConditionTrue(conditions, postgresConditionTypeProgressing) { - return PostgresInstanceStateProgressing +func (e PostgresBranchOrderField) IsValid() bool { + switch e { + case PostgresBranchOrderFieldName, PostgresBranchOrderFieldEnvironment: + return true } + return false +} - return PostgresInstanceStateAvailable +func (e PostgresBranchOrderField) String() string { + return string(e) } -type PostgresInstanceOrder struct { - Field PostgresInstanceOrderField `json:"field"` - Direction model.OrderDirection `json:"direction"` +func (e *PostgresBranchOrderField) UnmarshalGQL(v any) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = PostgresBranchOrderField(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid PostgresBranchOrderField", str) + } + return nil } -type PostgresInstanceOrderField string +func (e PostgresBranchOrderField) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) +} + +func (e *PostgresBranchOrderField) UnmarshalJSON(b []byte) error { + s, err := strconv.Unquote(string(b)) + if err != nil { + return err + } + return e.UnmarshalGQL(s) +} + +func (e PostgresBranchOrderField) MarshalJSON() ([]byte, error) { + var buf bytes.Buffer + e.MarshalGQL(&buf) + return buf.Bytes(), nil +} + +type TeamInventoryCountPostgresBranches struct { + Total int `json:"total"` +} + +// PostgresAccess exposes the API/CLI-facing state of a controller-owned personal +// database access. Credentials are read from the controller-owned Secret on +// demand; they are never cached by the watcher. +type PostgresAccess struct { + Name string `json:"name"` + TeamSlug slug.Slug `json:"-"` + EnvironmentName string `json:"-"` + PostgresBranchName string `json:"postgresBranch"` + Username string `json:"username"` + AccessLevel PostgresAccessLevel `json:"accessLevel"` + ExpiresAt time.Time `json:"expiresAt"` + State PostgresAccessState `json:"state"` + Message *string `json:"message,omitempty"` + RelayAccess *string `json:"relayAccess,omitempty"` +} + +func (PostgresAccess) IsNode() {} + +func (p *PostgresAccess) ID() ident.Ident { + return newAccessIdent(p.TeamSlug, p.EnvironmentName, p.Name) +} + +type PostgresAccessState string const ( - PostgresInstanceOrderFieldName PostgresInstanceOrderField = "NAME" - PostgresInstanceOrderFieldEnvironment PostgresInstanceOrderField = "ENVIRONMENT" + PostgresAccessStatePending PostgresAccessState = "PENDING" + PostgresAccessStateReady PostgresAccessState = "READY" + PostgresAccessStateFailed PostgresAccessState = "FAILED" + PostgresAccessStateExpired PostgresAccessState = "EXPIRED" ) -var AllPostgresInstanceOrderField = []PostgresInstanceOrderField{ - PostgresInstanceOrderFieldName, - PostgresInstanceOrderFieldEnvironment, +var AllPostgresAccessState = []PostgresAccessState{ + PostgresAccessStatePending, + PostgresAccessStateReady, + PostgresAccessStateFailed, + PostgresAccessStateExpired, } -func (e PostgresInstanceOrderField) IsValid() bool { +func (e PostgresAccessState) IsValid() bool { switch e { - case PostgresInstanceOrderFieldName, PostgresInstanceOrderFieldEnvironment: + case PostgresAccessStatePending, PostgresAccessStateReady, PostgresAccessStateFailed, PostgresAccessStateExpired: return true } return false } -func (e PostgresInstanceOrderField) String() string { +func (e PostgresAccessState) String() string { return string(e) } -func (e *PostgresInstanceOrderField) UnmarshalGQL(v any) error { +func (e *PostgresAccessState) UnmarshalGQL(v any) error { str, ok := v.(string) if !ok { return fmt.Errorf("enums must be strings") } - *e = PostgresInstanceOrderField(str) + *e = PostgresAccessState(str) if !e.IsValid() { - return fmt.Errorf("%s is not a valid PostgresInstanceOrderField", str) + return fmt.Errorf("%s is not a valid PostgresAccessState", str) } return nil } -func (e PostgresInstanceOrderField) MarshalGQL(w io.Writer) { +func (e PostgresAccessState) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } -func (e *PostgresInstanceOrderField) UnmarshalJSON(b []byte) error { +func (e *PostgresAccessState) UnmarshalJSON(b []byte) error { s, err := strconv.Unquote(string(b)) if err != nil { return err @@ -387,12 +577,44 @@ func (e *PostgresInstanceOrderField) UnmarshalJSON(b []byte) error { return e.UnmarshalGQL(s) } -func (e PostgresInstanceOrderField) MarshalJSON() ([]byte, error) { +func (e PostgresAccessState) MarshalJSON() ([]byte, error) { var buf bytes.Buffer e.MarshalGQL(&buf) return buf.Bytes(), nil } -type TeamInventoryCountPostgresInstances struct { - Total int `json:"total"` +type PostgresAccessConnectionInput struct { + Name string `json:"name"` + TeamSlug slug.Slug `json:"teamSlug"` + EnvironmentName string `json:"environmentName"` +} + +func (i *PostgresAccessConnectionInput) Validate(ctx context.Context) error { + return i.ValidationErrors(ctx).NilIfEmpty() +} + +func (i *PostgresAccessConnectionInput) ValidationErrors(_ context.Context) *validate.ValidationErrors { + verr := validate.New() + i.Name = strings.TrimSpace(i.Name) + i.EnvironmentName = strings.TrimSpace(i.EnvironmentName) + if i.Name == "" { + verr.Add("name", "Name must not be empty.") + } + if i.TeamSlug == "" { + verr.Add("teamSlug", "Team slug must not be empty.") + } + if i.EnvironmentName == "" { + verr.Add("environmentName", "Environment name must not be empty.") + } + return verr +} + +type PostgresAccessConnectionDetails struct { + Username string `json:"username"` + Password string `json:"password"` + CACertificate string `json:"caCertificate"` + ServerName string `json:"serverName"` + RelayEndpoint string `json:"relayEndpoint"` + RelayAccess string `json:"relayAccess"` + RelayToken string `json:"relayToken"` } diff --git a/internal/persistence/postgres/models_test.go b/internal/persistence/postgres/models_test.go index 91e046c71..de4901f4a 100644 --- a/internal/persistence/postgres/models_test.go +++ b/internal/persistence/postgres/models_test.go @@ -6,246 +6,104 @@ import ( "testing" "github.com/nais/api/internal/slug" - data_nais_io_v1 "github.com/nais/pgrator/pkg/api/datav1" - "k8s.io/apimachinery/pkg/api/resource" + nais_io_v1 "github.com/nais/pgrator/pkg/api/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" ) -func TestPostgresStateFromConditions(t *testing.T) { - tests := []struct { - name string - conditions []metav1.Condition - want PostgresInstanceState - }{ - { - name: "degraded when degraded is true", - conditions: []metav1.Condition{ - {Type: postgresConditionTypeAvailable, Status: metav1.ConditionTrue}, - {Type: postgresConditionTypeDegraded, Status: metav1.ConditionTrue}, - }, - want: PostgresInstanceStateDegraded, - }, - { - name: "progressing when progressing is true", - conditions: []metav1.Condition{ - {Type: postgresConditionTypeProgressing, Status: metav1.ConditionTrue}, - }, - want: PostgresInstanceStateProgressing, - }, - { - name: "available when available is true", - conditions: []metav1.Condition{ - {Type: postgresConditionTypeAvailable, Status: metav1.ConditionTrue}, - }, - want: PostgresInstanceStateAvailable, - }, - { - name: "available when no recognized true condition", - conditions: []metav1.Condition{ - {Type: postgresConditionTypeAvailable, Status: metav1.ConditionFalse}, - {Type: postgresConditionTypeProgressing, Status: metav1.ConditionFalse}, - }, - want: PostgresInstanceStateAvailable, - }, - { - name: "available when no conditions", - want: PostgresInstanceStateAvailable, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := postgresStateFromConditions(tt.conditions) - if got != tt.want { - t.Errorf("postgresStateFromConditions() = %q, want %q", got, tt.want) - } - }) +func TestToPostgresBranch(t *testing.T) { + obj := &unstructured.Unstructured{Object: map[string]any{ + "apiVersion": "nais.io/v1", "kind": "PostgresBranch", + "metadata": map[string]any{"name": nais_io_v1.PostgresBranchObjectName("orders", "restored"), "namespace": "my-team"}, + "spec": map[string]any{"postgres": "orders", "branchName": "restored"}, + "status": map[string]any{"reconcilePhase": "Completed", "conditions": []any{map[string]any{"type": "cluster.postgresql.cnpg.io/ObservedState", "status": "True", "lastTransitionTime": "2026-01-01T00:00:00Z", "reason": "Reconciled", "message": "Cluster is in phase: Cluster in healthy state"}}}, + }} + got, err := toPostgresBranch(obj, "dev") + if err != nil { + t.Fatal(err) } -} - -func TestToPostgres_MaintenanceWindow(t *testing.T) { - intPtr := func(v int) *int { return &v } - - tests := []struct { - name string - maintenance *data_nais_io_v1.Maintenance - wantNil bool - wantDay int - wantHour int - }{ - { - name: "populates day and hour when maintenance window is present", - maintenance: &data_nais_io_v1.Maintenance{ - Day: 2, - Hour: intPtr(5), - }, - wantDay: 2, - wantHour: 5, - }, - { - name: "defaults hour to 0 when maintenance window hour is omitted", - maintenance: &data_nais_io_v1.Maintenance{ - Day: 6, - }, - wantDay: 6, - wantHour: 0, - }, - { - name: "returns nil maintenance window when not configured", - wantNil: true, - }, + if got.Name != "restored" || got.PostgresName != "orders" || got.State != PostgresBranchStateAvailable { + t.Errorf("unexpected PostgresBranch: %+v", got) } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - obj := newPostgresTestObject(tt.maintenance, nil) - got := toPostgresFromCRD(t, obj) - - if tt.wantNil { - if got.MaintenanceWindow != nil { - t.Fatalf("MaintenanceWindow = %#v, want nil", got.MaintenanceWindow) - } - return - } - - if got.MaintenanceWindow == nil { - t.Fatalf("MaintenanceWindow = nil, want non-nil") - } - - if got.MaintenanceWindow.Day != tt.wantDay { - t.Errorf("MaintenanceWindow.Day = %d, want %d", got.MaintenanceWindow.Day, tt.wantDay) - } - - if got.MaintenanceWindow.Hour != tt.wantHour { - t.Errorf("MaintenanceWindow.Hour = %d, want %d", got.MaintenanceWindow.Hour, tt.wantHour) - } - }) + if got.GetName() != obj.GetName() { + t.Errorf("watcher name = %q, want %q", got.GetName(), obj.GetName()) } -} - -func TestToPostgres_MaintenanceWindow_WithConditions(t *testing.T) { - hour := 7 - obj := newPostgresTestObject(&data_nais_io_v1.Maintenance{ - Day: 3, - Hour: &hour, - }, []metav1.Condition{ - {Type: postgresConditionTypeAvailable, Status: metav1.ConditionTrue}, - {Type: postgresConditionTypeDegraded, Status: metav1.ConditionTrue}, - }) - - got := toPostgresFromCRD(t, obj) - - if got.MaintenanceWindow == nil { - t.Fatalf("MaintenanceWindow = nil, want non-nil") + if got.SearchName() != "orders/restored" { + t.Errorf("search name = %q, want orders/restored", got.SearchName()) } - - if got.MaintenanceWindow.Day != 3 { - t.Errorf("MaintenanceWindow.Day = %d, want %d", got.MaintenanceWindow.Day, 3) + if got.ID().Type != "PBR" { + t.Errorf("PostgresBranch ID type = %q, want PBR", got.ID().Type) } - - if got.MaintenanceWindow.Hour != 7 { - t.Errorf("MaintenanceWindow.Hour = %d, want %d", got.MaintenanceWindow.Hour, 7) + team, env, pg, branch, err := parsePostgresBranchIdent(got.ID()) + if err != nil || team != "my-team" || env != "dev" || pg != "orders" || branch != "restored" { + t.Errorf("branch ID = (%q, %q, %q, %q, %v)", team, env, pg, branch, err) } +} - if got.State != PostgresInstanceStateDegraded { - t.Errorf("State = %q, want %q", got.State, PostgresInstanceStateDegraded) +func TestToPostgresBranchRejectsMismatchedObjectName(t *testing.T) { + obj := &unstructured.Unstructured{Object: map[string]any{ + "apiVersion": "nais.io/v1", "kind": "PostgresBranch", + "metadata": map[string]any{"name": nais_io_v1.PostgresBranchObjectName("orders", "main")}, + "spec": map[string]any{"postgres": "orders", "branchName": "other"}, + }} + if _, err := toPostgresBranch(obj, "dev"); err == nil { + t.Fatal("expected a mismatched branch object name to be rejected") } } -func TestToPostgres_AuditStatementClasses(t *testing.T) { - obj := newPostgresTestObject(nil, nil) - obj.Spec.Cluster.Audit = &data_nais_io_v1.PostgresAudit{ - Enabled: true, - StatementClasses: []data_nais_io_v1.PostgresAuditStatementClass{ - "ddl", - "write", - }, +func TestToPostgres(t *testing.T) { + obj := &unstructured.Unstructured{Object: map[string]any{ + "apiVersion": "nais.io/v1", "kind": "Postgres", + "metadata": map[string]any{"name": "orders", "namespace": "my-team"}, + "spec": map[string]any{"majorVersion": "17", "highAvailability": true, "resources": map[string]any{"cpu": "100m", "memory": "2Gi", "diskSize": "10Gi"}}, + "status": map[string]any{"activeBranch": "restored"}, + }} + got, err := toPostgres(obj, "dev") + if err != nil { + t.Fatal(err) } - - got := toPostgresFromCRD(t, obj) - - if !got.Audit.Enabled { - t.Fatalf("Audit.Enabled = %v, want true", got.Audit.Enabled) + if got.Name != "orders" || got.MajorVersion != "17" || got.ActiveBranch == nil || *got.ActiveBranch != "restored" { + t.Errorf("unexpected Postgres: %+v", got) } - - want := []string{"ddl", "write"} - if !reflect.DeepEqual(got.Audit.StatementClasses, want) { - t.Errorf("Audit.StatementClasses = %#v, want %#v", got.Audit.StatementClasses, want) + if got.Resources.CPU == nil || *got.Resources.CPU != "100m" || got.Resources.Memory == nil || *got.Resources.Memory != "2Gi" || got.Resources.DiskSize == nil || *got.Resources.DiskSize != "10Gi" { + t.Errorf("unexpected Postgres resources: %+v", got.Resources) } } -func TestToPostgres_AuditStatementClasses_EmptyWhenAuditMissingOrEmpty(t *testing.T) { +func TestPostgresStateFromConditions(t *testing.T) { tests := []struct { - name string - audit *data_nais_io_v1.PostgresAudit + name string + reconciled bool + conditions []metav1.Condition + want PostgresBranchState }{ - { - name: "missing audit config", - audit: nil, - }, - { - name: "empty audit config", - audit: &data_nais_io_v1.PostgresAudit{}, - }, + {name: "not reconciled", want: PostgresBranchStateProgressing}, + {name: "healthy", reconciled: true, conditions: []metav1.Condition{{Type: "cluster.postgresql.cnpg.io/ObservedState", Status: metav1.ConditionTrue, Message: "Cluster is in phase: Cluster in healthy state"}}, want: PostgresBranchStateAvailable}, + {name: "still starting", reconciled: true, conditions: []metav1.Condition{{Type: "cluster.postgresql.cnpg.io/ObservedState", Status: metav1.ConditionFalse, Message: "Cluster is in phase: "}}, want: PostgresBranchStateProgressing}, + {name: "unrecoverable", reconciled: true, conditions: []metav1.Condition{{Type: "cluster.postgresql.cnpg.io/ObservedState", Status: metav1.ConditionTrue, Message: "Cluster is in phase: Cluster is unrecoverable and needs manual intervention"}}, want: PostgresBranchStateDegraded}, + {name: "plugin failure", reconciled: true, conditions: []metav1.Condition{{Type: "cluster.postgresql.cnpg.io/ObservedState", Status: metav1.ConditionTrue, Message: "Cluster is in phase: Cluster cannot proceed to reconciliation due to an error while interacting with plugins"}}, want: PostgresBranchStateDegraded}, + {name: "other phase", reconciled: true, conditions: []metav1.Condition{{Type: "cluster.postgresql.cnpg.io/ObservedState", Status: metav1.ConditionTrue, Message: "Cluster is in phase: Online upgrade in progress"}}, want: PostgresBranchStateProgressing}, + {name: "missing", reconciled: true, want: PostgresBranchStateProgressing}, } - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - obj := newPostgresTestObject(nil, nil) - obj.Spec.Cluster.Audit = tt.audit - - got := toPostgresFromCRD(t, obj) - - if got.Audit.Enabled { - t.Errorf("Audit.Enabled = %v, want false", got.Audit.Enabled) - } - - if len(got.Audit.StatementClasses) != 0 { - t.Errorf("Audit.StatementClasses = %#v, want empty", got.Audit.StatementClasses) + if got := postgresStateFromConditions(tt.conditions, tt.reconciled); got != tt.want { + t.Errorf("state = %s, want %s", got, tt.want) } }) } } -func newPostgresTestObject(maintenance *data_nais_io_v1.Maintenance, conditions []metav1.Condition) *data_nais_io_v1.Postgres { - obj := &data_nais_io_v1.Postgres{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-db", - Namespace: "my-team", - }, - Spec: data_nais_io_v1.PostgresSpec{ - Cluster: data_nais_io_v1.PostgresCluster{ - Resources: data_nais_io_v1.PostgresResources{ - DiskSize: resource.MustParse("10Gi"), - Cpu: resource.MustParse("100m"), - Memory: resource.MustParse("1Gi"), - }, - MajorVersion: "17", - }, - MaintenanceWindow: maintenance, - }, - } - - if len(conditions) > 0 { - obj.Status = &data_nais_io_v1.PostgresStatus{} - obj.Status.Conditions = conditions - } - - return obj -} - -func TestDeletePostgresInput_ValidationErrors(t *testing.T) { +func TestDeletePostgresBranchInput_ValidationErrors(t *testing.T) { tests := []struct { name string - input DeletePostgresInput + input DeletePostgresBranchInput wantErrFields []string }{ { name: "all fields valid", - input: DeletePostgresInput{ - Name: "my-db", + input: DeletePostgresBranchInput{ + Postgres: "my-db", Branch: "main", EnvironmentName: "dev", TeamSlug: slug.Slug("my-team"), }, @@ -253,17 +111,17 @@ func TestDeletePostgresInput_ValidationErrors(t *testing.T) { }, { name: "empty name", - input: DeletePostgresInput{ - Name: "", + input: DeletePostgresBranchInput{ + Postgres: "my-db", Branch: "", EnvironmentName: "dev", TeamSlug: slug.Slug("my-team"), }, - wantErrFields: []string{"name"}, + wantErrFields: []string{"branch"}, }, { name: "empty environmentName", - input: DeletePostgresInput{ - Name: "my-db", + input: DeletePostgresBranchInput{ + Postgres: "my-db", Branch: "main", EnvironmentName: "", TeamSlug: slug.Slug("my-team"), }, @@ -271,8 +129,8 @@ func TestDeletePostgresInput_ValidationErrors(t *testing.T) { }, { name: "empty teamSlug", - input: DeletePostgresInput{ - Name: "my-db", + input: DeletePostgresBranchInput{ + Postgres: "my-db", Branch: "main", EnvironmentName: "dev", TeamSlug: slug.Slug(""), }, @@ -280,21 +138,21 @@ func TestDeletePostgresInput_ValidationErrors(t *testing.T) { }, { name: "all fields empty", - input: DeletePostgresInput{ - Name: "", + input: DeletePostgresBranchInput{ + Postgres: "", Branch: "", EnvironmentName: "", TeamSlug: slug.Slug(""), }, - wantErrFields: []string{"name", "environmentName", "teamSlug"}, + wantErrFields: []string{"postgres", "branch", "environmentName", "teamSlug"}, }, { name: "whitespace-only name treated as empty", - input: DeletePostgresInput{ - Name: " ", + input: DeletePostgresBranchInput{ + Postgres: "my-db", Branch: " ", EnvironmentName: "dev", TeamSlug: slug.Slug("my-team"), }, - wantErrFields: []string{"name"}, + wantErrFields: []string{"branch"}, }, } @@ -313,19 +171,3 @@ func TestDeletePostgresInput_ValidationErrors(t *testing.T) { }) } } - -func toPostgresFromCRD(t *testing.T, obj *data_nais_io_v1.Postgres) *PostgresInstance { - t.Helper() - - uMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) - if err != nil { - t.Fatalf("ToUnstructured() error = %v", err) - } - - got, err := toPostgres(&unstructured.Unstructured{Object: uMap}, "dev") - if err != nil { - t.Fatalf("toPostgres() error = %v", err) - } - - return got -} diff --git a/internal/persistence/postgres/node.go b/internal/persistence/postgres/node.go index 1c7a49ffb..d7b824dbe 100644 --- a/internal/persistence/postgres/node.go +++ b/internal/persistence/postgres/node.go @@ -10,14 +10,31 @@ import ( type identType int const ( - identZalandoPostgres identType = iota + identPostgresBranch identType = iota + identPostgresAccess + identPostgres ) func init() { - ident.RegisterIdentType(identZalandoPostgres, "PP", GetZalandoPostgresByIdent) + ident.RegisterIdentType(identPostgresBranch, "PBR", GetPostgresBranchByIdent) + ident.RegisterIdentType(identPostgresAccess, "PA", GetPostgresAccessByIdent) + ident.RegisterIdentType(identPostgres, "PG", GetPostgresByIdent) } -func parseIdent(id ident.Ident) (teamSlug slug.Slug, environmentName, postgresInstanceName string, err error) { +func parsePostgresBranchIdent(id ident.Ident) (teamSlug slug.Slug, environmentName, postgresName, branchName string, err error) { + parts := id.Parts() + if len(parts) != 4 { + return "", "", "", "", fmt.Errorf("invalid ident") + } + + return slug.Slug(parts[0]), parts[1], parts[2], parts[3], nil +} + +func newIdent(teamSlug slug.Slug, environmentName, postgresName, branchName string) ident.Ident { + return ident.NewIdent(identPostgresBranch, teamSlug.String(), environmentName, postgresName, branchName) +} + +func parseAccessIdent(id ident.Ident) (teamSlug slug.Slug, environmentName, name string, err error) { parts := id.Parts() if len(parts) != 3 { return "", "", "", fmt.Errorf("invalid ident") @@ -26,6 +43,10 @@ func parseIdent(id ident.Ident) (teamSlug slug.Slug, environmentName, postgresIn return slug.Slug(parts[0]), parts[1], parts[2], nil } -func newIdent(teamSlug slug.Slug, environmentName, postgresInstanceName string) ident.Ident { - return ident.NewIdent(identZalandoPostgres, teamSlug.String(), environmentName, postgresInstanceName) +func newAccessIdent(teamSlug slug.Slug, environmentName, name string) ident.Ident { + return ident.NewIdent(identPostgresAccess, teamSlug.String(), environmentName, name) +} + +func newPostgresIdent(teamSlug slug.Slug, environmentName, name string) ident.Ident { + return ident.NewIdent(identPostgres, teamSlug.String(), environmentName, name) } diff --git a/internal/persistence/postgres/queries.go b/internal/persistence/postgres/queries.go index a401c4376..80fdc7874 100644 --- a/internal/persistence/postgres/queries.go +++ b/internal/persistence/postgres/queries.go @@ -3,61 +3,69 @@ package postgres import ( "cmp" "context" + "errors" "fmt" - "hash/crc32" - "net/url" "slices" - "strconv" + "strings" "time" + "github.com/google/uuid" "github.com/nais/api/internal/activitylog" "github.com/nais/api/internal/auth/authz" + "github.com/nais/api/internal/graph/apierror" "github.com/nais/api/internal/graph/ident" "github.com/nais/api/internal/graph/model" "github.com/nais/api/internal/graph/pagination" "github.com/nais/api/internal/kubernetes" "github.com/nais/api/internal/kubernetes/watcher" "github.com/nais/api/internal/slug" - "github.com/nais/api/internal/team" "github.com/nais/api/internal/workload" "github.com/nais/api/internal/workload/application" "github.com/nais/api/internal/workload/job" + liberatorv1 "github.com/nais/liberator/pkg/apis/nais.io/v1" + nais_io_v1 "github.com/nais/pgrator/pkg/api/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" + "k8s.io/utils/ptr" ) -func Delete(ctx context.Context, input DeletePostgresInput) (*DeletePostgresPayload, error) { +func Delete(ctx context.Context, input DeletePostgresBranchInput) (*DeletePostgresBranchPayload, error) { if err := input.Validate(ctx); err != nil { return nil, err } - client, err := fromContext(ctx).zalandoPostgresWatcher.ImpersonatedClientWithNamespace(ctx, input.EnvironmentName, input.TeamSlug.String()) + client, err := fromContext(ctx).postgresBranchWatcher.SystemAuthenticatedClient(ctx, input.EnvironmentName) if err != nil { return nil, err } - - obj, err := client.Get(ctx, input.Name, metav1.GetOptions{}) + objectName := nais_io_v1.PostgresBranchObjectName(input.Postgres, input.Branch) + instance, err := client.Namespace(input.TeamSlug.String()).Get(ctx, objectName, metav1.GetOptions{}) + if k8serrors.IsNotFound(err) { + return nil, apierror.Errorf("PostgresBranch %q not found in Postgres %q", input.Branch, input.Postgres) + } if err != nil { - return nil, err + return nil, fmt.Errorf("getting PostgresBranch %q before deletion: %w", input.Branch, err) } - - allowDeletion, _, err := unstructured.NestedBool(obj.Object, "spec", "cluster", "allowDeletion") + branch, err := toPostgresBranch(instance, input.EnvironmentName) + if err != nil || branch.PostgresName != input.Postgres || branch.Name != input.Branch { + return nil, apierror.Errorf("PostgresBranch %q not found in Postgres %q", input.Branch, input.Postgres) + } + postgresClient, err := fromContext(ctx).postgresBranchWatcher.SystemAuthenticatedClient(ctx, input.EnvironmentName, watcher.WithImpersonatedClientGVR(schema.GroupVersionResource{ + Group: "nais.io", Version: "v1", Resource: "postgres", + })) if err != nil { return nil, err } - if !allowDeletion { - if err := unstructured.SetNestedField(obj.Object, true, "spec", "cluster", "allowDeletion"); err != nil { - return nil, err - } - if _, err = client.Update(ctx, obj, metav1.UpdateOptions{}); err != nil { - return nil, fmt.Errorf("enabling deletion: %w", err) - } + postgres, err := postgresClient.Namespace(input.TeamSlug.String()).Get(ctx, input.Postgres, metav1.GetOptions{}) + if err != nil { + return nil, fmt.Errorf("getting Postgres %q before instance deletion: %w", input.Postgres, err) } - - if err := fromContext(ctx).zalandoPostgresWatcher.Delete(ctx, input.EnvironmentName, input.TeamSlug.String(), input.Name); err != nil { + if err := ensureInstanceMayBeDeleted(input.Branch, postgres); err != nil { + return nil, err + } + if err := client.Namespace(input.TeamSlug.String()).Delete(ctx, objectName, metav1.DeleteOptions{Preconditions: &metav1.Preconditions{UID: ptr.To(instance.GetUID())}}); err != nil { return nil, err } @@ -65,259 +73,495 @@ func Delete(ctx context.Context, input DeletePostgresInput) (*DeletePostgresPayl Action: activitylog.ActivityLogEntryActionDeleted, Actor: authz.ActorFromContext(ctx).User, ResourceType: activityLogEntryResourceTypePostgres, - ResourceName: input.Name, + ResourceName: input.Postgres, EnvironmentName: new(input.EnvironmentName), TeamSlug: new(input.TeamSlug), }); err != nil { return nil, err } - return &DeletePostgresPayload{PostgresDeleted: new(true)}, nil + return &DeletePostgresBranchPayload{PostgresBranchDeleted: new(true)}, nil +} + +// ensureInstanceMayBeDeleted prevents an API request from marking the active +// instance as terminating. Pgrator independently blocks finalization as well. +func ensureInstanceMayBeDeleted(branch string, postgres *unstructured.Unstructured) error { + obj, err := kubernetes.ToConcrete[nais_io_v1.Postgres](postgres) + if err != nil { + return err + } + requested := obj.Spec.ActiveBranch + current := "" + if obj.Status != nil { + current = obj.Status.ActiveBranch + } + // Pgrator selects main when neither field is set. + if requested == "" && current == "" { + current = nais_io_v1.DefaultBranchName + } + if branch == requested || branch == current { + return apierror.Errorf("PostgresBranch %q is active and cannot be deleted", branch) + } + return nil } -func GetForWorkload(ctx context.Context, teamSlug slug.Slug, environmentName, clusterName string) (*PostgresInstance, error) { - if clusterName == "" { +func GetForWorkload(ctx context.Context, teamSlug slug.Slug, environmentName, postgresName string) (*PostgresBranch, error) { + if postgresName == "" { + return nil, nil + } + postgres, err := GetPostgres(ctx, teamSlug, environmentName, postgresName) + if err != nil { + return nil, err + } + if postgres.ActiveBranch == nil { return nil, nil } + return GetPostgresBranch(ctx, teamSlug, environmentName, postgresName, *postgres.ActiveBranch) +} - return GetZalandoPostgres(ctx, teamSlug, environmentName, clusterName) +// ListForWorkload resolves each Postgres use to the instance selected by that +// Postgres. A workload can use several databases, each with its own active instance. +func ListForWorkload(ctx context.Context, teamSlug slug.Slug, environmentName string, uses []liberatorv1.PostgresUse) ([]*PostgresBranch, error) { + instances := make([]*PostgresBranch, 0, len(uses)) + for _, use := range uses { + instance, err := GetForWorkload(ctx, teamSlug, environmentName, use.Name) + if err != nil { + return nil, err + } + if instance != nil { + instances = append(instances, instance) + } + } + slices.SortFunc(instances, func(a, b *PostgresBranch) int { + if a.Name != b.Name { + return cmp.Compare(a.Name, b.Name) + } + return cmp.Compare(a.PostgresName, b.PostgresName) + }) + return instances, nil } -func ListForTeam(ctx context.Context, teamSlug slug.Slug, page *pagination.Pagination, orderBy *PostgresInstanceOrder, filter *PostgresInstanceFilter) (*PostgresInstanceConnection, error) { +func ListForTeam(ctx context.Context, teamSlug slug.Slug, page *pagination.Pagination, orderBy *PostgresBranchOrder, filter *PostgresBranchFilter) (*PostgresBranchConnection, error) { all := ListAllForTeam(ctx, teamSlug, filter) if orderBy == nil { - orderBy = &PostgresInstanceOrder{ - Field: PostgresInstanceOrderFieldName, + orderBy = &PostgresBranchOrder{ + Field: PostgresBranchOrderFieldName, Direction: model.OrderDirectionAsc, } } - return SortFilterPostgresInstance.PaginatedList(ctx, all, page, orderBy.Field, orderBy.Direction, filter), nil + return SortFilterPostgresBranch.PaginatedList(ctx, all, page, orderBy.Field, orderBy.Direction, filter), nil } -func ListAllForTeam(ctx context.Context, teamSlug slug.Slug, filter *PostgresInstanceFilter) []*PostgresInstance { - all := fromContext(ctx).zalandoPostgresWatcher.GetByNamespace(teamSlug.String()) +func ListAllForTeam(ctx context.Context, teamSlug slug.Slug, filter *PostgresBranchFilter) []*PostgresBranch { + all := fromContext(ctx).postgresBranchWatcher.GetByNamespace(teamSlug.String()) return watcher.Objects(all) } func CountForTeam(ctx context.Context, teamSlug slug.Slug) int { - return len(fromContext(ctx).zalandoPostgresWatcher.GetByNamespace(teamSlug.String())) + return len(fromContext(ctx).postgresBranchWatcher.GetByNamespace(teamSlug.String())) } -func GetZalandoPostgresByIdent(ctx context.Context, id ident.Ident) (*PostgresInstance, error) { - teamSlug, environmentName, clusterName, err := parseIdent(id) +func GetPostgresBranchByIdent(ctx context.Context, id ident.Ident) (*PostgresBranch, error) { + teamSlug, environmentName, postgresName, branchName, err := parsePostgresBranchIdent(id) if err != nil { return nil, err } - return GetZalandoPostgres(ctx, teamSlug, environmentName, clusterName) + return GetPostgresBranch(ctx, teamSlug, environmentName, postgresName, branchName) } -func GetZalandoPostgres(ctx context.Context, teamSlug slug.Slug, environmentName string, clusterName string) (*PostgresInstance, error) { - return fromContext(ctx).zalandoPostgresWatcher.Get(environmentName, teamSlug.String(), clusterName) +func GetPostgresByIdent(ctx context.Context, id ident.Ident) (*Postgres, error) { + teamSlug, environmentName, name, err := parseAccessIdent(id) + if err != nil { + return nil, err + } + return GetPostgres(ctx, teamSlug, environmentName, name) } -func GetAuditURL(ctx context.Context, audit *PostgresInstanceAudit) (*string, error) { - if audit == nil || !audit.Enabled { - return nil, nil +func GetPostgresAccessByIdent(ctx context.Context, id ident.Ident) (*PostgresAccess, error) { + teamSlug, environmentName, name, err := parseAccessIdent(id) + if err != nil { + return nil, err } - auditProjectID, location := GetAuditLogConfig(ctx) - if auditProjectID == "" || location == "" { - return nil, nil + return GetPostgresAccess(ctx, name, teamSlug, environmentName) +} + +func postgresAccessGVR() schema.GroupVersionResource { + return schema.GroupVersionResource{Group: "nais.io", Version: "v1", Resource: "postgresaccesses"} +} + +// GetPostgresAccess returns a personal PostgresAccess status. Connection +// credentials are deliberately available only through GetPostgresAccessConnection. +func GetPostgresAccess(ctx context.Context, name string, teamSlug slug.Slug, environmentName string) (*PostgresAccess, error) { + if err := authz.CanGrantPostgresAccess(ctx, teamSlug); err != nil { + return nil, err } - teamEnv, err := team.GetTeamEnvironment(ctx, audit.TeamSlug, audit.EnvironmentName) + u, err := getPostgresAccessResource(ctx, name, teamSlug, environmentName) if err != nil { - return nil, fmt.Errorf("failed to get team environment for audit URL (team=%s, env=%s): %w", audit.TeamSlug, audit.EnvironmentName, err) + return nil, err } - if teamEnv.GCPProjectID == nil || *teamEnv.GCPProjectID == "" { - return nil, nil + + access, err := toPostgresAccess(u, teamSlug, environmentName) + if err != nil { + return nil, err } - databaseProjectID := *teamEnv.GCPProjectID - databaseID := fmt.Sprintf("%s:%s", databaseProjectID, audit.InstanceName) - query := fmt.Sprintf("labels.databaseId=\"%s\"", databaseID) - storageScope := fmt.Sprintf("storage,projects/%s/locations/%s/buckets/%s-%s/views/_AllLogs", auditProjectID, location, audit.TeamSlug.String(), audit.EnvironmentName) - logURL := fmt.Sprintf("https://console.cloud.google.com/logs/query;query=%s;storageScope=%s?project=%s", - url.QueryEscape(query), - url.QueryEscape(storageScope), - databaseProjectID, - ) - return &logURL, nil + return access, nil } -func GrantZalandoPostgresAccess(ctx context.Context, input GrantPostgresAccessInput) error { - err := input.Validate(ctx) - if err != nil { - return err +func GetPostgresAccessConnection(ctx context.Context, input PostgresAccessConnectionInput) (*PostgresAccessConnectionDetails, error) { + if err := input.Validate(ctx); err != nil { + return nil, err + } + if err := authz.CanGrantPostgresAccess(ctx, input.TeamSlug); err != nil { + return nil, err } - namespace := fmt.Sprintf("pg-%s", input.TeamSlug.String()) - name, err := resourceNamer(input.TeamSlug, input.Grantee, input.ClusterName) + access, err := getPostgresAccessResource(ctx, input.Name, input.TeamSlug, input.EnvironmentName) if err != nil { - return err + return nil, err } - annotations := make(map[string]string) - d, err := time.ParseDuration(input.Duration) + resource, err := kubernetes.ToConcrete[nais_io_v1.PostgresAccess](access) if err != nil { - return fmt.Errorf("parsing TTL: %w", err) + return nil, fmt.Errorf("converting PostgresAccess %q: %w", input.Name, err) + } + actor := authz.ActorFromContext(ctx) + if actor == nil || resource.Spec.Username == "" || actor.User.Identity() != resource.Spec.Username { + return nil, authz.ErrUnauthorized } - until := time.Now().Add(d) - - labels := make(map[string]string) - labels["euthanaisa.nais.io/kill-after"] = strconv.FormatInt(until.Unix(), 10) - labels["postgres.data.nais.io/name"] = input.ClusterName - err = createRole(ctx, input, name, namespace, annotations, labels) + connection, credentialSecretName, err := postgresAccessConnectionDetails(access, time.Now()) if err != nil { - return err + return nil, err } - err = createRoleBinding(ctx, input, name, namespace, annotations, labels) - if err != nil { - return err + if err := loadPostgresAccessConnection(ctx, access, input, connection, credentialSecretName); err != nil { + return nil, err } - return activitylog.Create(ctx, activitylog.CreateInput{ - Action: activityLogEntryActionGrantAccess, - Actor: authz.ActorFromContext(ctx).User, + if err := activitylog.Create(ctx, activitylog.CreateInput{ + Action: activityLogEntryActionGetPersonalAccessConnection, + Actor: actor.User, ResourceType: activityLogEntryResourceTypePostgres, - ResourceName: input.ClusterName, + ResourceName: input.Name, EnvironmentName: new(input.EnvironmentName), TeamSlug: new(input.TeamSlug), - Data: PostgresGrantAccessActivityLogEntryData{ - Grantee: input.Grantee, - Until: until, - }, - }) + Data: PostgresPersonalAccessConnectionActivityLogEntryData{}, + }); err != nil { + return nil, err + } + + return connection, nil } -func createRoleBinding(ctx context.Context, input GrantPostgresAccessInput, name string, namespace string, annotations map[string]string, labels map[string]string) error { - gvr := schema.GroupVersionResource{ - Group: "rbac.authorization.k8s.io", - Version: "v1", - Resource: "rolebindings", +func getPostgresAccessResource(ctx context.Context, name string, teamSlug slug.Slug, environmentName string) (*unstructured.Unstructured, error) { + accessClient, err := fromContext(ctx).postgresBranchWatcher.SystemAuthenticatedClient(ctx, environmentName, watcher.WithImpersonatedClientGVR(postgresAccessGVR())) + if err != nil { + return nil, fmt.Errorf("creating postgresaccess client: %w", err) } - client, err := fromContext(ctx).zalandoPostgresWatcher.SystemAuthenticatedClient(ctx, input.EnvironmentName, watcher.WithImpersonatedClientGVR(gvr)) + u, err := accessClient.Namespace(teamSlug.String()).Get(ctx, name, metav1.GetOptions{}) if err != nil { - return err + if k8serrors.IsNotFound(err) { + return nil, apierror.Errorf("PostgresAccess %q not found", name) + } + return nil, fmt.Errorf("getting PostgresAccess %q: %w", name, err) } - namespacedClient := client.Namespace(namespace) + return u, nil +} - res := &unstructured.Unstructured{} - res.SetAPIVersion(gvr.GroupVersion().String()) - res.SetKind("RoleBinding") - res.SetName(name) - res.SetNamespace(namespace) - res.SetAnnotations(kubernetes.WithCommonAnnotations(annotations, authz.ActorFromContext(ctx).User.Identity())) - res.SetLabels(labels) - kubernetes.SetManagedByConsoleLabel(res) +func postgresAccessConnectionDetails(access *unstructured.Unstructured, now time.Time) (*PostgresAccessConnectionDetails, string, error) { + obj, err := kubernetes.ToConcrete[nais_io_v1.PostgresAccess](access) + if err != nil { + return nil, "", fmt.Errorf("converting PostgresAccess %q: %w", access.GetName(), err) + } + if obj.Spec.ExpiresAt.IsZero() { + return nil, "", apierror.Errorf("PostgresAccess %q has an invalid expiry", access.GetName()) + } + if !obj.Spec.ExpiresAt.After(now) { + return nil, "", apierror.Errorf("PostgresAccess %q has expired", access.GetName()) + } + if obj.Status == nil || !slices.ContainsFunc(obj.Status.Conditions, func(c metav1.Condition) bool { + return c.Type == "Ready" && c.Status == metav1.ConditionTrue + }) { + return nil, "", apierror.Errorf("PostgresAccess %q is not ready", access.GetName()) + } - res.Object["roleRef"] = map[string]any{ - "apiGroup": "rbac.authorization.k8s.io", - "kind": "Role", - "name": name, + if access.GetDeletionTimestamp() != nil { + return nil, "", apierror.Errorf("PostgresAccess %q is not ready", access.GetName()) + } + if obj.Status.RelayAccess == "" || obj.Status.TokenSecret == "" || obj.Status.DatabaseRole == "" || obj.Status.ServerName == "" || obj.Status.ServerCASecret == "" { + return nil, "", apierror.Errorf("PostgresAccess %q is not ready", access.GetName()) } + return &PostgresAccessConnectionDetails{}, obj.Status.TokenSecret, nil +} - res.Object["subjects"] = []any{ - map[string]any{ - "kind": "User", - "name": input.Grantee, - }, +func toPostgresAccess(u *unstructured.Unstructured, teamSlug slug.Slug, environmentName string) (*PostgresAccess, error) { + name := u.GetName() + obj, err := kubernetes.ToConcrete[nais_io_v1.PostgresAccess](u) + if err != nil { + return nil, fmt.Errorf("converting PostgresAccess %q: %w", name, err) + } + expiresAt := obj.Spec.ExpiresAt.Time + levelStr := obj.Spec.AccessLevel + level := PostgresAccessLevel(strings.ToUpper(string(levelStr))) + if !level.IsValid() { + return nil, fmt.Errorf("invalid accessLevel %q for PostgresAccess %q", levelStr, name) + } + + state, message := postgresAccessState(obj, expiresAt) + + relayName := "" + if obj.Status != nil { + relayName = obj.Status.RelayAccess + } + + return &PostgresAccess{ + Name: name, + TeamSlug: teamSlug, + EnvironmentName: environmentName, + PostgresBranchName: obj.Spec.PostgresBranch, + Username: obj.Spec.Username, + AccessLevel: level, + ExpiresAt: expiresAt, + State: state, + Message: strPtr(message), + RelayAccess: strPtr(relayName), + }, nil +} + +func strPtr(s string) *string { + if s == "" { + return nil } + return &s +} - return createOrUpdateResource(ctx, res, namespacedClient) +func postgresAccessState(obj *nais_io_v1.PostgresAccess, expiresAt time.Time) (PostgresAccessState, string) { + if !expiresAt.IsZero() && expiresAt.Before(time.Now()) { + return PostgresAccessStateExpired, "access has expired" + } + if obj.Status == nil { + return PostgresAccessStatePending, "waiting for controller" + } + for _, condition := range obj.Status.Conditions { + if condition.Type != "Ready" { + continue + } + if condition.Status == metav1.ConditionTrue { + return PostgresAccessStateReady, condition.Message + } + if condition.Reason == "UnsupportedAccessLevel" { + return PostgresAccessStateFailed, condition.Message + } + return PostgresAccessStatePending, condition.Message + } + return PostgresAccessStatePending, "waiting for controller" } -func createRole(ctx context.Context, input GrantPostgresAccessInput, name string, namespace string, annotations map[string]string, labels map[string]string) error { - gvr := schema.GroupVersionResource{ - Group: "rbac.authorization.k8s.io", - Version: "v1", - Resource: "roles", +func GetReadyPostgresBranch(ctx context.Context, teamSlug slug.Slug, environmentName, postgresName, branchName string) (*PostgresBranch, error) { + instance, err := GetPostgresBranch(ctx, teamSlug, environmentName, postgresName, branchName) + if err != nil { + return nil, err + } + if instance.State != PostgresBranchStateAvailable { + return instance, nil + } + if _, err := GetPostgres(ctx, teamSlug, environmentName, instance.PostgresName); err != nil { + return nil, fmt.Errorf("getting Postgres %q for branch %q: %w", instance.PostgresName, branchName, err) + } + // The pgrator reconciliation condition reflects the CNPG phase, but must be + // corroborated with CNPG's own Ready condition before issuing access. + client, err := fromContext(ctx).postgresBranchWatcher.SystemAuthenticatedClient(ctx, environmentName, watcher.WithImpersonatedClientGVR(schema.GroupVersionResource{Group: "postgresql.cnpg.io", Version: "v1", Resource: "clusters"})) + if err != nil { + return nil, err + } + cluster, err := client.Namespace(teamSlug.String()).Get(ctx, nais_io_v1.CNPGClusterName(nais_io_v1.PostgresBranchObjectName(postgresName, branchName)), metav1.GetOptions{}) + if k8serrors.IsNotFound(err) { + instance.State = PostgresBranchStateProgressing + return instance, nil + } + if err != nil { + return nil, fmt.Errorf("getting CNPG Cluster for PostgresBranch %q: %w", branchName, err) + } + conditions, found, err := unstructured.NestedSlice(cluster.Object, "status", "conditions") + if err != nil { + return nil, err + } + if !found { + instance.State = PostgresBranchStateProgressing + return instance, nil } + for _, raw := range conditions { + condition, ok := raw.(map[string]any) + if ok && condition["type"] == "Ready" && condition["status"] == "True" { + return instance, nil + } + } + instance.State = PostgresBranchStateProgressing + return instance, nil +} - client, err := fromContext(ctx).zalandoPostgresWatcher.SystemAuthenticatedClient(ctx, input.EnvironmentName, watcher.WithImpersonatedClientGVR(gvr)) +func GetPostgresBranch(ctx context.Context, teamSlug slug.Slug, environmentName, postgresName, branchName string) (*PostgresBranch, error) { + branch, err := fromContext(ctx).postgresBranchWatcher.Get(environmentName, teamSlug.String(), nais_io_v1.PostgresBranchObjectName(postgresName, branchName)) + if errors.Is(err, &watcher.ErrorNotFound{}) { + return nil, &watcher.ErrorNotFound{Cluster: environmentName, Namespace: teamSlug.String(), Name: postgresName + "/" + branchName} + } if err != nil { - return err + return nil, err } - namespacedClient := client.Namespace(namespace) + if branch.PostgresName != postgresName || branch.Name != branchName { + return nil, &watcher.ErrorNotFound{Cluster: environmentName, Namespace: teamSlug.String(), Name: postgresName + "/" + branchName} + } + return branch, nil +} - res := &unstructured.Unstructured{} - res.SetAPIVersion(gvr.GroupVersion().String()) - res.SetKind("Role") - res.SetName(name) - res.SetNamespace(namespace) - res.SetAnnotations(kubernetes.WithCommonAnnotations(annotations, authz.ActorFromContext(ctx).User.Identity())) - res.SetLabels(labels) - kubernetes.SetManagedByConsoleLabel(res) +// GetPostgresBranchByObjectName resolves the internal object name from PostgresAccess. +func GetPostgresBranchByObjectName(ctx context.Context, teamSlug slug.Slug, environmentName, objectName string) (*PostgresBranch, error) { + branch, err := fromContext(ctx).postgresBranchWatcher.Get(environmentName, teamSlug.String(), objectName) + if errors.Is(err, &watcher.ErrorNotFound{}) { + return nil, &watcher.ErrorNotFound{Cluster: environmentName, Namespace: teamSlug.String(), Name: "Postgres branch"} + } + if err != nil { + return nil, err + } + if nais_io_v1.PostgresBranchObjectName(branch.PostgresName, branch.Name) != objectName { + return nil, &watcher.ErrorNotFound{Cluster: environmentName, Namespace: teamSlug.String(), Name: "Postgres branch"} + } + return branch, nil +} - res.Object["rules"] = []any{ - map[string]any{ - "apiGroups": []any{""}, - "resources": []any{"pods"}, - "verbs": []any{"get", "list", "watch"}, - "resourceNames": []any{ - fmt.Sprintf("%s-0", input.ClusterName), - fmt.Sprintf("%s-1", input.ClusterName), - fmt.Sprintf("%s-2", input.ClusterName), - }, - }, - map[string]any{ - "apiGroups": []any{""}, - "resources": []any{"pods/portforward"}, - "verbs": []any{"get", "list", "watch", "create"}, - "resourceNames": []any{ - fmt.Sprintf("%s-0", input.ClusterName), - fmt.Sprintf("%s-1", input.ClusterName), - fmt.Sprintf("%s-2", input.ClusterName), - }, - }, +func ListForPostgres(ctx context.Context, pg *Postgres, page *pagination.Pagination, orderBy *PostgresBranchOrder) *PostgresBranchConnection { + all := make([]*PostgresBranch, 0) + for _, branch := range ListAllForTeam(ctx, pg.TeamSlug, nil) { + if branch.EnvironmentName == pg.EnvironmentName && branch.PostgresName == pg.Name { + all = append(all, branch) + } + } + if orderBy == nil { + orderBy = &PostgresBranchOrder{Field: PostgresBranchOrderFieldName, Direction: model.OrderDirectionAsc} } + return SortFilterPostgresBranch.PaginatedList(ctx, all, page, orderBy.Field, orderBy.Direction, nil) +} - return createOrUpdateResource(ctx, res, namespacedClient) +func GetPostgres(ctx context.Context, teamSlug slug.Slug, environmentName, name string) (*Postgres, error) { + client, err := fromContext(ctx).postgresBranchWatcher.SystemAuthenticatedClient(ctx, environmentName, watcher.WithImpersonatedClientGVR(schema.GroupVersionResource{Group: "nais.io", Version: "v1", Resource: "postgres"})) + if err != nil { + return nil, err + } + obj, err := client.Namespace(teamSlug.String()).Get(ctx, name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return toPostgres(obj, environmentName) } -func createOrUpdateResource(ctx context.Context, res *unstructured.Unstructured, client dynamic.ResourceInterface) error { - _, err := client.Create(ctx, res, metav1.CreateOptions{}) +const ( + postgresAccessAPIVersion = "nais.io/v1" + defaultPostgresAccessTTL = time.Hour + maxPostgresAccessTTL = time.Hour +) + +func CreatePostgresAccess(ctx context.Context, input CreatePostgresAccessInput) (*CreatePostgresAccessPayload, error) { + if err := input.Validate(ctx); err != nil { + return nil, err + } + accessTTL, err := input.accessTTL() if err != nil { - if k8serrors.IsAlreadyExists(err) { - _, err = client.Update(ctx, res, metav1.UpdateOptions{}) - if err != nil { - return err - } - return nil - } - return err + return nil, err } - return nil + + client, err := fromContext(ctx).postgresBranchWatcher.SystemAuthenticatedClient(ctx, input.EnvironmentName, watcher.WithImpersonatedClientGVR(postgresAccessGVR())) + if err != nil { + return nil, err + } + + expiresAt := time.Now().Add(accessTTL) + name := fmt.Sprintf("postgres-access-%s", uuid.NewString()[:8]) + res := newPostgresAccessResource(input, authz.ActorFromContext(ctx).User.Identity(), name, expiresAt) + + if _, err := client.Namespace(input.TeamSlug.String()).Create(ctx, res, metav1.CreateOptions{}); err != nil { + return nil, err + } + + if err := activitylog.Create(ctx, activitylog.CreateInput{ + Action: activityLogEntryActionCreatePersonalAccess, + Actor: authz.ActorFromContext(ctx).User, + ResourceType: activityLogEntryResourceTypePostgres, + ResourceName: input.Postgres, + EnvironmentName: new(input.EnvironmentName), + TeamSlug: new(input.TeamSlug), + Data: PostgresPersonalAccessCreatedActivityLogEntryData{ + Username: authz.ActorFromContext(ctx).User.Identity(), + AccessLevel: new(input.AccessLevel), + ExpiresAt: expiresAt, + Reason: input.Reason, + }, + }); err != nil { + return nil, err + } + + return &CreatePostgresAccessPayload{Name: name, ExpiresAt: expiresAt}, nil } -func resourceNamer(teamSlug slug.Slug, grantee string, name string) (string, error) { - hasher := crc32.NewIEEE() - _, err := fmt.Fprintf(hasher, "%s-%s-%s", teamSlug.String(), grantee, name) +func (i CreatePostgresAccessInput) accessTTL() (time.Duration, error) { + if i.TTL == "" { + return defaultPostgresAccessTTL, nil + } + + ttl, err := time.ParseDuration(i.TTL) if err != nil { - return "", err + return 0, fmt.Errorf("TTL must be a Go duration, for example %q", "4h") + } + if ttl <= 0 { + return 0, fmt.Errorf("TTL must be positive") + } + if ttl > maxPostgresAccessTTL { + return 0, fmt.Errorf("TTL cannot exceed %s", maxPostgresAccessTTL) } - hashStr := fmt.Sprintf("%08x", hasher.Sum32()) - return fmt.Sprintf("pg-grant-%s", hashStr), nil + return ttl, nil } -func WorkloadsForInstance(ctx context.Context, teamSlug slug.Slug, environmentName, clusterName string) []workload.Workload { +func newPostgresAccessResource(input CreatePostgresAccessInput, username, name string, expiresAt time.Time) *unstructured.Unstructured { + res := &unstructured.Unstructured{} + res.SetAPIVersion(postgresAccessAPIVersion) + res.SetKind("PostgresAccess") + res.SetName(name) + res.SetNamespace(input.TeamSlug.String()) + res.SetAnnotations(kubernetes.WithCommonAnnotations(nil, username)) + kubernetes.SetManagedByConsoleLabel(res) + res.Object["spec"] = map[string]any{ + "postgresBranch": nais_io_v1.PostgresBranchObjectName(input.Postgres, input.Branch), + "username": username, + "accessLevel": input.AccessLevel.CRDValue(), + "expiresAt": expiresAt.Format(time.RFC3339), + } + return res +} + +func WorkloadsForInstance(ctx context.Context, teamSlug slug.Slug, environmentName, postgresName, branchName string) []workload.Workload { + instance, err := GetPostgresBranch(ctx, teamSlug, environmentName, postgresName, branchName) + if err != nil { + return nil + } + postgres, err := GetPostgres(ctx, teamSlug, environmentName, instance.PostgresName) + if err != nil || postgres.ActiveBranch == nil || *postgres.ActiveBranch != branchName { + return nil + } apps := application.ListAllForTeamInEnvironment(ctx, teamSlug, environmentName) jobs := job.ListAllForTeamInEnvironment(ctx, teamSlug, environmentName) - ret := make([]workload.Workload, 0) for _, app := range apps { - if app.Spec != nil && app.Spec.Postgres != nil && app.Spec.Postgres.ClusterName == clusterName { + if app.Spec != nil && app.Spec.Uses != nil && slices.ContainsFunc(app.Spec.Uses.Postgres, func(use liberatorv1.PostgresUse) bool { return use.Name == instance.PostgresName }) { ret = append(ret, app) } } - for _, j := range jobs { - if j.Spec != nil && j.Spec.Postgres != nil && j.Spec.Postgres.ClusterName == clusterName { + if j.Spec != nil && j.Spec.Uses != nil && slices.ContainsFunc(j.Spec.Uses.Postgres, func(use liberatorv1.PostgresUse) bool { return use.Name == instance.PostgresName }) { ret = append(ret, j) } } diff --git a/internal/persistence/postgres/queries_test.go b/internal/persistence/postgres/queries_test.go new file mode 100644 index 000000000..f7077fe9c --- /dev/null +++ b/internal/persistence/postgres/queries_test.go @@ -0,0 +1,212 @@ +package postgres + +import ( + "strings" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/nais/api/internal/slug" + "github.com/nais/pgrator/pkg/api" + nais_io_v1 "github.com/nais/pgrator/pkg/api/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func TestNewPostgresAccessResource(t *testing.T) { + expiresAt := time.Date(2026, time.September, 17, 12, 0, 0, 0, time.UTC) + resource := newPostgresAccessResource(CreatePostgresAccessInput{ + Postgres: "orders", Branch: "main", + TeamSlug: slug.Slug("team-a"), + EnvironmentName: "dev", + AccessLevel: PostgresAccessLevelReadWrite, + }, "user@example.com", "postgres-access-12345678", expiresAt) + + if got, want := resource.GetAPIVersion(), "nais.io/v1"; got != want { + t.Errorf("apiVersion = %q, want %q", got, want) + } + if got, want := resource.GetKind(), "PostgresAccess"; got != want { + t.Errorf("kind = %q, want %q", got, want) + } + if got, want := resource.GetName(), "postgres-access-12345678"; got != want { + t.Errorf("name = %q, want %q", got, want) + } + if got, want := resource.GetNamespace(), "team-a"; got != want { + t.Errorf("namespace = %q, want %q", got, want) + } + + spec, found, err := unstructured.NestedMap(resource.Object, "spec") + if err != nil || !found { + t.Fatalf("spec = (%v, %t, %v), want a spec", spec, found, err) + } + wantSpec := map[string]any{ + "postgresBranch": nais_io_v1.PostgresBranchObjectName("orders", "main"), + "username": "user@example.com", + "accessLevel": "readwrite", + "expiresAt": "2026-09-17T12:00:00Z", + } + if diff := cmp.Diff(wantSpec, spec); diff != "" { + t.Errorf("spec mismatch (-want +got):\n%s", diff) + } +} + +func TestCreatePostgresAccessTTL(t *testing.T) { + tests := []struct { + name string + ttl string + want time.Duration + wantErr string + }{ + {name: "default", want: time.Hour}, + {name: "requested", ttl: "30m", want: 30 * time.Minute}, + {name: "maximum", ttl: "1h", want: time.Hour}, + {name: "invalid", ttl: "tomorrow", wantErr: "TTL must be a Go duration"}, + {name: "zero", ttl: "0s", wantErr: "TTL must be positive"}, + {name: "too long", ttl: "1h1m", wantErr: "TTL cannot exceed 1h0m0s"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := (CreatePostgresAccessInput{TTL: tt.ttl}).accessTTL() + if tt.wantErr != "" { + if err == nil || !strings.Contains(err.Error(), tt.wantErr) { + t.Errorf("accessTTL() error = %v, want containing %q", err, tt.wantErr) + } + return + } + if err != nil { + t.Fatalf("accessTTL() error = %v", err) + } + if got != tt.want { + t.Errorf("accessTTL() = %s, want %s", got, tt.want) + } + }) + } +} + +func TestPostgresAccessState(t *testing.T) { + future := time.Now().Add(time.Hour) + past := time.Now().Add(-time.Hour) + + tests := []struct { + name string + expiresAt time.Time + status *nais_io_v1.PostgresAccessStatus + wantState PostgresAccessState + wantMsg string + }{ + { + name: "expired", + expiresAt: past, + wantState: PostgresAccessStateExpired, + wantMsg: "access has expired", + }, + { + name: "pending without status", + expiresAt: future, + wantState: PostgresAccessStatePending, + wantMsg: "waiting for controller", + }, + { + name: "ready", + expiresAt: future, + status: &nais_io_v1.PostgresAccessStatus{BaseStatus: api.BaseStatus{Conditions: []metav1.Condition{{Type: "Ready", Status: metav1.ConditionTrue, Message: "database role and relay mapping are ready"}}}}, + wantState: PostgresAccessStateReady, + wantMsg: "database role and relay mapping are ready", + }, + { + name: "failed unsupported access level", + expiresAt: future, + status: &nais_io_v1.PostgresAccessStatus{BaseStatus: api.BaseStatus{Conditions: []metav1.Condition{{Type: "Ready", Status: metav1.ConditionFalse, Reason: "UnsupportedAccessLevel", Message: "readwritecreate requires an instance initialized with the app_readwritecreate group role"}}}}, + wantState: PostgresAccessStateFailed, + wantMsg: "readwritecreate requires an instance initialized with the app_readwritecreate group role", + }, + { + name: "pending waiting on relay", + expiresAt: future, + status: &nais_io_v1.PostgresAccessStatus{BaseStatus: api.BaseStatus{Conditions: []metav1.Condition{{Type: "Ready", Status: metav1.ConditionFalse, Message: "waiting for relay"}}}}, + wantState: PostgresAccessStatePending, + wantMsg: "waiting for relay", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotState, gotMsg := postgresAccessState(&nais_io_v1.PostgresAccess{Status: tt.status}, tt.expiresAt) + if gotState != tt.wantState { + t.Errorf("state = %q, want %q", gotState, tt.wantState) + } + if gotMsg != tt.wantMsg { + t.Errorf("message = %q, want %q", gotMsg, tt.wantMsg) + } + }) + } +} + +func TestPostgresAccessConnectionDetails(t *testing.T) { + now := time.Date(2026, time.September, 17, 12, 0, 0, 0, time.UTC) + ready := func() *unstructured.Unstructured { + return &unstructured.Unstructured{Object: map[string]any{ + "metadata": map[string]any{"name": "access"}, + "spec": map[string]any{"expiresAt": "2026-09-17T13:00:00Z"}, + "status": map[string]any{ + "databaseRole": "personal-role", + "relayAccess": "access", + "tokenSecret": "access-relay-token", + "serverName": "pg-orders-rw.team.svc.cluster.local", + "serverCASecret": "pg-orders-ca", + "conditions": []any{map[string]any{"type": "Ready", "status": "True"}}, + }, + }} + } + + tests := []struct { + name string + edit func(*unstructured.Unstructured) + want string + }{ + {name: "ready"}, + {name: "expired", edit: func(u *unstructured.Unstructured) { + _ = unstructured.SetNestedField(u.Object, "2026-09-17T12:00:00Z", "spec", "expiresAt") + }, want: "expired"}, + {name: "not ready", edit: func(u *unstructured.Unstructured) { + _ = unstructured.SetNestedField(u.Object, []any{map[string]any{"type": "Ready", "status": "False"}}, "status", "conditions") + }, want: "not ready"}, + {name: "missing token secret name", edit: func(u *unstructured.Unstructured) { + unstructured.RemoveNestedField(u.Object, "status", "tokenSecret") + }, want: "not ready"}, + {name: "missing relay mapping", edit: func(u *unstructured.Unstructured) { + unstructured.RemoveNestedField(u.Object, "status", "relayAccess") + }, want: "not ready"}, + {name: "missing server name", edit: func(u *unstructured.Unstructured) { + unstructured.RemoveNestedField(u.Object, "status", "serverName") + }, want: "not ready"}, + {name: "missing server CA reference", edit: func(u *unstructured.Unstructured) { + unstructured.RemoveNestedField(u.Object, "status", "serverCASecret") + }, want: "not ready"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + u := ready() + if tt.edit != nil { + tt.edit(u) + } + got, secretName, err := postgresAccessConnectionDetails(u, now) + if tt.want != "" { + if err == nil || !strings.Contains(err.Error(), tt.want) { + t.Fatalf("error = %v, want %q", err, tt.want) + } + return + } + if err != nil { + t.Fatalf("postgresAccessConnectionDetails: %v", err) + } + if secretName != "access-relay-token" { + t.Errorf("secret name = %q", secretName) + } + if got == nil { + t.Fatal("connection is nil") + } + }) + } +} diff --git a/internal/persistence/postgres/search.go b/internal/persistence/postgres/search.go index fe5128deb..1c2457da2 100644 --- a/internal/persistence/postgres/search.go +++ b/internal/persistence/postgres/search.go @@ -9,14 +9,14 @@ import ( "github.com/nais/api/internal/slug" ) -func AddSearchZalandoPostgres(client search.Client, watcher *watcher.Watcher[*PostgresInstance]) { - createIdent := func(env string, obj *PostgresInstance) ident.Ident { - return newIdent(slug.Slug(obj.GetNamespace()), env, obj.GetName()) +func AddSearchPostgresBranch(client search.Client, watcher *watcher.Watcher[*PostgresBranch]) { + createIdent := func(env string, obj *PostgresBranch) ident.Ident { + return newIdent(slug.Slug(obj.GetNamespace()), env, obj.PostgresName, obj.Name) } gbi := func(ctx context.Context, id ident.Ident) (search.SearchNode, error) { - return GetZalandoPostgresByIdent(ctx, id) + return GetPostgresBranchByIdent(ctx, id) } - client.AddClient("POSTGRES", search.NewK8sSearch("POSTGRES", watcher, gbi, createIdent)) + client.AddClient("POSTGRES_BRANCH", search.NewK8sSearch("POSTGRES_BRANCH", watcher, gbi, createIdent)) } diff --git a/internal/persistence/postgres/sortfilter.go b/internal/persistence/postgres/sortfilter.go index 57a580b70..437998e65 100644 --- a/internal/persistence/postgres/sortfilter.go +++ b/internal/persistence/postgres/sortfilter.go @@ -9,17 +9,20 @@ import ( "github.com/nais/api/internal/graph/sortfilter" ) -var SortFilterPostgresInstance = sortfilter.New[*PostgresInstance, PostgresInstanceOrderField, *PostgresInstanceFilter]() +var SortFilterPostgresBranch = sortfilter.New[*PostgresBranch, PostgresBranchOrderField, *PostgresBranchFilter]() func init() { - SortFilterPostgresInstance.RegisterSort("NAME", func(ctx context.Context, a, b *PostgresInstance) int { - return strings.Compare(a.GetName(), b.GetName()) + SortFilterPostgresBranch.RegisterSort("NAME", func(ctx context.Context, a, b *PostgresBranch) int { + if a.Name != b.Name { + return strings.Compare(a.Name, b.Name) + } + return strings.Compare(a.PostgresName, b.PostgresName) }, "ENVIRONMENT") - SortFilterPostgresInstance.RegisterSort("ENVIRONMENT", func(ctx context.Context, a, b *PostgresInstance) int { + SortFilterPostgresBranch.RegisterSort("ENVIRONMENT", func(ctx context.Context, a, b *PostgresBranch) int { return strings.Compare(a.EnvironmentName, b.EnvironmentName) }, "NAME") - SortFilterPostgresInstance.RegisterFilter(func(ctx context.Context, v *PostgresInstance, filter *PostgresInstanceFilter) bool { + SortFilterPostgresBranch.RegisterFilter(func(ctx context.Context, v *PostgresBranch, filter *PostgresBranchFilter) bool { if filter.Name != "" { if !strings.Contains(strings.ToLower(v.Name), strings.ToLower(filter.Name)) { return false @@ -38,18 +41,6 @@ func init() { } } - if filter.HighAvailability != nil { - if v.HighAvailability != *filter.HighAvailability { - return false - } - } - - if len(filter.MajorVersions) > 0 { - if !slices.Contains(filter.MajorVersions, v.MajorVersion) { - return false - } - } - if !model.MatchesLabelFilters(v.Labels, filter.Labels) { return false } diff --git a/internal/search/k8s_searcher.go b/internal/search/k8s_searcher.go index d9bccde0c..9dc59b74d 100644 --- a/internal/search/k8s_searcher.go +++ b/internal/search/k8s_searcher.go @@ -51,7 +51,7 @@ func (k K8sSearch[T]) ReIndex(ctx context.Context) []Document { docs = append(docs, Document{ ID: k.newIdent(obj.Cluster, obj.Obj).String(), Kind: k.kind.String(), - Name: obj.GetName(), + Name: searchName(obj.Obj), Team: team.String(), }) } @@ -73,12 +73,21 @@ func (k K8sSearch[T]) upsert(indexer Indexer) func(string, T) { indexer.Upsert(Document{ ID: k.newIdent(env, obj).String(), Kind: k.kind.String(), - Name: obj.GetName(), + Name: searchName(obj), Team: team.String(), }) } } +// searchName lets resources whose Kubernetes name is internal expose a useful +// display name without changing the name used by the informer. +func searchName(obj watcher.Object) string { + if named, ok := obj.(interface{ SearchName() string }); ok { + return named.SearchName() + } + return obj.GetName() +} + func (k K8sSearch[T]) onRemove(indexer Indexer) func(string, T) { return func(env string, obj T) { indexer.Remove(k.newIdent(env, obj)) diff --git a/internal/thirdparty/promclient/client.go b/internal/thirdparty/promclient/client.go index 49041aba8..1b3f348e9 100644 --- a/internal/thirdparty/promclient/client.go +++ b/internal/thirdparty/promclient/client.go @@ -126,7 +126,7 @@ func (c *RealClient) QueryRange(ctx context.Context, environment string, query s } func (c *RealClient) Rules(ctx context.Context, environment string, teamSlug slug.Slug) (promv1.RulesResult, error) { - res, err := c.mimirRules.Rules(ctx) + res, err := c.mimirRules.Rules(ctx, nil) if err != nil { return promv1.RulesResult{}, err } diff --git a/internal/workload/secret/queries.go b/internal/workload/secret/queries.go index 96a98c44d..e6f191cb5 100644 --- a/internal/workload/secret/queries.go +++ b/internal/workload/secret/queries.go @@ -731,6 +731,18 @@ func ViewSecretValues(ctx context.Context, input ViewSecretValuesInput) (*ViewSe actor := authz.ActorFromContext(ctx) loaders := fromContext(ctx) + clusterName := environmentmapper.ClusterName(input.Environment) + k8sClient, exists := loaders.K8sClient(clusterName) + if !exists { + return nil, apierror.Errorf("Environment %q does not exist.", input.Environment) + } + current, err := k8sClient.Resource(schema.GroupVersionResource{Version: "v1", Resource: "secrets"}).Namespace(input.Team.String()).Get(ctx, input.Name, v1.GetOptions{}) + if err != nil { + return nil, fmt.Errorf("checking secret ownership: %w", err) + } + if isPostgresAccessSecret(current) { + return nil, apierror.Errorf("PostgresAccess credentials are only available through postgresAccessConnection") + } // Create temporary Role and RoleBinding for the user (1 minute TTL) elevationID, err := createTemporaryRBAC(ctx, loaders, input, actor) @@ -739,7 +751,6 @@ func ViewSecretValues(ctx context.Context, input ViewSecretValuesInput) (*ViewSe } // Use impersonated client to read secret values (defense in depth) - clusterName := environmentmapper.ClusterName(input.Environment) impersonatedClient, err := loaders.Client(ctx, clusterName) if err != nil { return nil, fmt.Errorf("creating impersonated client: %w", err) @@ -768,6 +779,10 @@ func ViewSecretValues(ctx context.Context, input ViewSecretValuesInput) (*ViewSe } } + if isPostgresAccessSecret(u) { + return nil, apierror.Errorf("PostgresAccess credentials are only available through postgresAccessConnection") + } + data, _, err := unstructured.NestedStringMap(u.Object, "data") if err != nil { return nil, err @@ -811,6 +826,15 @@ func ViewSecretValues(ctx context.Context, input ViewSecretValuesInput) (*ViewSe }, nil } +func isPostgresAccessSecret(secret *unstructured.Unstructured) bool { + for _, owner := range secret.GetOwnerReferences() { + if owner.APIVersion == "nais.io/v1" && owner.Kind == "PostgresAccess" { + return true + } + } + return false +} + var ( roleGVR = schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "roles"} roleBindingGVR = schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "rolebindings"} diff --git a/internal/workload/secret/queries_test.go b/internal/workload/secret/queries_test.go new file mode 100644 index 000000000..5c25e4552 --- /dev/null +++ b/internal/workload/secret/queries_test.go @@ -0,0 +1,27 @@ +package secret + +import ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +func TestPersonalAccessSecretsAreNotAvailableThroughGenericSecretView(t *testing.T) { + for _, name := range []string{"postgres-access-12345678-relay-token", "postgres-access-12345678-credentials"} { + t.Run(name, func(t *testing.T) { + secret := &unstructured.Unstructured{} + secret.SetName(name) + secret.SetOwnerReferences([]metav1.OwnerReference{{APIVersion: "nais.io/v1", Kind: "PostgresAccess", Name: "postgres-access-12345678"}}) + if !isPostgresAccessSecret(secret) { + t.Fatal("personal access credentials must not be readable through generic Secret view") + } + }) + } + + ordinary := &unstructured.Unstructured{} + ordinary.SetName("application-credentials") + if isPostgresAccessSecret(ordinary) { + t.Fatal("ordinary Secrets must remain readable through generic Secret view") + } +} diff --git a/pkg/apiclient/protoapi/databases.pb.go b/pkg/apiclient/protoapi/databases.pb.go index c92a0491b..d7b036ab0 100644 --- a/pkg/apiclient/protoapi/databases.pb.go +++ b/pkg/apiclient/protoapi/databases.pb.go @@ -28,6 +28,7 @@ const ( DatabaseType_DATABASE_TYPE_UNSPECIFIED DatabaseType = 0 DatabaseType_CLOUD_SQL DatabaseType = 1 DatabaseType_ZALANDO_POSTGRES DatabaseType = 2 + DatabaseType_NAIS_POSTGRES DatabaseType = 3 ) // Enum value maps for DatabaseType. @@ -36,11 +37,13 @@ var ( 0: "DATABASE_TYPE_UNSPECIFIED", 1: "CLOUD_SQL", 2: "ZALANDO_POSTGRES", + 3: "NAIS_POSTGRES", } DatabaseType_value = map[string]int32{ "DATABASE_TYPE_UNSPECIFIED": 0, "CLOUD_SQL": 1, "ZALANDO_POSTGRES": 2, + "NAIS_POSTGRES": 3, } ) @@ -378,21 +381,22 @@ var file_databases_proto_rawDesc = string([]byte{ 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x2a, 0x52, 0x0a, 0x0c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x54, 0x79, 0x70, + 0x6f, 0x2a, 0x65, 0x0a, 0x0c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4c, 0x4f, 0x55, 0x44, 0x5f, 0x53, 0x51, 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x5a, 0x41, 0x4c, 0x41, 0x4e, 0x44, 0x4f, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, - 0x52, 0x45, 0x53, 0x10, 0x02, 0x32, 0x68, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, - 0x65, 0x73, 0x12, 0x5b, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x2e, 0x6e, 0x61, 0x69, - 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, - 0x1a, 0x5a, 0x18, 0x2e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x52, 0x45, 0x53, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x41, 0x49, 0x53, 0x5f, 0x50, 0x4f, + 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x10, 0x03, 0x32, 0x68, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, + 0x62, 0x61, 0x73, 0x65, 0x73, 0x12, 0x5b, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x2e, + 0x6e, 0x61, 0x69, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x42, 0x1a, 0x5a, 0x18, 0x2e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x70, 0x69, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var file_databases_proto_enumTypes = make([]protoimpl.EnumInfo, 1) diff --git a/pkg/apiclient/protoapi/databases_protoopaque.pb.go b/pkg/apiclient/protoapi/databases_protoopaque.pb.go index f0eb1aca8..f5b9a9796 100644 --- a/pkg/apiclient/protoapi/databases_protoopaque.pb.go +++ b/pkg/apiclient/protoapi/databases_protoopaque.pb.go @@ -28,6 +28,7 @@ const ( DatabaseType_DATABASE_TYPE_UNSPECIFIED DatabaseType = 0 DatabaseType_CLOUD_SQL DatabaseType = 1 DatabaseType_ZALANDO_POSTGRES DatabaseType = 2 + DatabaseType_NAIS_POSTGRES DatabaseType = 3 ) // Enum value maps for DatabaseType. @@ -36,11 +37,13 @@ var ( 0: "DATABASE_TYPE_UNSPECIFIED", 1: "CLOUD_SQL", 2: "ZALANDO_POSTGRES", + 3: "NAIS_POSTGRES", } DatabaseType_value = map[string]int32{ "DATABASE_TYPE_UNSPECIFIED": 0, "CLOUD_SQL": 1, "ZALANDO_POSTGRES": 2, + "NAIS_POSTGRES": 3, } ) @@ -380,21 +383,22 @@ var file_databases_proto_rawDesc = string([]byte{ 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x70, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x2a, 0x52, 0x0a, 0x0c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x54, 0x79, 0x70, + 0x6f, 0x2a, 0x65, 0x0a, 0x0c, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x44, 0x41, 0x54, 0x41, 0x42, 0x41, 0x53, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4c, 0x4f, 0x55, 0x44, 0x5f, 0x53, 0x51, 0x4c, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x5a, 0x41, 0x4c, 0x41, 0x4e, 0x44, 0x4f, 0x5f, 0x50, 0x4f, 0x53, 0x54, 0x47, - 0x52, 0x45, 0x53, 0x10, 0x02, 0x32, 0x68, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, - 0x65, 0x73, 0x12, 0x5b, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x2e, 0x6e, 0x61, 0x69, - 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, - 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, - 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, - 0x1a, 0x5a, 0x18, 0x2e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x63, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x52, 0x45, 0x53, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x4e, 0x41, 0x49, 0x53, 0x5f, 0x50, 0x4f, + 0x53, 0x54, 0x47, 0x52, 0x45, 0x53, 0x10, 0x03, 0x32, 0x68, 0x0a, 0x09, 0x44, 0x61, 0x74, 0x61, + 0x62, 0x61, 0x73, 0x65, 0x73, 0x12, 0x5b, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x27, 0x2e, + 0x6e, 0x61, 0x69, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x6e, 0x61, 0x69, 0x73, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, + 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x42, 0x1a, 0x5a, 0x18, 0x2e, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x61, 0x70, 0x69, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var file_databases_proto_enumTypes = make([]protoimpl.EnumInfo, 1) diff --git a/pkg/apiclient/protoapi/databases_test.go b/pkg/apiclient/protoapi/databases_test.go new file mode 100644 index 000000000..ffc13db6b --- /dev/null +++ b/pkg/apiclient/protoapi/databases_test.go @@ -0,0 +1,47 @@ +package protoapi_test + +import ( + "encoding/json" + "testing" + + "github.com/nais/api/pkg/apiclient/protoapi" + "google.golang.org/protobuf/encoding/protojson" +) + +func TestPostgresDatabaseTypes(t *testing.T) { + if protoapi.DatabaseType_ZALANDO_POSTGRES != 2 || protoapi.DatabaseType_NAIS_POSTGRES != 3 { + t.Fatal("old and new Postgres database types must retain distinct wire values") + } + + for _, tt := range []struct { + name string + typeValue protoapi.DatabaseType + }{ + {name: "ZALANDO_POSTGRES", typeValue: protoapi.DatabaseType_ZALANDO_POSTGRES}, + {name: "NAIS_POSTGRES", typeValue: protoapi.DatabaseType_NAIS_POSTGRES}, + } { + t.Run(tt.name, func(t *testing.T) { + var database protoapi.Database + if err := protojson.Unmarshal([]byte(`{"type":"`+tt.name+`"}`), &database); err != nil { + t.Fatalf("unmarshal database type %q: %v", tt.name, err) + } + if database.GetType() != tt.typeValue { + t.Fatalf("database type = %v, want %v", database.GetType(), tt.typeValue) + } + + data, err := protojson.Marshal(&database) + if err != nil { + t.Fatalf("marshal database: %v", err) + } + var fields struct { + Type string `json:"type"` + } + if err := json.Unmarshal(data, &fields); err != nil { + t.Fatalf("decode database JSON: %v", err) + } + if fields.Type != tt.name { + t.Errorf("serialized database type = %q, want %q", fields.Type, tt.name) + } + }) + } +} diff --git a/pkg/apiclient/protoapi/schema/databases.proto b/pkg/apiclient/protoapi/schema/databases.proto index 6876580c5..8388e50d0 100644 --- a/pkg/apiclient/protoapi/schema/databases.proto +++ b/pkg/apiclient/protoapi/schema/databases.proto @@ -10,6 +10,7 @@ enum DatabaseType { DATABASE_TYPE_UNSPECIFIED = 0; CLOUD_SQL = 1; ZALANDO_POSTGRES = 2; + NAIS_POSTGRES = 3; } message Database {