diff --git a/go.mod b/go.mod index 1ef624131..2f2eaad5f 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,8 @@ 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-20251112085730-ffef5fe13671 + github.com/nais/liberator v0.0.0-20260210104718-283ff480d830 + github.com/nais/pgrator/pkg/api v0.0.0-20260212110002-bbf4f8e0284a github.com/nais/tester v0.1.0 github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe github.com/nais/v13s/pkg/api v0.0.0-20260116082144-a141ed4631c6 @@ -52,8 +53,8 @@ require ( github.com/ravilushqa/otelgqlgen v0.19.0 github.com/rs/cors v1.11.1 github.com/sethvargo/go-envconfig v1.3.0 - github.com/sirupsen/logrus v1.9.3 - github.com/sourcegraph/conc v0.3.0 + github.com/sirupsen/logrus v1.9.4 + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 github.com/stretchr/testify v1.11.1 github.com/testcontainers/testcontainers-go v0.35.0 github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 @@ -73,19 +74,19 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.39.0 go.opentelemetry.io/otel/trace v1.39.0 golang.org/x/exp v0.0.0-20250808145144-a408d31f581a - golang.org/x/oauth2 v0.34.0 + golang.org/x/oauth2 v0.35.0 golang.org/x/sync v0.19.0 - golang.org/x/text v0.32.0 - golang.org/x/tools v0.40.0 + golang.org/x/text v0.34.0 + golang.org/x/tools v0.42.0 google.golang.org/api v0.259.0 google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 google.golang.org/grpc v1.78.0 google.golang.org/protobuf v1.36.11 - k8s.io/api v0.34.3 - k8s.io/apimachinery v0.34.3 - k8s.io/client-go v0.34.3 + k8s.io/api v0.35.1 + k8s.io/apimachinery v0.35.1 + k8s.io/client-go v0.35.1 k8s.io/klog/v2 v2.130.1 - k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 + k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 sigs.k8s.io/yaml v1.6.0 ) @@ -219,7 +220,7 @@ require ( github.com/go-openapi/swag/yamlutils v0.25.4 // indirect github.com/go-openapi/validate v0.24.0 // indirect github.com/go-sql-driver/mysql v1.9.3 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.19.0 // indirect @@ -259,7 +260,7 @@ require ( github.com/hashicorp/go-metrics v0.5.4 // indirect github.com/hashicorp/go-msgpack/v2 v2.1.2 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-retryablehttp v0.7.7 // indirect + github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-sockaddr v1.0.7 // indirect github.com/hashicorp/go-version v1.7.0 // indirect @@ -371,7 +372,7 @@ require ( github.com/shopspring/decimal v1.4.0 // indirect github.com/sony/gobreaker/v2 v2.3.0 // indirect github.com/sosodev/duration v1.3.1 // indirect - github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/sqlc-dev/sqlc v1.29.0 // indirect @@ -398,8 +399,8 @@ require ( github.com/zitadel/schema v1.3.0 // indirect go.etcd.io/bbolt v1.4.3 // indirect go.etcd.io/etcd/api/v3 v3.6.6 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.6.4 // indirect - go.etcd.io/etcd/client/v3 v3.6.4 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.5 // indirect + go.etcd.io/etcd/client/v3 v3.6.5 // indirect go.mongodb.org/mongo-driver v1.17.4 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect @@ -437,13 +438,13 @@ require ( go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect - golang.org/x/crypto v0.46.0 // indirect + golang.org/x/crypto v0.48.0 // indirect golang.org/x/exp/typeparams v0.0.0-20251209150349-8475f28825e9 // indirect - golang.org/x/mod v0.31.0 // indirect - golang.org/x/net v0.48.0 // indirect - golang.org/x/sys v0.39.0 // indirect - golang.org/x/telemetry v0.0.0-20251208220230-2638a1023523 // indirect - golang.org/x/term v0.38.0 // indirect + golang.org/x/mod v0.33.0 // indirect + golang.org/x/net v0.50.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect + golang.org/x/term v0.40.0 // indirect golang.org/x/time v0.14.0 // indirect golang.org/x/vuln v1.1.4 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect @@ -456,17 +457,17 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.6.1 // indirect - k8s.io/apiextensions-apiserver v0.34.3 // indirect - k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect + k8s.io/apiextensions-apiserver v0.35.0 // indirect + k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 // indirect modernc.org/libc v1.66.3 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect modernc.org/sqlite v1.38.2 // indirect mvdan.cc/gofumpt v0.9.2 // indirect - sigs.k8s.io/controller-runtime v0.22.4 // indirect + sigs.k8s.io/controller-runtime v0.23.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.1 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect ) replace github.com/GoogleCloudPlatform/k8s-config-connector/mockgcp => ./mockgcp diff --git a/go.sum b/go.sum index 506e818d4..013614a37 100644 --- a/go.sum +++ b/go.sum @@ -426,8 +426,8 @@ github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI6 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-zookeeper/zk v1.0.4 h1:DPzxraQx7OrPyXq2phlGlNSIyWEsAox0RJmjTseMV6I= github.com/go-zookeeper/zk v1.0.4/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -505,8 +505,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= -github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8 h1:ZI8gCoCjGzPsum4L21jHdQs8shFBIQih1TM9Rd/c+EQ= -github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= +github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 h1:z2ogiKUYzX5Is6zr/vP9vJGqPwcdqsWjOt+V8J7+bTc= +github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= @@ -572,8 +572,8 @@ github.com/hashicorp/go-msgpack/v2 v2.1.2/go.mod h1:upybraOAblm4S7rx0+jeNy+CWWhz github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= -github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= +github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= +github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= @@ -792,8 +792,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-20251112085730-ffef5fe13671 h1:cFjbN7NzWcDkVd1nO3v7GkydHeRbZGE5PRYd+0VRh+A= -github.com/nais/liberator v0.0.0-20251112085730-ffef5fe13671/go.mod h1:p6EA7AKqzH798N7yvG8GDHVMzzkpORoxoyfcFz5Oa7c= +github.com/nais/liberator v0.0.0-20260210104718-283ff480d830 h1:vNM4wRtvPClQBD0Pk2P+pMPj1pJYifGfphCYcrDx5eI= +github.com/nais/liberator v0.0.0-20260210104718-283ff480d830/go.mod h1:P7z+suxnM/3LfkRrHAXPBVCBNuEz+F7zw/UehcAxpRM= +github.com/nais/pgrator/pkg/api v0.0.0-20260212110002-bbf4f8e0284a h1:tkkq/GalWSCVORei+I80GlW1OTJSZa6hFqJNyyEoBjk= +github.com/nais/pgrator/pkg/api v0.0.0-20260212110002-bbf4f8e0284a/go.mod h1:E7ZUEL98EjdsaGzbd8/QTvA7utbGgsPIbjXaaqZdDjE= github.com/nais/tester v0.1.0 h1:dojqSFT3RB8bPZ0Wv0QlBAa/MVPitftpkErC2awVHmU= github.com/nais/tester v0.1.0/go.mod h1:NCQMcgftHz/EXorob1XwDTOqkQmImDqr51YQ2Uea9Pc= github.com/nais/unleasherator v0.0.0-20251216221129-efebc54203fe h1:CdRVopOihru4tXVwKZjhg6C8SbPLCQYOhJKpjBZYhjg= @@ -812,11 +814,11 @@ github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNs github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= -github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI= +github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= -github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= +github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.139.0 h1:D5aGQCErSCb4sKIHoZhgR4El6AzgviTRYlHUpbSFqDo= github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.139.0/go.mod h1:ZjeRsA5oaVk89fg5D+iXStx2QncmhAvtGbdSumT07H4= github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.139.0 h1:6/j0Ta8ZJnmAFVEoC3aZ1Hs19RB4fHzlN6kOZhsBJqM= @@ -964,16 +966,16 @@ 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.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/sony/gobreaker/v2 v2.3.0 h1:7VYxZ69QXRQ2Q4eEawHn6eU4FiuwovzJwsUMA03Lu4I= github.com/sony/gobreaker/v2 v2.3.0/go.mod h1:pTyFJgcZ3h2tdQVLZZruK2C0eoFL1fb/G83wK1ZQl+s= github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4= github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= -github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -1077,10 +1079,10 @@ go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= go.etcd.io/etcd/api/v3 v3.6.6 h1:mcaMp3+7JawWv69p6QShYWS8cIWUOl32bFLb6qf8pOQ= go.etcd.io/etcd/api/v3 v3.6.6/go.mod h1:f/om26iXl2wSkcTA1zGQv8reJRSLVdoEBsi4JdfMrx4= -go.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0= -go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI= -go.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A= -go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo= +go.etcd.io/etcd/client/pkg/v3 v3.6.5 h1:Duz9fAzIZFhYWgRjp/FgNq2gO1jId9Yae/rLn3RrBP8= +go.etcd.io/etcd/client/pkg/v3 v3.6.5/go.mod h1:8Wx3eGRPiy0qOFMZT/hfvdos+DjEaPxdIDiCDUv/FQk= +go.etcd.io/etcd/client/v3 v3.6.5 h1:yRwZNFBx/35VKHTcLDeO7XVLbCBFbPi+XV4OC3QJf2U= +go.etcd.io/etcd/client/v3 v3.6.5/go.mod h1:ZqwG/7TAFZ0BJ0jXRPoJjKQJtbFo/9NIY8uoFFKcCyo= go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw= go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= @@ -1219,8 +1221,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= -golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1242,8 +1244,8 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= -golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1258,12 +1260,12 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= +golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= +golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1298,23 +1300,22 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20251208220230-2638a1023523 h1:H52Mhyrc44wBgLTGzq6+0cmuVuF3LURCSXsLMOqfFos= -golang.org/x/telemetry v0.0.0-20251208220230-2638a1023523/go.mod h1:ArQvPJS723nJQietgilmZA+shuB3CZxH1n2iXq9VSfs= -golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= -golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 h1:bTLqdHv7xrGlFbvf5/TXNxy/iUwwdkjhqQTJDjW7aj0= +golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548= +golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= -golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1331,8 +1332,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= -golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= @@ -1427,20 +1428,20 @@ 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.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI= honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4= -k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= -k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk= -k8s.io/apiextensions-apiserver v0.34.3 h1:p10fGlkDY09eWKOTeUSioxwLukJnm+KuDZdrW71y40g= -k8s.io/apiextensions-apiserver v0.34.3/go.mod h1:aujxvqGFRdb/cmXYfcRTeppN7S2XV/t7WMEc64zB5A0= -k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= -k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= -k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= +k8s.io/api v0.35.1 h1:0PO/1FhlK/EQNVK5+txc4FuhQibV25VLSdLMmGpDE/Q= +k8s.io/api v0.35.1/go.mod h1:28uR9xlXWml9eT0uaGo6y71xK86JBELShLy4wR1XtxM= +k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4= +k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU= +k8s.io/apimachinery v0.35.1 h1:yxO6gV555P1YV0SANtnTjXYfiivaTPvCTKX6w6qdDsU= +k8s.io/apimachinery v0.35.1/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/client-go v0.35.1 h1:+eSfZHwuo/I19PaSxqumjqZ9l5XiTEKbIaJ+j1wLcLM= +k8s.io/client-go v0.35.1/go.mod h1:1p1KxDt3a0ruRfc/pG4qT/3oHmUj1AhSHEcxNSGg+OA= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ= -k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 h1:HhDfevmPS+OalTjQRKbTHppRIz01AWi8s45TMXStgYY= +k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= +k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM= modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU= @@ -1470,13 +1471,13 @@ modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= mvdan.cc/gofumpt v0.9.2 h1:zsEMWL8SVKGHNztrx6uZrXdp7AX8r421Vvp23sz7ik4= mvdan.cc/gofumpt v0.9.2/go.mod h1:iB7Hn+ai8lPvofHd9ZFGVg2GOr8sBUw1QUWjNbmIL/s= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= -sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/controller-runtime v0.23.1 h1:TjJSM80Nf43Mg21+RCy3J70aj/W6KyvDtOlpKf+PupE= +sigs.k8s.io/controller-runtime v0.23.1/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0= 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.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= -sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8= +sigs.k8s.io/structured-merge-diff/v6 v6.3.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/k8s_resources/opensearch_crud/dev/someteamname/opensearch_noversion.yaml b/integration_tests/k8s_resources/opensearch_crud/dev/someteamname/opensearch_noversion.yaml index b1ceebab6..e0c455ecd 100644 --- a/integration_tests/k8s_resources/opensearch_crud/dev/someteamname/opensearch_noversion.yaml +++ b/integration_tests/k8s_resources/opensearch_crud/dev/someteamname/opensearch_noversion.yaml @@ -1,44 +1,12 @@ -apiVersion: aiven.io/v1alpha1 +apiVersion: nais.io/v1 kind: OpenSearch metadata: - annotations: - controllers.aiven.io/generation-was-processed: "2" - controllers.aiven.io/instance-is-running: "true" - nais.io/created_by: aiven-iac-migration - creationTimestamp: "2023-11-08T10:35:59Z" - finalizers: - - finalizers.aiven.io/delete-remote-resource - generation: 2 labels: - team: teampam nais.io/managed-by: console - name: opensearch-someteamname-noversion - namespace: teampam - resourceVersion: "3990043290" - uid: 2a8d4d8a-2bf4-4b2f-99fc-814f6a937ecd + name: noversion + namespace: someteamname spec: - cloudName: google-europe-north1 - connInfoSecretTarget: - name: "" - disk_space: 525G - plan: hobbyist - project: nav-prod - projectVpcId: fff21e17-95d5-408b-8df5-15aacf38f5de - tags: - environment: prod - team: teampam - tenant: nav - terminationProtection: true -status: - conditions: - - lastTransitionTime: "2023-11-08T10:36:06Z" - message: Instance was created or update on Aiven side - reason: Updated - status: "True" - type: Initialized - - lastTransitionTime: "2024-01-10T09:40:58Z" - message: Instance is running on Aiven side - reason: CheckRunning - status: "True" - type: Running - state: RUNNING + tier: SingleNode + memory: "2GB" + version: "2" + storageGB: 16 diff --git a/integration_tests/opensearch_crud.lua b/integration_tests/opensearch_crud.lua index 3f8e57de7..1d19eee91 100644 --- a/integration_tests/opensearch_crud.lua +++ b/integration_tests/opensearch_crud.lua @@ -134,7 +134,7 @@ Test.gql("Create opensearch as team member with existing name", function(t) t.check { errors = { { - message = "Resource already exists.", + message = "OpenSearch with the name \"not-managed\" already exists, but are not yet managed through Console.", path = { "createOpenSearch", }, @@ -259,73 +259,26 @@ Test.gql("Create opensearch with invalid storage capacity increment", function(t end) Test.k8s("Validate OpenSearch resource", function(t) - local resourceName = string.format("opensearch-%s-foobar", mainTeam:slug()) - - t.check("aiven.io/v1alpha1", "opensearches", "dev", mainTeam:slug(), resourceName, { - apiVersion = "aiven.io/v1alpha1", + t.check("nais.io/v1", "opensearches", "dev", mainTeam:slug(), "foobar", { + apiVersion = "nais.io/v1", kind = "OpenSearch", metadata = { - name = resourceName, - namespace = mainTeam:slug(), annotations = { ["console.nais.io/last-modified-at"] = NotNull(), - ["console.nais.io/last-modified-by"] = user:email(), + ["console.nais.io/last-modified-by"] = "user@usersen.com", }, labels = { ["app.kubernetes.io/managed-by"] = "console", ["nais.io/managed-by"] = "console", }, + name = "foobar", + namespace = "someteamname", }, spec = { - project = "aiven-dev", - projectVpcId = "aiven-vpc", - plan = "startup-16", - cloudName = "google-europe-north1", - disk_space = "350G", - terminationProtection = true, - tags = { - environment = "dev", - team = mainTeam:slug(), - tenant = "some-tenant", - }, - userConfig = { - opensearch_version = "2", - }, - }, - }) -end) - -Test.k8s("Validate serviceintegration", function(t) - local resourceName = string.format("opensearch-%s-foobar", mainTeam:slug()) - - t.check("aiven.io/v1alpha1", "serviceintegrations", "dev", mainTeam:slug(), resourceName, { - apiVersion = "aiven.io/v1alpha1", - kind = "ServiceIntegration", - metadata = { - name = resourceName, - namespace = mainTeam:slug(), - annotations = { - ["console.nais.io/last-modified-at"] = NotNull(), - ["console.nais.io/last-modified-by"] = user:email(), - }, - labels = { - ["app.kubernetes.io/managed-by"] = "console", - ["nais.io/managed-by"] = "console", - }, - ownerReferences = { - { - apiVersion = "aiven.io/v1alpha1", - kind = "OpenSearch", - name = resourceName, - uid = NotNull(), - }, - }, - }, - spec = { - project = "aiven-dev", - destinationEndpointId = "endpoint-id", - integrationType = "prometheus", - sourceServiceName = resourceName, + memory = "16GB", + tier = "SingleNode", + version = "2", + storageGB = NotNull(), }, }) end) @@ -364,73 +317,26 @@ Test.gql("Create opensearch with tier and memory equivalent to hobbyist plan", f end) Test.k8s("Validate hobbyist OpenSearch resource", function(t) - local resourceName = string.format("opensearch-%s-foobar-hobbyist", mainTeam:slug()) - - t.check("aiven.io/v1alpha1", "opensearches", "dev", mainTeam:slug(), resourceName, { - apiVersion = "aiven.io/v1alpha1", + t.check("nais.io/v1", "opensearches", "dev", mainTeam:slug(), "foobar-hobbyist", { + apiVersion = "nais.io/v1", kind = "OpenSearch", metadata = { - name = resourceName, - namespace = mainTeam:slug(), annotations = { ["console.nais.io/last-modified-at"] = NotNull(), - ["console.nais.io/last-modified-by"] = user:email(), + ["console.nais.io/last-modified-by"] = "user@usersen.com", }, labels = { ["app.kubernetes.io/managed-by"] = "console", ["nais.io/managed-by"] = "console", }, + name = "foobar-hobbyist", + namespace = "someteamname", }, spec = { - project = "aiven-dev", - projectVpcId = "aiven-vpc", - plan = "hobbyist", - cloudName = "google-europe-north1", - disk_space = "16G", - terminationProtection = true, - tags = { - environment = "dev", - team = mainTeam:slug(), - tenant = "some-tenant", - }, - userConfig = { - opensearch_version = "2", - }, - }, - }) -end) - -Test.k8s("Validate hobbyist serviceintegration", function(t) - local resourceName = string.format("opensearch-%s-foobar-hobbyist", mainTeam:slug()) - - t.check("aiven.io/v1alpha1", "serviceintegrations", "dev", mainTeam:slug(), resourceName, { - apiVersion = "aiven.io/v1alpha1", - kind = "ServiceIntegration", - metadata = { - name = resourceName, - namespace = mainTeam:slug(), - annotations = { - ["console.nais.io/last-modified-at"] = NotNull(), - ["console.nais.io/last-modified-by"] = user:email(), - }, - labels = { - ["app.kubernetes.io/managed-by"] = "console", - ["nais.io/managed-by"] = "console", - }, - ownerReferences = { - { - apiVersion = "aiven.io/v1alpha1", - kind = "OpenSearch", - name = resourceName, - uid = NotNull(), - }, - }, - }, - spec = { - project = "aiven-dev", - destinationEndpointId = "endpoint-id", - integrationType = "prometheus", - sourceServiceName = resourceName, + memory = "2GB", + tier = "SingleNode", + version = "2", + storageGB = NotNull(), }, }) end) @@ -539,38 +445,26 @@ Test.gql("Update OpenSearch as team-member", function(t) end) Test.k8s("Validate OpenSearch resource after update", function(t) - local resourceName = string.format("opensearch-%s-foobar", mainTeam:slug()) - - t.check("aiven.io/v1alpha1", "opensearches", "dev", mainTeam:slug(), resourceName, { - apiVersion = "aiven.io/v1alpha1", + t.check("nais.io/v1", "opensearches", "dev", mainTeam:slug(), "foobar", { + apiVersion = "nais.io/v1", kind = "OpenSearch", metadata = { - name = resourceName, - namespace = mainTeam:slug(), annotations = { ["console.nais.io/last-modified-at"] = NotNull(), - ["console.nais.io/last-modified-by"] = user:email(), + ["console.nais.io/last-modified-by"] = "user@usersen.com", }, labels = { ["app.kubernetes.io/managed-by"] = "console", ["nais.io/managed-by"] = "console", }, + name = "foobar", + namespace = "someteamname", }, spec = { - project = "aiven-dev", - projectVpcId = "aiven-vpc", - plan = "business-4", - cloudName = "google-europe-north1", - disk_space = "1020G", - terminationProtection = true, - tags = { - environment = "dev", - team = mainTeam:slug(), - tenant = "some-tenant", - }, - userConfig = { - opensearch_version = "2", - }, + memory = "4GB", + tier = "HighAvailability", + version = "2", + storageGB = NotNull(), }, }) end) @@ -664,7 +558,7 @@ Test.gql("Downgrade OpenSearch as team-member", function(t) } end) -Test.gql("Downgrade OpenSearch without explicit version set", function(t) +Test.gql("Downgrade OpenSearch noversion instance", function(t) t.addHeader("x-user-email", user:email()) t.query [[ mutation UpdateOpenSearch { @@ -699,41 +593,6 @@ Test.gql("Downgrade OpenSearch without explicit version set", function(t) } end) -Test.gql("Update non-console managed OpenSearch as team-member", function(t) - t.addHeader("x-user-email", user:email()) - t.query [[ - mutation UpdateOpenSearch { - updateOpenSearch( - input: { - name: "not-managed" - environmentName: "dev" - teamSlug: "someteamname" - tier: HIGH_AVAILABILITY - memory: GB_4 - version: V2 - storageGB: 240 - } - ) { - openSearch { - name - } - } - } - ]] - - t.check { - errors = { - { - message = "OpenSearch someteamname/not-managed is not managed by Console", - path = { - "updateOpenSearch", - }, - }, - }, - data = Null, - } -end) - Test.gql("Update OpenSearch with tier and memory equivalent to hobbyist plan", function(t) t.addHeader("x-user-email", user:email()) t.query [[ @@ -768,38 +627,26 @@ Test.gql("Update OpenSearch with tier and memory equivalent to hobbyist plan", f end) Test.k8s("Validate hobbyist OpenSearch resource after update", function(t) - local resourceName = string.format("opensearch-%s-foobar", mainTeam:slug()) - - t.check("aiven.io/v1alpha1", "opensearches", "dev", mainTeam:slug(), resourceName, { - apiVersion = "aiven.io/v1alpha1", + t.check("nais.io/v1", "opensearches", "dev", mainTeam:slug(), "foobar", { + apiVersion = "nais.io/v1", kind = "OpenSearch", metadata = { - name = resourceName, - namespace = mainTeam:slug(), annotations = { ["console.nais.io/last-modified-at"] = NotNull(), - ["console.nais.io/last-modified-by"] = user:email(), + ["console.nais.io/last-modified-by"] = "user@usersen.com", }, labels = { ["app.kubernetes.io/managed-by"] = "console", ["nais.io/managed-by"] = "console", }, + name = "foobar", + namespace = "someteamname", }, spec = { - project = "aiven-dev", - projectVpcId = "aiven-vpc", - plan = "hobbyist", - cloudName = "google-europe-north1", - disk_space = "16G", - terminationProtection = true, - tags = { - environment = "dev", - team = mainTeam:slug(), - tenant = "some-tenant", - }, - userConfig = { - opensearch_version = "2", - }, + memory = "2GB", + tier = "SingleNode", + version = "2", + storageGB = NotNull(), }, }) end) @@ -1096,32 +943,3 @@ Test.gql("Verify activity log for opensearch operations", function(t) }, } end) - -Test.gql("Delete non-managed opensearch as team-member", function(t) - t.addHeader("x-user-email", user:email()) - t.query [[ - mutation DeleteOpenSearch { - deleteOpenSearch( - input: { - name: "not-managed" - environmentName: "dev" - teamSlug: "someteamname" - } - ) { - openSearchDeleted - } - } - ]] - - t.check { - errors = { - { - message = "OpenSearch someteamname/not-managed is not managed by Console", - path = { - "deleteOpenSearch", - }, - }, - }, - data = Null, - } -end) diff --git a/integration_tests/valkey_crud.lua b/integration_tests/valkey_crud.lua index d978496ab..8fa9b0e58 100644 --- a/integration_tests/valkey_crud.lua +++ b/integration_tests/valkey_crud.lua @@ -127,7 +127,7 @@ Test.gql("Create valkey as team member with existing name", function(t) t.check { errors = { { - message = "Resource already exists.", + message = "Valkey with the name \"not-managed\" already exists, but are not yet managed through Console.", path = { "createValkey", }, @@ -138,69 +138,24 @@ Test.gql("Create valkey as team member with existing name", function(t) end) Test.k8s("Validate Valkey resource", function(t) - local resourceName = string.format("valkey-%s-foobar", mainTeam:slug()) - - t.check("aiven.io/v1alpha1", "valkeys", "dev", mainTeam:slug(), resourceName, { - apiVersion = "aiven.io/v1alpha1", + t.check("nais.io/v1", "valkeys", "dev", mainTeam:slug(), "foobar", { + apiVersion = "nais.io/v1", kind = "Valkey", metadata = { - name = resourceName, - namespace = mainTeam:slug(), - annotations = { - ["console.nais.io/last-modified-at"] = NotNull(), - ["console.nais.io/last-modified-by"] = user:email(), - }, - labels = { - ["app.kubernetes.io/managed-by"] = "console", - ["nais.io/managed-by"] = "console", - }, - }, - spec = { - project = "aiven-dev", - projectVpcId = "aiven-vpc", - plan = "startup-14", - cloudName = "google-europe-north1", - terminationProtection = true, - tags = { - environment = "dev", - team = mainTeam:slug(), - tenant = "some-tenant", - }, - }, - }) -end) - -Test.k8s("Validate serviceintegration", function(t) - local resourceName = string.format("valkey-%s-foobar", mainTeam:slug()) - - t.check("aiven.io/v1alpha1", "serviceintegrations", "dev", mainTeam:slug(), resourceName, { - apiVersion = "aiven.io/v1alpha1", - kind = "ServiceIntegration", - metadata = { - name = resourceName, - namespace = mainTeam:slug(), annotations = { ["console.nais.io/last-modified-at"] = NotNull(), - ["console.nais.io/last-modified-by"] = user:email(), + ["console.nais.io/last-modified-by"] = "user@usersen.com", }, labels = { ["app.kubernetes.io/managed-by"] = "console", ["nais.io/managed-by"] = "console", }, - ownerReferences = { - { - apiVersion = "aiven.io/v1alpha1", - kind = "Valkey", - name = resourceName, - uid = NotNull(), - }, - }, + name = "foobar", + namespace = "someteamname", }, spec = { - project = "aiven-dev", - destinationEndpointId = "endpoint-id", - integrationType = "prometheus", - sourceServiceName = resourceName, + memory = "14GB", + tier = "SingleNode", }, }) end) @@ -305,38 +260,26 @@ Test.gql("Update Valkey as team-member", function(t) end) Test.k8s("Validate Valkey resource after update", function(t) - local resourceName = string.format("valkey-%s-foobar", mainTeam:slug()) - - t.check("aiven.io/v1alpha1", "valkeys", "dev", mainTeam:slug(), resourceName, { - apiVersion = "aiven.io/v1alpha1", + t.check("nais.io/v1", "valkeys", "dev", mainTeam:slug(), "foobar", { + apiVersion = "nais.io/v1", kind = "Valkey", metadata = { - name = resourceName, - namespace = mainTeam:slug(), annotations = { ["console.nais.io/last-modified-at"] = NotNull(), - ["console.nais.io/last-modified-by"] = user:email(), + ["console.nais.io/last-modified-by"] = "user@usersen.com", }, labels = { ["app.kubernetes.io/managed-by"] = "console", ["nais.io/managed-by"] = "console", }, + name = "foobar", + namespace = "someteamname", }, spec = { - project = "aiven-dev", - projectVpcId = "aiven-vpc", - plan = "business-4", - cloudName = "google-europe-north1", - terminationProtection = true, - userConfig = { - valkey_maxmemory_policy = "allkeys-random", - valkey_notify_keyspace_events = "Exd", - }, - tags = { - environment = "dev", - team = mainTeam:slug(), - tenant = "some-tenant", - }, + maxMemoryPolicy = "allkeys-random", + memory = "4GB", + notifyKeyspaceEvents = "Exd", + tier = "HighAvailability", }, }) end) @@ -372,70 +315,26 @@ Test.gql("Create valkey with tier and memory equivalent to hobbyist plan", funct } end) +-- TODO: Do we need this? Test.k8s("Validate hobbyist Valkey resource", function(t) - local resourceName = string.format("valkey-%s-foobar-hobbyist", mainTeam:slug()) - - t.check("aiven.io/v1alpha1", "valkeys", "dev", mainTeam:slug(), resourceName, { - apiVersion = "aiven.io/v1alpha1", + t.check("nais.io/v1", "valkeys", "dev", mainTeam:slug(), "foobar-hobbyist", { + apiVersion = "nais.io/v1", kind = "Valkey", metadata = { - name = resourceName, - namespace = mainTeam:slug(), - annotations = { - ["console.nais.io/last-modified-at"] = NotNull(), - ["console.nais.io/last-modified-by"] = user:email(), - }, - labels = { - ["app.kubernetes.io/managed-by"] = "console", - ["nais.io/managed-by"] = "console", - }, - }, - spec = { - project = "aiven-dev", - projectVpcId = "aiven-vpc", - plan = "hobbyist", - cloudName = "google-europe-north1", - terminationProtection = true, - tags = { - environment = "dev", - team = mainTeam:slug(), - tenant = "some-tenant", - }, - }, - }) -end) - -Test.k8s("Validate hobbyist serviceintegration", function(t) - local resourceName = string.format("valkey-%s-foobar-hobbyist", mainTeam:slug()) - - t.check("aiven.io/v1alpha1", "serviceintegrations", "dev", mainTeam:slug(), resourceName, { - apiVersion = "aiven.io/v1alpha1", - kind = "ServiceIntegration", - metadata = { - name = resourceName, - namespace = mainTeam:slug(), annotations = { ["console.nais.io/last-modified-at"] = NotNull(), - ["console.nais.io/last-modified-by"] = user:email(), + ["console.nais.io/last-modified-by"] = "user@usersen.com", }, labels = { ["app.kubernetes.io/managed-by"] = "console", ["nais.io/managed-by"] = "console", }, - ownerReferences = { - { - apiVersion = "aiven.io/v1alpha1", - kind = "Valkey", - name = resourceName, - uid = NotNull(), - }, - }, + name = "foobar-hobbyist", + namespace = "someteamname", }, spec = { - project = "aiven-dev", - destinationEndpointId = "endpoint-id", - integrationType = "prometheus", - sourceServiceName = resourceName, + memory = "1GB", + tier = "SingleNode", }, }) end) @@ -499,41 +398,6 @@ Test.gql("List valkeys for team", function(t) } end) -Test.gql("Update non-console managed Valkey as team-member", function(t) - t.addHeader("x-user-email", user:email()) - t.query [[ - mutation UpdateValkey { - updateValkey( - input: { - name: "not-managed" - environmentName: "dev" - teamSlug: "someteamname" - tier: HIGH_AVAILABILITY - memory: GB_4 - maxMemoryPolicy: ALLKEYS_RANDOM - notifyKeyspaceEvents: "Exd" - } - ) { - valkey { - name - } - } - } - ]] - - t.check { - errors = { - { - message = "Valkey someteamname/not-managed is not managed by Console", - path = { - "updateValkey", - }, - }, - }, - data = Null, - } -end) - Test.gql("Update Valkey with tier and memory equivalent to hobbyist plan", function(t) t.addHeader("x-user-email", user:email()) t.query [[ @@ -568,38 +432,26 @@ Test.gql("Update Valkey with tier and memory equivalent to hobbyist plan", funct end) Test.k8s("Validate hobbyist Valkey resource after update", function(t) - local resourceName = string.format("valkey-%s-foobar", mainTeam:slug()) - - t.check("aiven.io/v1alpha1", "valkeys", "dev", mainTeam:slug(), resourceName, { - apiVersion = "aiven.io/v1alpha1", + t.check("nais.io/v1", "valkeys", "dev", mainTeam:slug(), "foobar", { + apiVersion = "nais.io/v1", kind = "Valkey", metadata = { - name = resourceName, - namespace = mainTeam:slug(), annotations = { ["console.nais.io/last-modified-at"] = NotNull(), - ["console.nais.io/last-modified-by"] = user:email(), + ["console.nais.io/last-modified-by"] = "user@usersen.com", }, labels = { ["app.kubernetes.io/managed-by"] = "console", ["nais.io/managed-by"] = "console", }, + name = "foobar", + namespace = "someteamname", }, spec = { - project = "aiven-dev", - projectVpcId = "aiven-vpc", - plan = "hobbyist", - cloudName = "google-europe-north1", - terminationProtection = true, - userConfig = { - valkey_maxmemory_policy = "allkeys-random", - valkey_notify_keyspace_events = "Exd", - }, - tags = { - environment = "dev", - team = mainTeam:slug(), - tenant = "some-tenant", - }, + maxMemoryPolicy = "allkeys-random", + memory = "1GB", + notifyKeyspaceEvents = "Exd", + tier = "SingleNode", }, }) end) @@ -732,35 +584,6 @@ Test.gql("Verify activity log after deleting valkey", function(t) } end) -Test.gql("Delete non-managed valkey as team-member", function(t) - t.addHeader("x-user-email", user:email()) - t.query [[ - mutation DeleteValkey { - deleteValkey( - input: { - name: "not-managed" - environmentName: "dev" - teamSlug: "someteamname" - } - ) { - valkeyDeleted - } - } - ]] - - t.check { - errors = { - { - message = "Valkey someteamname/not-managed is not managed by Console", - path = { - "deleteValkey", - }, - }, - }, - data = Null, - } -end) - Test.gql("Verify activity log for valkey operations", function(t) t.addHeader("x-user-email", user:email()) t.query(string.format([[ diff --git a/internal/cmd/api/http.go b/internal/cmd/api/http.go index acae8e209..a667cba6b 100644 --- a/internal/cmd/api/http.go +++ b/internal/cmd/api/http.go @@ -314,8 +314,8 @@ func ConfigureGraph( ctx = workload.NewLoaderContext(ctx, watchers.PodWatcher) ctx = secret.NewLoaderContext(ctx, watchers.SecretWatcher, secretClientCreator, dynamicClients, clusters, log) ctx = aiven.NewLoaderContext(ctx, aivenProjects) - ctx = opensearch.NewLoaderContext(ctx, tenantName, watchers.OpenSearchWatcher, aivenClient, log) - ctx = valkey.NewLoaderContext(ctx, tenantName, watchers.ValkeyWatcher, aivenClient) + ctx = opensearch.NewLoaderContext(ctx, tenantName, watchers.OpenSearchWatcher, watchers.NaisOpenSearchWatcher, aivenClient, log) + ctx = valkey.NewLoaderContext(ctx, tenantName, watchers.ValkeyWatcher, watchers.NaisValkeyWatcher, aivenClient) ctx = price.NewLoaderContext(ctx, priceRetriever, log) ctx = utilization.NewLoaderContext(ctx, prometheusClient, log) ctx = alerts.NewLoaderContext(ctx, prometheusClient, log) diff --git a/internal/graph/apierror/apierror.go b/internal/graph/apierror/apierror.go index 181fd663b..20ccca0f6 100644 --- a/internal/graph/apierror/apierror.go +++ b/internal/graph/apierror/apierror.go @@ -64,6 +64,8 @@ func GetErrorPresenter(log logrus.FieldLogger) graphql.ErrorPresenterFunc { case k8serrors.IsNotFound(unwrappedError): unwrappedError = ErrNotFound case k8serrors.IsForbidden(unwrappedError): + log.WithError(unwrappedError).Errorf("kubernetes api permission error") + unwrappedError = ErrForbidden } diff --git a/internal/graph/servicemaintenance.resolvers.go b/internal/graph/servicemaintenance.resolvers.go index 403191feb..53aa00d02 100644 --- a/internal/graph/servicemaintenance.resolvers.go +++ b/internal/graph/servicemaintenance.resolvers.go @@ -11,6 +11,7 @@ import ( "github.com/nais/api/internal/persistence/opensearch" "github.com/nais/api/internal/persistence/valkey" "github.com/nais/api/internal/servicemaintenance" + "github.com/nais/api/internal/thirdparty/aiven" ) func (r *mutationResolver) StartValkeyMaintenance(ctx context.Context, input servicemaintenance.StartValkeyMaintenanceInput) (*servicemaintenance.StartValkeyMaintenancePayload, error) { @@ -42,8 +43,12 @@ func (r *mutationResolver) StartOpenSearchMaintenance(ctx context.Context, input } func (r *openSearchResolver) Maintenance(ctx context.Context, obj *opensearch.OpenSearch) (*servicemaintenance.OpenSearchMaintenance, error) { + project, err := aiven.GetProject(ctx, obj.EnvironmentName) + if err != nil { + return nil, err + } return &servicemaintenance.OpenSearchMaintenance{ - AivenProject: obj.AivenProject, + AivenProject: project.ID, ServiceName: obj.FullyQualifiedName(), }, nil } @@ -88,8 +93,12 @@ func (r *openSearchMaintenanceResolver) Updates(ctx context.Context, obj *servic } func (r *valkeyResolver) Maintenance(ctx context.Context, obj *valkey.Valkey) (*servicemaintenance.ValkeyMaintenance, error) { + project, err := aiven.GetProject(ctx, obj.EnvironmentName) + if err != nil { + return nil, err + } return &servicemaintenance.ValkeyMaintenance{ - AivenProject: obj.AivenProject, + AivenProject: project.ID, ServiceName: obj.FullyQualifiedName(), }, nil } diff --git a/internal/kubernetes/fake/fake.go b/internal/kubernetes/fake/fake.go index ca564f30e..6baed40e2 100644 --- a/internal/kubernetes/fake/fake.go +++ b/internal/kubernetes/fake/fake.go @@ -13,7 +13,8 @@ import ( "github.com/nais/api/internal/kubernetes" "github.com/nais/api/internal/kubernetes/watcher" liberator_aiven_io_v1alpha1 "github.com/nais/liberator/pkg/apis/aiven.io/v1alpha1" - data_nais_io_v1 "github.com/nais/liberator/pkg/apis/data.nais.io/v1" + 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" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -218,6 +219,8 @@ func NewDynamicClient(scheme *runtime.Scheme) *dynfake.FakeDynamicClient { 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("valkeys"): "ValkeyList", + mapperatorv1.GroupVersion.WithResource("opensearches"): "OpenSearchList", }) // Add reactor for JSON Patch support diff --git a/internal/kubernetes/scheme.go b/internal/kubernetes/scheme.go index 975470840..60e0a63e2 100644 --- a/internal/kubernetes/scheme.go +++ b/internal/kubernetes/scheme.go @@ -5,11 +5,12 @@ import ( storage_cnrm_cloud_gogle_com_v1beta1 "github.com/GoogleCloudPlatform/k8s-config-connector/pkg/clients/generated/apis/storage/v1beta1" liberator_aiven_io_v1alpha1 "github.com/nais/liberator/pkg/apis/aiven.io/v1alpha1" aiven_nais_io_v1 "github.com/nais/liberator/pkg/apis/aiven.nais.io/v1" - data_nais_io_v1 "github.com/nais/liberator/pkg/apis/data.nais.io/v1" bigquery_nais_io_v1 "github.com/nais/liberator/pkg/apis/google.nais.io/v1" 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" authorizationv1 "k8s.io/api/authorization/v1" @@ -40,6 +41,7 @@ func NewScheme() (*runtime.Scheme, error) { aiven_nais_io_v1.AddToScheme, data_nais_io_v1.AddToScheme, authorizationv1.AddToScheme, + mapperatorv1.AddToScheme, } for _, f := range funcs { diff --git a/internal/kubernetes/utils.go b/internal/kubernetes/utils.go new file mode 100644 index 000000000..4c49491ca --- /dev/null +++ b/internal/kubernetes/utils.go @@ -0,0 +1,24 @@ +package kubernetes + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" +) + +func ToUnstructured(obj any) (*unstructured.Unstructured, error) { + mp, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj) + if err != nil { + return nil, err + } + + return &unstructured.Unstructured{Object: mp}, nil +} + +func ToConcrete[T any](u *unstructured.Unstructured) (*T, error) { + var obj T + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &obj); err != nil { + return nil, err + } + + return &obj, nil +} diff --git a/internal/kubernetes/watcher/cluster_watcher.go b/internal/kubernetes/watcher/cluster_watcher.go index 1815b075a..4eb9e4d53 100644 --- a/internal/kubernetes/watcher/cluster_watcher.go +++ b/internal/kubernetes/watcher/cluster_watcher.go @@ -135,6 +135,7 @@ func (w *clusterWatcher[T]) OnDelete(obj any) { w.watcher.remove(w.cluster, t) } +// Delete will delete the resource using an imperonated client. func (w *clusterWatcher[T]) Delete(ctx context.Context, namespace, name string) error { client, err := w.ImpersonatedClient(ctx) if err != nil { diff --git a/internal/kubernetes/watcher/watcher.go b/internal/kubernetes/watcher/watcher.go index 3a5dce45c..881821f78 100644 --- a/internal/kubernetes/watcher/watcher.go +++ b/internal/kubernetes/watcher/watcher.go @@ -276,6 +276,7 @@ func (w *Watcher[T]) GetByNamespace(namespace string, filter ...Filter) []*Envir return ret } +// Delete will delete the resource using an imperonated client. func (w *Watcher[T]) Delete(ctx context.Context, cluster, namespace string, name string) error { for _, watcher := range w.watchers { if watcher.cluster == cluster { diff --git a/internal/kubernetes/watchers/watchers.go b/internal/kubernetes/watchers/watchers.go index 08bdf50fa..eef5dbb4a 100644 --- a/internal/kubernetes/watchers/watchers.go +++ b/internal/kubernetes/watchers/watchers.go @@ -31,6 +31,7 @@ type ( 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] @@ -41,6 +42,7 @@ type ( NamespaceWatcher = watcher.Watcher[*v1.Namespace] UnleashWatcher = watcher.Watcher[*unleash.UnleashInstance] SecretWatcher = watcher.Watcher[*secret.Secret] + NaisValkeyWatcher = watcher.Watcher[*valkey.Valkey] ) type Watchers struct { @@ -50,6 +52,7 @@ type Watchers struct { BqWatcher *BqWatcher ValkeyWatcher *ValkeyWatcher OpenSearchWatcher *OpenSearchWatcher + NaisOpenSearchWatcher *NaisOpenSearchWatcher BucketWatcher *BucketWatcher SqlDatabaseWatcher *SqlDatabaseWatcher SqlInstanceWatcher *SqlInstanceWatcher @@ -60,6 +63,7 @@ type Watchers struct { NamespaceWatcher *NamespaceWatcher UnleashWatcher *UnleashWatcher SecretWatcher *SecretWatcher + NaisValkeyWatcher *NaisValkeyWatcher } func SetupWatchers( @@ -74,6 +78,7 @@ func SetupWatchers( 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), @@ -84,5 +89,6 @@ func SetupWatchers( NamespaceWatcher: team.NewNamespaceWatcher(ctx, watcherMgr), UnleashWatcher: unleash.NewWatcher(ctx, mgmtWatcherMgr), SecretWatcher: secret.NewWatcher(ctx, watcherMgr), + NaisValkeyWatcher: valkey.NewNaisValkeyWatcher(ctx, watcherMgr), } } diff --git a/internal/persistence/opensearch/client.go b/internal/persistence/opensearch/client.go index f31967f7f..533c1ddce 100644 --- a/internal/persistence/opensearch/client.go +++ b/internal/persistence/opensearch/client.go @@ -3,16 +3,13 @@ package opensearch import ( "context" - "github.com/nais/api/internal/kubernetes/watcher" "github.com/nais/api/internal/slug" "github.com/nais/api/internal/workload" "github.com/nais/api/internal/workload/application" "github.com/nais/api/internal/workload/job" ) -type client struct { - watcher *watcher.Watcher[*OpenSearch] -} +type client struct{} func instanceNamer(teamSlug slug.Slug, instanceName string) string { return namePrefix(teamSlug) + instanceName diff --git a/internal/persistence/opensearch/dataloader.go b/internal/persistence/opensearch/dataloader.go index 474d6deba..64d6b54d0 100644 --- a/internal/persistence/opensearch/dataloader.go +++ b/internal/persistence/opensearch/dataloader.go @@ -4,13 +4,17 @@ import ( "context" "github.com/nais/api/internal/graph/loader" + "github.com/nais/api/internal/kubernetes" "github.com/nais/api/internal/kubernetes/watcher" + "github.com/nais/api/internal/slug" "github.com/nais/api/internal/thirdparty/aiven" + naiscrd "github.com/nais/pgrator/pkg/api/v1" "github.com/sirupsen/logrus" "github.com/sourcegraph/conc/pool" "github.com/vikstrous/dataloadgen" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" ) type ctxKey int @@ -22,8 +26,8 @@ type AivenDataLoaderKey struct { ServiceName string } -func NewLoaderContext(ctx context.Context, tenantName string, watcher *watcher.Watcher[*OpenSearch], aivenClient aiven.AivenClient, logger logrus.FieldLogger) context.Context { - return context.WithValue(ctx, loadersKey, newLoaders(tenantName, watcher, aivenClient, logger)) +func NewLoaderContext(ctx context.Context, tenantName string, openSearchWatcher, naisOpenSearchWatcher *watcher.Watcher[*OpenSearch], aivenClient aiven.AivenClient, logger logrus.FieldLogger) context.Context { + return context.WithValue(ctx, loadersKey, newLoaders(tenantName, openSearchWatcher, naisOpenSearchWatcher, aivenClient, logger)) } func NewWatcher(ctx context.Context, mgr *watcher.Manager) *watcher.Watcher[*OpenSearch] { @@ -42,6 +46,26 @@ func NewWatcher(ctx context.Context, mgr *watcher.Manager) *watcher.Watcher[*Ope return w } +func NewNaisOpenSearchWatcher(ctx context.Context, mgr *watcher.Manager) *watcher.Watcher[*OpenSearch] { + w := watcher.Watch(mgr, &OpenSearch{}, watcher.WithConverter(func(o *unstructured.Unstructured, environmentName string) (obj any, ok bool) { + v, err := kubernetes.ToConcrete[naiscrd.OpenSearch](o) + if err != nil { + return nil, false + } + ret, err := toOpenSearchFromNais(v, environmentName) + if err != nil { + return nil, false + } + return ret, true + }), watcher.WithGVR(schema.GroupVersionResource{ + Group: "nais.io", + Version: "v1", + Resource: "opensearches", + })) + w.Start(ctx) + return w +} + func fromContext(ctx context.Context) *loaders { return ctx.Value(loadersKey).(*loaders) } @@ -49,27 +73,38 @@ func fromContext(ctx context.Context) *loaders { type loaders struct { client *client watcher *watcher.Watcher[*OpenSearch] + naisWatcher *watcher.Watcher[*OpenSearch] versionLoader *dataloadgen.Loader[*AivenDataLoaderKey, string] tenantName string aivenClient aiven.AivenClient } -func newLoaders(tenantName string, watcher *watcher.Watcher[*OpenSearch], aivenClient aiven.AivenClient, logger logrus.FieldLogger) *loaders { - client := &client{ - watcher: watcher, - } +func newLoaders(tenantName string, watcher, naisOpenSearchWatcher *watcher.Watcher[*OpenSearch], aivenClient aiven.AivenClient, logger logrus.FieldLogger) *loaders { + client := &client{} versionLoader := &dataloader{aivenClient: aivenClient, log: logger} return &loaders{ client: client, watcher: watcher, + naisWatcher: naisOpenSearchWatcher, tenantName: tenantName, versionLoader: dataloadgen.NewLoader(versionLoader.getVersions, loader.DefaultDataLoaderOptions...), aivenClient: aivenClient, } } +func newK8sClient(ctx context.Context, environmentName string, teamSlug slug.Slug) (dynamic.ResourceInterface, error) { + sysClient, err := fromContext(ctx).naisWatcher.SystemAuthenticatedClient( + ctx, + environmentName, + ) + if err != nil { + return nil, err + } + return sysClient.Namespace(teamSlug.String()), nil +} + type dataloader struct { aivenClient aiven.AivenClient log logrus.FieldLogger diff --git a/internal/persistence/opensearch/models.go b/internal/persistence/opensearch/models.go index f0cad5c55..e5e0b68b0 100644 --- a/internal/persistence/opensearch/models.go +++ b/internal/persistence/opensearch/models.go @@ -17,6 +17,8 @@ import ( "github.com/nais/api/internal/validate" "github.com/nais/api/internal/workload" aiven_io_v1alpha1 "github.com/nais/liberator/pkg/apis/aiven.io/v1alpha1" + "github.com/nais/pgrator/pkg/api" + naiscrd "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" @@ -32,17 +34,16 @@ type ( ) type OpenSearch struct { - Name string `json:"name"` - Status *OpenSearchStatus `json:"status"` - TerminationProtection bool `json:"terminationProtection"` - Tier OpenSearchTier `json:"tier"` - Memory OpenSearchMemory `json:"memory"` - StorageGB StorageGB `json:"storageGB"` - TeamSlug slug.Slug `json:"-"` - EnvironmentName string `json:"-"` - WorkloadReference *workload.Reference `json:"-"` - AivenProject string `json:"-"` - MajorVersion OpenSearchMajorVersion `json:"-"` + Name string `json:"name"` + Status *naiscrd.OpenSearchStatus `json:"status"` + TerminationProtection bool `json:"terminationProtection"` + Tier OpenSearchTier `json:"tier"` + Memory OpenSearchMemory `json:"memory"` + StorageGB StorageGB `json:"storageGB"` + TeamSlug slug.Slug `json:"-"` + EnvironmentName string `json:"-"` + WorkloadReference *workload.Reference `json:"-"` + MajorVersion OpenSearchMajorVersion `json:"-"` } func (OpenSearch) IsPersistence() {} @@ -158,6 +159,14 @@ func toOpenSearch(u *unstructured.Unstructured, envName string) (*OpenSearch, er return nil, fmt.Errorf("converting to OpenSearch: %w", err) } + if len(obj.GetOwnerReferences()) > 0 { + for _, ownerRef := range obj.GetOwnerReferences() { + if ownerRef.Kind == "OpenSearch" { + return nil, fmt.Errorf("skipping OpenSearch %s in namespace %s because it has an owner reference", obj.GetName(), obj.GetNamespace()) + } + } + } + // Liberator doesn't contain this field, so we read it directly from the unstructured object terminationProtection, _, _ := unstructured.NestedBool(u.Object, specTerminationProtection...) @@ -192,13 +201,13 @@ func toOpenSearch(u *unstructured.Unstructured, envName string) (*OpenSearch, er Name: name, EnvironmentName: envName, TerminationProtection: terminationProtection, - Status: &OpenSearchStatus{ - Conditions: obj.Status.Conditions, - State: obj.Status.State, + Status: &naiscrd.OpenSearchStatus{ + BaseStatus: api.BaseStatus{ + Conditions: obj.Status.Conditions, + }, }, TeamSlug: slug.Slug(obj.GetNamespace()), WorkloadReference: workload.ReferenceFromOwnerReferences(obj.GetOwnerReferences()), - AivenProject: obj.Spec.Project, Tier: machine.Tier, Memory: machine.Memory, MajorVersion: majorVersion, @@ -206,6 +215,22 @@ func toOpenSearch(u *unstructured.Unstructured, envName string) (*OpenSearch, er }, nil } +func toOpenSearchFromNais(o *naiscrd.OpenSearch, envName string) (*OpenSearch, error) { + majorVersion := fromMapperatorVersion(o.Spec.Version) + + return &OpenSearch{ + Name: o.Name, + EnvironmentName: envName, + Status: o.Status, + TeamSlug: slug.Slug(o.Namespace), + WorkloadReference: workload.ReferenceFromOwnerReferences(o.OwnerReferences), + Tier: fromMapperatorTier(o.Spec.Tier), + Memory: fromMapperatorMemory(o.Spec.Memory), + MajorVersion: majorVersion, + StorageGB: StorageGB(o.Spec.StorageGB), + }, nil +} + type TeamInventoryCountOpenSearches struct { Total int } diff --git a/internal/persistence/opensearch/queries.go b/internal/persistence/opensearch/queries.go index 76ca96d8b..7c2188abe 100644 --- a/internal/persistence/opensearch/queries.go +++ b/internal/persistence/opensearch/queries.go @@ -2,6 +2,7 @@ package opensearch import ( "context" + "errors" "fmt" "strings" @@ -16,15 +17,15 @@ import ( "github.com/nais/api/internal/slug" "github.com/nais/api/internal/thirdparty/aiven" nais_io_v1 "github.com/nais/liberator/pkg/apis/nais.io/v1" + "github.com/nais/pgrator/pkg/api" + naiscrd "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/utils/ptr" ) var ( specDiskSpace = []string{"spec", "disk_space"} - specPlan = []string{"spec", "plan"} specTerminationProtection = []string{"spec", "terminationProtection"} specOpenSearchVersion = []string{"spec", "userConfig", "opensearch_version"} ) @@ -39,15 +40,23 @@ func GetByIdent(ctx context.Context, id ident.Ident) (*OpenSearch, error) { } func Get(ctx context.Context, teamSlug slug.Slug, environment, name string) (*OpenSearch, error) { - prefix := instanceNamer(teamSlug, "") - if !strings.HasPrefix(name, prefix) { - name = instanceNamer(teamSlug, name) + v, err := fromContext(ctx).naisWatcher.Get(environment, teamSlug.String(), name) + if errors.Is(err, &watcher.ErrorNotFound{}) { + prefix := instanceNamer(teamSlug, "") + if !strings.HasPrefix(name, prefix) { + name = instanceNamer(teamSlug, name) + } + v, err = fromContext(ctx).watcher.Get(environment, teamSlug.String(), name) } - return fromContext(ctx).client.watcher.Get(environment, teamSlug.String(), name) + return v, err } func State(ctx context.Context, os *OpenSearch) (OpenSearchState, error) { - s, err := fromContext(ctx).aivenClient.ServiceGet(ctx, os.AivenProject, os.FullyQualifiedName()) + project, err := aiven.GetProject(ctx, os.EnvironmentName) + if err != nil { + return OpenSearchStateUnknown, err + } + s, err := fromContext(ctx).aivenClient.ServiceGet(ctx, project.ID, os.FullyQualifiedName()) if err != nil { // The OpenSearch instance may not have been created in Aiven yet, or it has been deleted. // In both cases, we return "unknown" state rather than an error. @@ -80,7 +89,9 @@ func ListForTeam(ctx context.Context, teamSlug slug.Slug, page *pagination.Pagin } func ListAllForTeam(ctx context.Context, teamSlug slug.Slug) []*OpenSearch { - all := fromContext(ctx).client.watcher.GetByNamespace(teamSlug.String(), watcher.WithoutDeleted()) + all := fromContext(ctx).watcher.GetByNamespace(teamSlug.String(), watcher.WithoutDeleted()) + allNais := fromContext(ctx).naisWatcher.GetByNamespace(teamSlug.String(), watcher.WithoutDeleted()) + all = append(all, allNais...) return watcher.Objects(all) } @@ -114,8 +125,12 @@ func ListAccess(ctx context.Context, openSearch *OpenSearch, page *pagination.Pa } func GetOpenSearchVersion(ctx context.Context, os *OpenSearch) (*OpenSearchVersion, error) { + project, err := aiven.GetProject(ctx, os.EnvironmentName) + if err != nil { + return nil, err + } key := AivenDataLoaderKey{ - Project: os.AivenProject, + Project: project.ID, ServiceName: os.FullyQualifiedName(), } @@ -134,7 +149,7 @@ func GetOpenSearchVersion(ctx context.Context, os *OpenSearch) (*OpenSearchVersi } if major == "" { - major = OpenSearchMajorVersionV2 + major = OpenSearchMajorVersionV3_3 } return &OpenSearchVersion{ @@ -148,7 +163,7 @@ func GetForWorkload(ctx context.Context, teamSlug slug.Slug, environment string, return nil, nil } - return fromContext(ctx).client.watcher.Get(environment, teamSlug.String(), instanceNamer(teamSlug, reference.Instance)) + return Get(ctx, teamSlug, environment, reference.Instance) } func orderOpenSearch(ctx context.Context, ret []*OpenSearch, orderBy *OpenSearchOrder) { @@ -167,64 +182,49 @@ func Create(ctx context.Context, input CreateOpenSearchInput) (*CreateOpenSearch return nil, err } - namespace := input.TeamSlug.String() - client, err := fromContext(ctx).watcher.ImpersonatedClientWithNamespace(ctx, input.EnvironmentName, namespace) + client, err := newK8sClient(ctx, input.EnvironmentName, input.TeamSlug) if err != nil { return nil, err } - machine, err := machineTypeFromTierAndMemory(input.Tier, input.Memory) - if err != nil { - return nil, err + // Ensure there's no existing Aiven OpenSearch with the same name + // This can be removed when we manage all opensearches through Console + _, err = fromContext(ctx).watcher.Get(input.EnvironmentName, input.TeamSlug.String(), instanceNamer(input.TeamSlug, input.Name)) + if !errors.Is(err, &watcher.ErrorNotFound{}) { + return nil, apierror.Errorf("OpenSearch with the name %q already exists, but are not yet managed through Console.", input.Name) } - res := &unstructured.Unstructured{} - res.SetAPIVersion("aiven.io/v1alpha1") - res.SetKind("OpenSearch") - res.SetName(instanceNamer(input.TeamSlug, input.Name)) - res.SetNamespace(namespace) + res := &naiscrd.OpenSearch{ + TypeMeta: metav1.TypeMeta{ + Kind: "OpenSearch", + APIVersion: "nais.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: input.Name, + Namespace: input.TeamSlug.String(), + }, + Spec: naiscrd.OpenSearchSpec{ + Tier: toMapperatorTier(input.Tier), + Memory: toMapperatorMemory(input.Memory), + Version: toMapperatorVersion(input.Version), + StorageGB: int(input.StorageGB), + }, + } res.SetAnnotations(kubernetes.WithCommonAnnotations(nil, authz.ActorFromContext(ctx).User.Identity())) kubernetes.SetManagedByConsoleLabel(res) - aivenProject, err := aiven.GetProject(ctx, input.EnvironmentName) + obj, err := kubernetes.ToUnstructured(res) if err != nil { return nil, err } - version, err := input.Version.ToAivenString() - if err != nil { - return nil, err - } - - res.Object["spec"] = map[string]any{ - "cloudName": "google-europe-north1", - "plan": machine.AivenPlan, - "project": aivenProject.ID, - "projectVpcId": aivenProject.VPC, - "disk_space": input.StorageGB.ToAivenString(), - "terminationProtection": true, - "tags": map[string]any{ - "environment": input.EnvironmentName, - "team": namespace, - "tenant": fromContext(ctx).tenantName, - }, - "userConfig": map[string]any{ - "opensearch_version": version, - }, - } - ret, err := client.Create(ctx, res, metav1.CreateOptions{}) - if err != nil { + if _, err = client.Create(ctx, obj, metav1.CreateOptions{}); err != nil { if k8serrors.IsAlreadyExists(err) { return nil, apierror.ErrAlreadyExists } return nil, err } - err = aiven.UpsertPrometheusServiceIntegration(ctx, fromContext(ctx).watcher, ret, aivenProject, input.EnvironmentName) - if err != nil { - return nil, fmt.Errorf("creating Prometheus service integration: %w", err) - } - err = activitylog.Create(ctx, activitylog.CreateInput{ Action: activitylog.ActivityLogEntryActionCreated, Actor: authz.ActorFromContext(ctx).User, @@ -237,7 +237,7 @@ func Create(ctx context.Context, input CreateOpenSearchInput) (*CreateOpenSearch return nil, err } - os, err := toOpenSearch(ret, input.EnvironmentName) + os, err := toOpenSearchFromNais(res, input.EnvironmentName) if err != nil { return nil, err } @@ -252,41 +252,38 @@ func Update(ctx context.Context, input UpdateOpenSearchInput) (*UpdateOpenSearch return nil, err } - client, err := fromContext(ctx).watcher.ImpersonatedClientWithNamespace(ctx, input.EnvironmentName, input.TeamSlug.String()) + client, err := newK8sClient(ctx, input.EnvironmentName, input.TeamSlug) if err != nil { return nil, err } - openSearch, err := client.Get(ctx, instanceNamer(input.TeamSlug, input.Name), metav1.GetOptions{}) + openSearch, err := client.Get(ctx, input.Name, metav1.GetOptions{}) if err != nil { return nil, err } - if !kubernetes.HasManagedByConsoleLabel(openSearch) { - return nil, apierror.Errorf("OpenSearch %s/%s is not managed by Console", input.TeamSlug, input.Name) - } - changes := make([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, 0) - - res, err := updatePlan(openSearch, input) + concreteOpenSearch, err := kubernetes.ToConcrete[naiscrd.OpenSearch](openSearch) if err != nil { return nil, err } - changes = append(changes, res...) - res, err = updateVersion(ctx, openSearch, input) - if err != nil { - return nil, err + changes := make([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, 0) + updateFuncs := []func(*naiscrd.OpenSearch, UpdateOpenSearchInput) ([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, error){ + updateTier, + updateMemory, + updateVersion, + updateStorage, } - changes = append(changes, res...) - res, err = updateStorage(openSearch, input) - if err != nil { - return nil, err + for _, f := range updateFuncs { + res, err := f(concreteOpenSearch, input) + if err != nil { + return nil, err + } + changes = append(changes, res...) } - changes = append(changes, res...) if len(changes) == 0 { - // No changes to update os, err := toOpenSearch(openSearch, input.EnvironmentName) if err != nil { return nil, err @@ -297,21 +294,16 @@ func Update(ctx context.Context, input UpdateOpenSearchInput) (*UpdateOpenSearch }, nil } - openSearch.SetAnnotations(kubernetes.WithCommonAnnotations(openSearch.GetAnnotations(), authz.ActorFromContext(ctx).User.Identity())) - - ret, err := client.Update(ctx, openSearch, metav1.UpdateOptions{}) + obj, err := kubernetes.ToUnstructured(concreteOpenSearch) if err != nil { return nil, err } - aivenProject, err := aiven.GetProject(ctx, input.EnvironmentName) - if err != nil { - return nil, err - } + obj.SetAnnotations(kubernetes.WithCommonAnnotations(obj.GetAnnotations(), authz.ActorFromContext(ctx).User.Identity())) - err = aiven.UpsertPrometheusServiceIntegration(ctx, fromContext(ctx).watcher, ret, aivenProject, input.EnvironmentName) + ret, err := client.Update(ctx, obj, metav1.UpdateOptions{}) if err != nil { - return nil, fmt.Errorf("creating Prometheus service integration: %w", err) + return nil, err } err = activitylog.Create(ctx, activitylog.CreateInput{ @@ -329,158 +321,106 @@ func Update(ctx context.Context, input UpdateOpenSearchInput) (*UpdateOpenSearch return nil, err } - os, err := toOpenSearch(ret, input.EnvironmentName) - if err != nil { - return nil, err - } - - return &UpdateOpenSearchPayload{ - OpenSearch: os, - }, nil -} - -func updatePlan(openSearch *unstructured.Unstructured, input UpdateOpenSearchInput) ([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, error) { - changes := make([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, 0) - - desired, err := machineTypeFromTierAndMemory(input.Tier, input.Memory) + retOpenSearch, err := kubernetes.ToConcrete[naiscrd.OpenSearch](ret) if err != nil { return nil, err } - oldPlan, found, err := unstructured.NestedString(openSearch.Object, specPlan...) + osUpdated, err := toOpenSearchFromNais(retOpenSearch, input.EnvironmentName) if err != nil { return nil, err } - if !found { - // .spec.plan is a required field - return nil, fmt.Errorf("missing .spec.plan in OpenSearch resource") - } - if oldPlan == desired.AivenPlan { - return changes, nil - } + return &UpdateOpenSearchPayload{ + OpenSearch: osUpdated, + }, nil +} - oldMachine, err := machineTypeFromPlan(oldPlan) - if err != nil { - return nil, err - } +func updateTier(openSearch *naiscrd.OpenSearch, input UpdateOpenSearchInput) ([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, error) { + changes := make([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, 0) - if input.Tier != oldMachine.Tier { + origTier := fromMapperatorTier(openSearch.Spec.Tier) + if input.Tier != origTier { changes = append(changes, &OpenSearchUpdatedActivityLogEntryDataUpdatedField{ Field: "tier", - OldValue: ptr.To(oldMachine.Tier.String()), + OldValue: ptr.To(origTier.String()), NewValue: ptr.To(input.Tier.String()), }) } - if input.Memory != oldMachine.Memory { + openSearch.Spec.Tier = toMapperatorTier(input.Tier) + + return changes, nil +} + +func updateMemory(openSearch *naiscrd.OpenSearch, input UpdateOpenSearchInput) ([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, error) { + changes := make([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, 0) + + origMemory := fromMapperatorMemory(openSearch.Spec.Memory) + if input.Memory != origMemory { changes = append(changes, &OpenSearchUpdatedActivityLogEntryDataUpdatedField{ Field: "memory", - OldValue: ptr.To(oldMachine.Memory.String()), + OldValue: ptr.To(origMemory.String()), NewValue: ptr.To(input.Memory.String()), }) } - if err := unstructured.SetNestedField(openSearch.Object, desired.AivenPlan, specPlan...); err != nil { - return nil, err - } + openSearch.Spec.Memory = toMapperatorMemory(input.Memory) + return changes, nil } -func updateVersion(ctx context.Context, openSearch *unstructured.Unstructured, input UpdateOpenSearchInput) ([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, error) { - changes := make([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, 0) - - oldVersion, found, err := unstructured.NestedString(openSearch.Object, specOpenSearchVersion...) - if err != nil { - return nil, err - } - if !found { - os, err := toOpenSearch(openSearch, input.EnvironmentName) - if err != nil { - return nil, err - } - version, err := GetOpenSearchVersion(ctx, os) - if err != nil { - return nil, err - } +func updateVersion(openSearch *naiscrd.OpenSearch, input UpdateOpenSearchInput) ([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, error) { + origVersion := fromMapperatorVersion(openSearch.Spec.Version) - oldVersion = *version.Actual + if origVersion == input.Version { + return nil, nil } - oldMajorVersion, err := OpenSearchMajorVersionFromAivenString(oldVersion) - if err != nil { + if err := input.Version.ValidateUpgradePath(origVersion); err != nil { return nil, err } - if oldMajorVersion == input.Version { - return changes, nil - } + changes := make([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, 0) - if err := input.Version.ValidateUpgradePath(oldMajorVersion); err != nil { - return nil, err + var oldValue *string + if origVersion != "" { + oldValue = ptr.To(origVersion.String()) } changes = append(changes, &OpenSearchUpdatedActivityLogEntryDataUpdatedField{ - Field: "version", - OldValue: func() *string { - if found { - return ptr.To(oldVersion) - } - return nil - }(), + Field: "version", + OldValue: oldValue, NewValue: ptr.To(input.Version.String()), }) - version, err := input.Version.ToAivenString() - if err != nil { - return nil, err - } + openSearch.Spec.Version = toMapperatorVersion(input.Version) - if err := unstructured.SetNestedField(openSearch.Object, version, specOpenSearchVersion...); err != nil { - return nil, err - } return changes, nil } -func updateStorage(openSearch *unstructured.Unstructured, input UpdateOpenSearchInput) ([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, error) { - changes := make([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, 0) +func updateStorage(openSearch *naiscrd.OpenSearch, input UpdateOpenSearchInput) ([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, error) { + oldStorageGB := StorageGB(openSearch.Spec.StorageGB) - desired, err := machineTypeFromTierAndMemory(input.Tier, input.Memory) - if err != nil { - return nil, err + if oldStorageGB == input.StorageGB { + return nil, nil } - oldAivenDiskSpace, found, err := unstructured.NestedString(openSearch.Object, specDiskSpace...) - if err != nil { - return nil, err - } - // default to minimum storage capacity for the selected plan, in case the field is not set explicitly - oldStorageGB := desired.StorageMin - if found { - oldStorageGB, err = StorageGBFromAivenString(oldAivenDiskSpace) - if err != nil { - return nil, err - } - } + changes := make([]*OpenSearchUpdatedActivityLogEntryDataUpdatedField, 0) - if oldStorageGB == input.StorageGB { - return changes, nil + var oldValue *string + if oldStorageGB > 0 { + oldValue = ptr.To(oldStorageGB.String()) } changes = append(changes, &OpenSearchUpdatedActivityLogEntryDataUpdatedField{ - Field: "storageGB", - OldValue: func() *string { - if found { - return ptr.To(oldStorageGB.String()) - } - return nil - }(), + Field: "storageGB", + OldValue: oldValue, NewValue: ptr.To(input.StorageGB.String()), }) - if err := unstructured.SetNestedField(openSearch.Object, input.StorageGB.ToAivenString(), specDiskSpace...); err != nil { - return nil, err - } + openSearch.Spec.StorageGB = int(input.StorageGB) + return changes, nil } @@ -489,36 +429,35 @@ func Delete(ctx context.Context, input DeleteOpenSearchInput) (*DeleteOpenSearch return nil, err } - name := instanceNamer(input.TeamSlug, input.Name) - client, err := fromContext(ctx).watcher.ImpersonatedClientWithNamespace(ctx, input.EnvironmentName, input.TeamSlug.String()) + client, err := newK8sClient(ctx, input.EnvironmentName, input.TeamSlug) if err != nil { return nil, err } - os, err := client.Get(ctx, name, metav1.GetOptions{}) + openSearch, err := client.Get(ctx, input.Name, metav1.GetOptions{}) if err != nil { return nil, err } - if !kubernetes.HasManagedByConsoleLabel(os) { + if !kubernetes.HasManagedByConsoleLabel(openSearch) { return nil, apierror.Errorf("OpenSearch %s/%s is not managed by Console", input.TeamSlug, input.Name) } - terminationProtection, found, err := unstructured.NestedBool(os.Object, specTerminationProtection...) - if err != nil { - return nil, err + annotations := openSearch.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) } - if found && terminationProtection { - if err := unstructured.SetNestedField(os.Object, false, specTerminationProtection...); err != nil { - return nil, err - } + if annotations[api.AllowDeletionAnnotation] != "true" { + annotations[api.AllowDeletionAnnotation] = "true" + openSearch.SetAnnotations(annotations) - if _, err = client.Update(ctx, os, metav1.UpdateOptions{}); err != nil { - return nil, fmt.Errorf("removing deletion protection: %w", err) + _, err = client.Update(ctx, openSearch, metav1.UpdateOptions{}) + if err != nil { + return nil, fmt.Errorf("set allow deletion annotation: %w", err) } } - if err := fromContext(ctx).watcher.Delete(ctx, input.EnvironmentName, input.TeamSlug.String(), name); err != nil { + if err := client.Delete(ctx, input.Name, metav1.DeleteOptions{}); err != nil { return nil, err } @@ -537,3 +476,93 @@ func Delete(ctx context.Context, input DeleteOpenSearchInput) (*DeleteOpenSearch OpenSearchDeleted: ptr.To(true), }, nil } + +func toMapperatorTier(tier OpenSearchTier) naiscrd.OpenSearchTier { + switch tier { + case OpenSearchTierSingleNode: + return naiscrd.OpenSearchTierSingleNode + case OpenSearchTierHighAvailability: + return naiscrd.OpenSearchTierHighAvailability + default: + return "" + } +} + +func fromMapperatorTier(tier naiscrd.OpenSearchTier) OpenSearchTier { + switch tier { + case naiscrd.OpenSearchTierSingleNode: + return OpenSearchTierSingleNode + case naiscrd.OpenSearchTierHighAvailability: + return OpenSearchTierHighAvailability + default: + return "" + } +} + +func toMapperatorMemory(memory OpenSearchMemory) naiscrd.OpenSearchMemory { + switch memory { + case OpenSearchMemoryGB2: + return naiscrd.OpenSearchMemory2GB + case OpenSearchMemoryGB4: + return naiscrd.OpenSearchMemory4GB + case OpenSearchMemoryGB8: + return naiscrd.OpenSearchMemory8GB + case OpenSearchMemoryGB16: + return naiscrd.OpenSearchMemory16GB + case OpenSearchMemoryGB32: + return naiscrd.OpenSearchMemory32GB + case OpenSearchMemoryGB64: + return naiscrd.OpenSearchMemory64GB + default: + return "" + } +} + +func fromMapperatorMemory(memory naiscrd.OpenSearchMemory) OpenSearchMemory { + switch memory { + case naiscrd.OpenSearchMemory2GB: + return OpenSearchMemoryGB2 + case naiscrd.OpenSearchMemory4GB: + return OpenSearchMemoryGB4 + case naiscrd.OpenSearchMemory8GB: + return OpenSearchMemoryGB8 + case naiscrd.OpenSearchMemory16GB: + return OpenSearchMemoryGB16 + case naiscrd.OpenSearchMemory32GB: + return OpenSearchMemoryGB32 + case naiscrd.OpenSearchMemory64GB: + return OpenSearchMemoryGB64 + default: + return "" + } +} + +func toMapperatorVersion(version OpenSearchMajorVersion) naiscrd.OpenSearchVersion { + switch version { + case OpenSearchMajorVersionV1: + return naiscrd.OpenSearchVersionV1 + case OpenSearchMajorVersionV2: + return naiscrd.OpenSearchVersionV2 + case OpenSearchMajorVersionV2_19: + return naiscrd.OpenSearchVersionV2_19 + case OpenSearchMajorVersionV3_3: + return naiscrd.OpenSearchVersionV3_3 + default: + return "" + } +} + +func fromMapperatorVersion(version naiscrd.OpenSearchVersion) OpenSearchMajorVersion { + switch version { + case naiscrd.OpenSearchVersionV1: + return OpenSearchMajorVersionV1 + case naiscrd.OpenSearchVersionV2: + return OpenSearchMajorVersionV2 + case naiscrd.OpenSearchVersionV2_19: + return OpenSearchMajorVersionV2_19 + case naiscrd.OpenSearchVersionV3_3: + return OpenSearchMajorVersionV3_3 + default: + return "" + } +} diff --git a/internal/persistence/valkey/client.go b/internal/persistence/valkey/client.go index 2717dd0c5..322778b87 100644 --- a/internal/persistence/valkey/client.go +++ b/internal/persistence/valkey/client.go @@ -3,16 +3,13 @@ package valkey import ( "context" - "github.com/nais/api/internal/kubernetes/watcher" "github.com/nais/api/internal/slug" "github.com/nais/api/internal/workload" "github.com/nais/api/internal/workload/application" "github.com/nais/api/internal/workload/job" ) -type client struct { - watcher *watcher.Watcher[*Valkey] -} +type client struct{} func namePrefix(teamSlug slug.Slug) string { return "valkey-" + teamSlug.String() + "-" diff --git a/internal/persistence/valkey/dataloader.go b/internal/persistence/valkey/dataloader.go index 81cc60a53..372694c2f 100644 --- a/internal/persistence/valkey/dataloader.go +++ b/internal/persistence/valkey/dataloader.go @@ -3,18 +3,22 @@ package valkey import ( "context" + "github.com/nais/api/internal/kubernetes" "github.com/nais/api/internal/kubernetes/watcher" + "github.com/nais/api/internal/slug" "github.com/nais/api/internal/thirdparty/aiven" + naiscrd "github.com/nais/pgrator/pkg/api/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" ) type ctxKey int const loadersKey ctxKey = iota -func NewLoaderContext(ctx context.Context, tenantName string, valkeyWatcher *watcher.Watcher[*Valkey], aivenClient aiven.AivenClient) context.Context { - return context.WithValue(ctx, loadersKey, newLoaders(tenantName, valkeyWatcher, aivenClient)) +func NewLoaderContext(ctx context.Context, tenantName string, valkeyWatcher, naisValkeyWatcher *watcher.Watcher[*Valkey], aivenClient aiven.AivenClient) context.Context { + return context.WithValue(ctx, loadersKey, newLoaders(tenantName, valkeyWatcher, naisValkeyWatcher, aivenClient)) } func NewWatcher(ctx context.Context, mgr *watcher.Manager) *watcher.Watcher[*Valkey] { @@ -33,6 +37,26 @@ func NewWatcher(ctx context.Context, mgr *watcher.Manager) *watcher.Watcher[*Val return w } +func NewNaisValkeyWatcher(ctx context.Context, mgr *watcher.Manager) *watcher.Watcher[*Valkey] { + w := watcher.Watch(mgr, &Valkey{}, watcher.WithConverter(func(o *unstructured.Unstructured, environmentName string) (obj any, ok bool) { + v, err := kubernetes.ToConcrete[naiscrd.Valkey](o) + if err != nil { + return nil, false + } + ret, err := toValkeyFromNais(v, environmentName) + if err != nil { + return nil, false + } + return ret, true + }), watcher.WithGVR(schema.GroupVersionResource{ + Group: "nais.io", + Version: "v1", + Resource: "valkeys", + })) + w.Start(ctx) + return w +} + func fromContext(ctx context.Context) *loaders { return ctx.Value(loadersKey).(*loaders) } @@ -41,18 +65,29 @@ type loaders struct { client *client tenantName string watcher *watcher.Watcher[*Valkey] + naisWatcher *watcher.Watcher[*Valkey] aivenClient aiven.AivenClient } -func newLoaders(tenantName string, watcher *watcher.Watcher[*Valkey], aivenClient aiven.AivenClient) *loaders { - client := &client{ - watcher: watcher, - } +func newLoaders(tenantName string, watcher, naisValkeyWatcher *watcher.Watcher[*Valkey], aivenClient aiven.AivenClient) *loaders { + client := &client{} return &loaders{ client: client, tenantName: tenantName, watcher: watcher, + naisWatcher: naisValkeyWatcher, aivenClient: aivenClient, } } + +func newK8sClient(ctx context.Context, environmentName string, teamSlug slug.Slug) (dynamic.ResourceInterface, error) { + sysClient, err := fromContext(ctx).naisWatcher.SystemAuthenticatedClient( + ctx, + environmentName, + ) + if err != nil { + return nil, err + } + return sysClient.Namespace(teamSlug.String()), nil +} diff --git a/internal/persistence/valkey/machines.go b/internal/persistence/valkey/machines.go index 62353cf30..081caf9b1 100644 --- a/internal/persistence/valkey/machines.go +++ b/internal/persistence/valkey/machines.go @@ -27,24 +27,10 @@ var machineTypes = []machineType{ {AivenPlan: "business-200", Tier: ValkeyTierHighAvailability, Memory: ValkeyMemoryGB200}, } -// tierAndMemory transposes machineTypes for lookup by ValkeyTier and ValkeyMemory -var tierAndMemory map[ValkeyTier]map[ValkeyMemory]machineType - // aivenPlans transposes machineTypes for lookup by an Aiven plan string var aivenPlans map[string]machineType func init() { - tierAndMemory = make(map[ValkeyTier]map[ValkeyMemory]machineType) - for _, m := range machineTypes { - if _, ok := tierAndMemory[m.Tier]; !ok { - tierAndMemory[m.Tier] = make(map[ValkeyMemory]machineType) - } - if _, ok := tierAndMemory[m.Tier][m.Memory]; ok { - panic("duplicate tier and memory combination [" + string(m.Tier) + ", " + string(m.Memory) + "] in machineTypes") - } - tierAndMemory[m.Tier][m.Memory] = m - } - aivenPlans = make(map[string]machineType) for _, m := range machineTypes { if _, ok := aivenPlans[m.AivenPlan]; ok { @@ -54,20 +40,6 @@ func init() { } } -func machineTypeFromTierAndMemory(tier ValkeyTier, memory ValkeyMemory) (*machineType, error) { - memories, ok := tierAndMemory[tier] - if !ok { - return nil, apierror.Errorf("Invalid Valkey tier: %s", tier) - } - - machine, ok := memories[memory] - if !ok { - return nil, apierror.Errorf("Invalid Valkey memory for tier. %v cannot have memory %v", tier, memory) - } - - return &machine, nil -} - func machineTypeFromPlan(plan string) (*machineType, error) { machine, ok := aivenPlans[plan] if !ok { diff --git a/internal/persistence/valkey/models.go b/internal/persistence/valkey/models.go index a6c8736d8..c869ccf64 100644 --- a/internal/persistence/valkey/models.go +++ b/internal/persistence/valkey/models.go @@ -15,6 +15,8 @@ import ( "github.com/nais/api/internal/validate" "github.com/nais/api/internal/workload" aiven_io_v1alpha1 "github.com/nais/liberator/pkg/apis/aiven.io/v1alpha1" + "github.com/nais/pgrator/pkg/api" + naiscrd "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" @@ -31,7 +33,7 @@ type ( type Valkey struct { Name string `json:"name"` - Status *ValkeyStatus `json:"status"` + Status *naiscrd.ValkeyStatus `json:"status"` TerminationProtection bool `json:"terminationProtection"` Tier ValkeyTier `json:"tier"` Memory ValkeyMemory `json:"memory"` @@ -40,7 +42,6 @@ type Valkey struct { TeamSlug slug.Slug `json:"-"` EnvironmentName string `json:"-"` WorkloadReference *workload.Reference `json:"-"` - AivenProject string `json:"-"` } func (Valkey) IsPersistence() {} @@ -156,11 +157,19 @@ func toValkey(u *unstructured.Unstructured, envName string) (*Valkey, error) { return nil, fmt.Errorf("converting to Valkey instance: %w", err) } + if len(obj.GetOwnerReferences()) > 0 { + for _, ownerRef := range obj.GetOwnerReferences() { + if ownerRef.Kind == "Valkey" { + return nil, fmt.Errorf("skipping Valkey %s in namespace %s because it has an owner reference", obj.GetName(), obj.GetNamespace()) + } + } + } + // Liberator doesn't contain this field, so we read it directly from the unstructured object terminationProtection, _, _ := unstructured.NestedBool(u.Object, specTerminationProtection...) maxMemoryPolicyStr, _, _ := unstructured.NestedString(u.Object, specMaxMemoryPolicy...) - maxMemoryPolicy, err := ValkeyMaxMemoryPolicyFromAivenString(maxMemoryPolicyStr) + maxMemoryPolicy, err := ValkeyMaxMemoryPolicyFromAivenString(naiscrd.ValkeyMaxMemoryPolicy(maxMemoryPolicyStr)) if err != nil { maxMemoryPolicy = "" } @@ -181,13 +190,13 @@ func toValkey(u *unstructured.Unstructured, envName string) (*Valkey, error) { Name: name, EnvironmentName: envName, TerminationProtection: terminationProtection, - Status: &ValkeyStatus{ - Conditions: obj.Status.Conditions, - State: obj.Status.State, + Status: &naiscrd.ValkeyStatus{ + BaseStatus: api.BaseStatus{ + Conditions: obj.Status.Conditions, + }, }, TeamSlug: slug.Slug(obj.GetNamespace()), WorkloadReference: workload.ReferenceFromOwnerReferences(obj.GetOwnerReferences()), - AivenProject: obj.Spec.Project, Tier: machine.Tier, Memory: machine.Memory, MaxMemoryPolicy: maxMemoryPolicy, @@ -195,6 +204,28 @@ func toValkey(u *unstructured.Unstructured, envName string) (*Valkey, error) { }, nil } +func toValkeyFromNais(v *naiscrd.Valkey, envName string) (*Valkey, error) { + var mmp ValkeyMaxMemoryPolicy + if v.Spec.MaxMemoryPolicy != "" { + var err error + mmp, err = ValkeyMaxMemoryPolicyFromAivenString(v.Spec.MaxMemoryPolicy) + if err != nil { + return nil, err + } + } + return &Valkey{ + Name: v.Name, + EnvironmentName: envName, + Status: v.Status, + TeamSlug: slug.Slug(v.Namespace), + WorkloadReference: workload.ReferenceFromOwnerReferences(v.OwnerReferences), + Tier: fromMapperatorTier(v.Spec.Tier), + Memory: fromMapperatorMemory(v.Spec.Memory), + NotifyKeyspaceEvents: v.Spec.NotifyKeyspaceEvents, + MaxMemoryPolicy: mmp, + }, nil +} + type TeamInventoryCountValkeys struct { Total int } @@ -323,9 +354,9 @@ func (e ValkeyMaxMemoryPolicy) MarshalGQL(w io.Writer) { fmt.Fprint(w, strconv.Quote(e.String())) } -func ValkeyMaxMemoryPolicyFromAivenString(s string) (ValkeyMaxMemoryPolicy, error) { +func ValkeyMaxMemoryPolicyFromAivenString(s naiscrd.ValkeyMaxMemoryPolicy) (ValkeyMaxMemoryPolicy, error) { for _, policy := range AllValkeyMaxMemoryPolicy { - if policy.ToAivenString() == s { + if policy.ToAivenString() == string(s) { return policy, nil } } @@ -335,21 +366,21 @@ func ValkeyMaxMemoryPolicyFromAivenString(s string) (ValkeyMaxMemoryPolicy, erro func (e ValkeyMaxMemoryPolicy) ToAivenString() string { switch e { case ValkeyMaxMemoryPolicyAllkeysLfu: - return "allkeys-lfu" + return string(naiscrd.ValkeyMaxMemoryPolicyAllkeysLFU) case ValkeyMaxMemoryPolicyAllkeysLru: - return "allkeys-lru" + return string(naiscrd.ValkeyMaxMemoryPolicyAllkeysLRU) case ValkeyMaxMemoryPolicyAllkeysRandom: - return "allkeys-random" + return string(naiscrd.ValkeyMaxMemoryPolicyAllkeysRandom) case ValkeyMaxMemoryPolicyNoEviction: - return "noeviction" + return string(naiscrd.ValkeyMaxMemoryPolicyNoEviction) case ValkeyMaxMemoryPolicyVolatileLfu: - return "volatile-lfu" + return string(naiscrd.ValkeyMaxMemoryPolicyVolatileLFU) case ValkeyMaxMemoryPolicyVolatileLru: - return "volatile-lru" + return string(naiscrd.ValkeyMaxMemoryPolicyVolatileLRU) case ValkeyMaxMemoryPolicyVolatileRandom: - return "volatile-random" + return string(naiscrd.ValkeyMaxMemoryPolicyVolatileRandom) case ValkeyMaxMemoryPolicyVolatileTTL: - return "volatile-ttl" + return string(naiscrd.ValkeyMaxMemoryPolicyVolatileTTL) default: return "" } diff --git a/internal/persistence/valkey/queries.go b/internal/persistence/valkey/queries.go index f602df56f..c6058044f 100644 --- a/internal/persistence/valkey/queries.go +++ b/internal/persistence/valkey/queries.go @@ -2,6 +2,7 @@ package valkey import ( "context" + "errors" "fmt" "strings" @@ -16,14 +17,14 @@ import ( "github.com/nais/api/internal/slug" "github.com/nais/api/internal/thirdparty/aiven" nais_io_v1 "github.com/nais/liberator/pkg/apis/nais.io/v1" + "github.com/nais/pgrator/pkg/api" + naiscrd "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/utils/ptr" ) var ( - specPlan = []string{"spec", "plan"} specTerminationProtection = []string{"spec", "terminationProtection"} specMaxMemoryPolicy = []string{"spec", "userConfig", "valkey_maxmemory_policy"} specNotifyKeyspaceEvents = []string{"spec", "userConfig", "valkey_notify_keyspace_events"} @@ -39,11 +40,15 @@ func GetByIdent(ctx context.Context, id ident.Ident) (*Valkey, error) { } func Get(ctx context.Context, teamSlug slug.Slug, environment, name string) (*Valkey, error) { - prefix := instanceNamer(teamSlug, "") - if !strings.HasPrefix(name, prefix) { - name = instanceNamer(teamSlug, name) + v, err := fromContext(ctx).naisWatcher.Get(environment, teamSlug.String(), name) + if errors.Is(err, &watcher.ErrorNotFound{}) { + prefix := instanceNamer(teamSlug, "") + if !strings.HasPrefix(name, prefix) { + name = instanceNamer(teamSlug, name) + } + v, err = fromContext(ctx).watcher.Get(environment, teamSlug.String(), name) } - return fromContext(ctx).client.watcher.Get(environment, teamSlug.String(), name) + return v, err } func ListForTeam(ctx context.Context, teamSlug slug.Slug, page *pagination.Pagination, orderBy *ValkeyOrder) (*ValkeyConnection, error) { @@ -55,7 +60,9 @@ func ListForTeam(ctx context.Context, teamSlug slug.Slug, page *pagination.Pagin } func ListAllForTeam(ctx context.Context, teamSlug slug.Slug) []*Valkey { - all := fromContext(ctx).client.watcher.GetByNamespace(teamSlug.String(), watcher.WithoutDeleted()) + all := fromContext(ctx).watcher.GetByNamespace(teamSlug.String(), watcher.WithoutDeleted()) + allNais := fromContext(ctx).naisWatcher.GetByNamespace(teamSlug.String(), watcher.WithoutDeleted()) + all = append(all, allNais...) return watcher.Objects(all) } @@ -89,13 +96,13 @@ func ListAccess(ctx context.Context, valkey *Valkey, page *pagination.Pagination } func ListForWorkload(ctx context.Context, teamSlug slug.Slug, environmentName string, references []nais_io_v1.Valkey, orderBy *ValkeyOrder) (*ValkeyConnection, error) { - all := fromContext(ctx).client.watcher.GetByNamespace(teamSlug.String(), watcher.InCluster(environmentName)) + all := ListAllForTeam(ctx, teamSlug) ret := make([]*Valkey, 0) for _, ref := range references { for _, d := range all { - if d.Obj.FullyQualifiedName() == instanceNamer(teamSlug, ref.Instance) { - ret = append(ret, d.Obj) + if d.FullyQualifiedName() == instanceNamer(teamSlug, ref.Instance) || d.FullyQualifiedName() == ref.Instance { + ret = append(ret, d) } } } @@ -120,71 +127,56 @@ func Create(ctx context.Context, input CreateValkeyInput) (*CreateValkeyPayload, return nil, err } - namespace := input.TeamSlug.String() - client, err := fromContext(ctx).watcher.ImpersonatedClientWithNamespace(ctx, input.EnvironmentName, namespace) - if err != nil { - return nil, err - } - - machine, err := machineTypeFromTierAndMemory(input.Tier, input.Memory) + client, err := newK8sClient(ctx, input.EnvironmentName, input.TeamSlug) if err != nil { return nil, err } - res := &unstructured.Unstructured{} - res.SetAPIVersion("aiven.io/v1alpha1") - res.SetKind("Valkey") - res.SetName(instanceNamer(input.TeamSlug, input.Name)) - res.SetNamespace(namespace) - res.SetAnnotations(kubernetes.WithCommonAnnotations(nil, authz.ActorFromContext(ctx).User.Identity())) - kubernetes.SetManagedByConsoleLabel(res) - - aivenProject, err := aiven.GetProject(ctx, input.EnvironmentName) - if err != nil { + // Ensure there's no existing Aiven Valkey with the same name + // This can be removed when we manage all valkeys through Console + _, err = fromContext(ctx).watcher.Get(input.EnvironmentName, input.TeamSlug.String(), instanceNamer(input.TeamSlug, input.Name)) + if err == nil { + return nil, apierror.Errorf("Valkey with the name %q already exists, but are not yet managed through Console.", input.Name) + } else if !errors.Is(err, &watcher.ErrorNotFound{}) { return nil, err } - res.Object["spec"] = map[string]any{ - "cloudName": "google-europe-north1", - "plan": machine.AivenPlan, - "project": aivenProject.ID, - "projectVpcId": aivenProject.VPC, - "terminationProtection": true, - "tags": map[string]any{ - "environment": input.EnvironmentName, - "team": namespace, - "tenant": fromContext(ctx).tenantName, + res := &naiscrd.Valkey{ + TypeMeta: metav1.TypeMeta{ + Kind: "Valkey", + APIVersion: "nais.io/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: input.Name, + Namespace: input.TeamSlug.String(), + }, + Spec: naiscrd.ValkeySpec{ + Tier: toMapperatorTier(input.Tier), + Memory: toMapperatorMemory(input.Memory), }, } + res.SetAnnotations(kubernetes.WithCommonAnnotations(nil, authz.ActorFromContext(ctx).User.Identity())) + kubernetes.SetManagedByConsoleLabel(res) if input.MaxMemoryPolicy != nil { - maxMemoryPolicy := input.MaxMemoryPolicy.ToAivenString() - err := unstructured.SetNestedField(res.Object, maxMemoryPolicy, specMaxMemoryPolicy...) - if err != nil { - return nil, err - } + res.Spec.MaxMemoryPolicy = naiscrd.ValkeyMaxMemoryPolicy(input.MaxMemoryPolicy.ToAivenString()) } - if input.NotifyKeyspaceEvents != nil { - err := unstructured.SetNestedField(res.Object, *input.NotifyKeyspaceEvents, specNotifyKeyspaceEvents...) - if err != nil { - return nil, err - } + res.Spec.NotifyKeyspaceEvents = *input.NotifyKeyspaceEvents } - ret, err := client.Create(ctx, res, metav1.CreateOptions{}) + obj, err := kubernetes.ToUnstructured(res) if err != nil { + return nil, err + } + + if _, err = client.Create(ctx, obj, metav1.CreateOptions{}); err != nil { if k8serrors.IsAlreadyExists(err) { return nil, apierror.ErrAlreadyExists } return nil, err } - err = aiven.UpsertPrometheusServiceIntegration(ctx, fromContext(ctx).watcher, ret, aivenProject, input.EnvironmentName) - if err != nil { - return nil, fmt.Errorf("creating Prometheus service integration: %w", err) - } - err = activitylog.Create(ctx, activitylog.CreateInput{ Action: activitylog.ActivityLogEntryActionCreated, Actor: authz.ActorFromContext(ctx).User, @@ -197,7 +189,7 @@ func Create(ctx context.Context, input CreateValkeyInput) (*CreateValkeyPayload, return nil, err } - valkey, err := toValkey(ret, input.EnvironmentName) + valkey, err := toValkeyFromNais(res, input.EnvironmentName) if err != nil { return nil, err } @@ -212,41 +204,43 @@ func Update(ctx context.Context, input UpdateValkeyInput) (*UpdateValkeyPayload, return nil, err } - client, err := fromContext(ctx).watcher.ImpersonatedClientWithNamespace(ctx, input.EnvironmentName, input.TeamSlug.String()) + client, err := newK8sClient(ctx, input.EnvironmentName, input.TeamSlug) if err != nil { return nil, err } - valkey, err := client.Get(ctx, instanceNamer(input.TeamSlug, input.Name), metav1.GetOptions{}) + valkey, err := client.Get(ctx, input.Name, metav1.GetOptions{}) if err != nil { return nil, err } - if !kubernetes.HasManagedByConsoleLabel(valkey) { - return nil, apierror.Errorf("Valkey %s/%s is not managed by Console", input.TeamSlug, input.Name) - } - - changes := make([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, 0) - res, err := updatePlan(valkey, input) + concreteValkey, err := kubernetes.ToConcrete[naiscrd.Valkey](valkey) if err != nil { return nil, err } - changes = append(changes, res...) - res, err = updateMaxMemoryPolicy(valkey, input) - if err != nil { - return nil, err + changes := make([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, 0) + updateFuncs := []func(*naiscrd.Valkey, UpdateValkeyInput) ([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, error){ + updateTier, + updateMemory, + updateMaxMemoryPolicy, + updateNotifyKeyspaceEvents, } - changes = append(changes, res...) - res, err = updateNotifyKeyspaceEvents(valkey, input) - if err != nil { - return nil, err + for _, f := range updateFuncs { + res, err := f(concreteValkey, input) + if err != nil { + return nil, err + } + changes = append(changes, res...) } - changes = append(changes, res...) if len(changes) == 0 { - vk, err := toValkey(valkey, input.EnvironmentName) + v, err := kubernetes.ToConcrete[naiscrd.Valkey](valkey) + if err != nil { + return nil, err + } + vk, err := toValkeyFromNais(v, input.EnvironmentName) if err != nil { return nil, err } @@ -256,21 +250,16 @@ func Update(ctx context.Context, input UpdateValkeyInput) (*UpdateValkeyPayload, }, nil } - valkey.SetAnnotations(kubernetes.WithCommonAnnotations(valkey.GetAnnotations(), authz.ActorFromContext(ctx).User.Identity())) - - ret, err := client.Update(ctx, valkey, metav1.UpdateOptions{}) + obj, err := kubernetes.ToUnstructured(concreteValkey) if err != nil { return nil, err } - aivenProject, err := aiven.GetProject(ctx, input.EnvironmentName) - if err != nil { - return nil, err - } + obj.SetAnnotations(kubernetes.WithCommonAnnotations(obj.GetAnnotations(), authz.ActorFromContext(ctx).User.Identity())) - err = aiven.UpsertPrometheusServiceIntegration(ctx, fromContext(ctx).watcher, ret, aivenProject, input.EnvironmentName) + ret, err := client.Update(ctx, obj, metav1.UpdateOptions{}) if err != nil { - return nil, fmt.Errorf("creating Prometheus service integration: %w", err) + return nil, err } err = activitylog.Create(ctx, activitylog.CreateInput{ @@ -288,7 +277,12 @@ func Update(ctx context.Context, input UpdateValkeyInput) (*UpdateValkeyPayload, return nil, err } - valkeyUpdated, err := toValkey(ret, input.EnvironmentName) + retValkey, err := kubernetes.ToConcrete[naiscrd.Valkey](ret) + if err != nil { + return nil, err + } + + valkeyUpdated, err := toValkeyFromNais(retValkey, input.EnvironmentName) if err != nil { return nil, err } @@ -298,126 +292,93 @@ func Update(ctx context.Context, input UpdateValkeyInput) (*UpdateValkeyPayload, }, nil } -func updatePlan(valkey *unstructured.Unstructured, input UpdateValkeyInput) ([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, error) { +func updateTier(valkey *naiscrd.Valkey, input UpdateValkeyInput) ([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, error) { changes := make([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, 0) - desired, err := machineTypeFromTierAndMemory(input.Tier, input.Memory) - if err != nil { - return nil, err - } - - oldPlan, found, err := unstructured.NestedString(valkey.Object, specPlan...) - if err != nil { - return nil, err - } - if !found { - // .spec.plan is a required field - return nil, fmt.Errorf("missing .spec.plan in Valkey resource") - } - - if oldPlan == desired.AivenPlan { - return changes, nil - } - - oldMachine, err := machineTypeFromPlan(oldPlan) - if err != nil { - return nil, err - } - - if input.Tier != oldMachine.Tier { + origTier := fromMapperatorTier(valkey.Spec.Tier) + if input.Tier != origTier { changes = append(changes, &ValkeyUpdatedActivityLogEntryDataUpdatedField{ Field: "tier", - OldValue: ptr.To(oldMachine.Tier.String()), + OldValue: ptr.To(origTier.String()), NewValue: ptr.To(input.Tier.String()), }) } - if input.Memory != oldMachine.Memory { + valkey.Spec.Tier = toMapperatorTier(input.Tier) + + return changes, nil +} + +func updateMemory(valkey *naiscrd.Valkey, input UpdateValkeyInput) ([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, error) { + changes := make([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, 0) + + origMemory := fromMapperatorMemory(valkey.Spec.Memory) + if input.Memory != origMemory { changes = append(changes, &ValkeyUpdatedActivityLogEntryDataUpdatedField{ Field: "memory", - OldValue: ptr.To(oldMachine.Memory.String()), + OldValue: ptr.To(origMemory.String()), NewValue: ptr.To(input.Memory.String()), }) } - if err := unstructured.SetNestedField(valkey.Object, desired.AivenPlan, specPlan...); err != nil { - return nil, err - } + valkey.Spec.Memory = toMapperatorMemory(input.Memory) return changes, nil } -func updateMaxMemoryPolicy(valkey *unstructured.Unstructured, input UpdateValkeyInput) ([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, error) { - changes := make([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, 0) - +func updateMaxMemoryPolicy(valkey *naiscrd.Valkey, input UpdateValkeyInput) ([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, error) { if input.MaxMemoryPolicy == nil { - return changes, nil + return nil, nil } - oldAivenPolicy, found, err := unstructured.NestedString(valkey.Object, specMaxMemoryPolicy...) - if err != nil { - return nil, err - } - - if found && oldAivenPolicy == input.MaxMemoryPolicy.ToAivenString() { - return changes, nil + if string(valkey.Spec.MaxMemoryPolicy) == input.MaxMemoryPolicy.ToAivenString() { + return nil, nil } - // continue if not found so that we explicitly set the policy on the resource - var oldValue *string - if found { - oldPolicy, err := ValkeyMaxMemoryPolicyFromAivenString(oldAivenPolicy) + var oldMMP *string + if valkey.Spec.MaxMemoryPolicy != "" { + old, err := ValkeyMaxMemoryPolicyFromAivenString(valkey.Spec.MaxMemoryPolicy) if err != nil { - return nil, err + return nil, fmt.Errorf("parsing existing max memory policy: %w", err) } - oldValue = ptr.To(oldPolicy.String()) + oldMMP = ptr.To(old.String()) } + changes := make([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, 0) + changes = append(changes, &ValkeyUpdatedActivityLogEntryDataUpdatedField{ Field: "maxMemoryPolicy", - OldValue: oldValue, + OldValue: oldMMP, NewValue: ptr.To(input.MaxMemoryPolicy.String()), }) - maxMemoryPolicy := input.MaxMemoryPolicy.ToAivenString() - if err := unstructured.SetNestedField(valkey.Object, maxMemoryPolicy, specMaxMemoryPolicy...); err != nil { - return nil, err - } + valkey.Spec.MaxMemoryPolicy = naiscrd.ValkeyMaxMemoryPolicy(input.MaxMemoryPolicy.ToAivenString()) return changes, nil } -func updateNotifyKeyspaceEvents(valkey *unstructured.Unstructured, input UpdateValkeyInput) ([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, error) { - changes := make([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, 0) - +func updateNotifyKeyspaceEvents(valkey *naiscrd.Valkey, input UpdateValkeyInput) ([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, error) { if input.NotifyKeyspaceEvents == nil { - return changes, nil + return nil, nil } - oldValue, found, err := unstructured.NestedString(valkey.Object, specNotifyKeyspaceEvents...) - if err != nil { - return nil, err + if string(valkey.Spec.NotifyKeyspaceEvents) == *input.NotifyKeyspaceEvents { + return nil, nil } - if found && oldValue == *input.NotifyKeyspaceEvents { - return changes, nil - } + changes := make([]*ValkeyUpdatedActivityLogEntryDataUpdatedField, 0) - var oldValPtr *string - if found { - oldValPtr = ptr.To(oldValue) + var oldValue *string + if valkey.Spec.NotifyKeyspaceEvents != "" { + oldValue = ptr.To(string(valkey.Spec.NotifyKeyspaceEvents)) } - changes = append(changes, &ValkeyUpdatedActivityLogEntryDataUpdatedField{ Field: "notifyKeyspaceEvents", - OldValue: oldValPtr, + OldValue: oldValue, NewValue: input.NotifyKeyspaceEvents, }) - if err := unstructured.SetNestedField(valkey.Object, *input.NotifyKeyspaceEvents, specNotifyKeyspaceEvents...); err != nil { - return nil, err - } - + valkey.Spec.NotifyKeyspaceEvents = *input.NotifyKeyspaceEvents return changes, nil } @@ -426,13 +387,12 @@ func Delete(ctx context.Context, input DeleteValkeyInput) (*DeleteValkeyPayload, return nil, err } - name := instanceNamer(input.TeamSlug, input.Name) - client, err := fromContext(ctx).watcher.ImpersonatedClientWithNamespace(ctx, input.EnvironmentName, input.TeamSlug.String()) + client, err := newK8sClient(ctx, input.EnvironmentName, input.TeamSlug) if err != nil { return nil, err } - valkey, err := client.Get(ctx, name, metav1.GetOptions{}) + valkey, err := client.Get(ctx, input.Name, metav1.GetOptions{}) if err != nil { return nil, err } @@ -441,22 +401,21 @@ func Delete(ctx context.Context, input DeleteValkeyInput) (*DeleteValkeyPayload, return nil, apierror.Errorf("Valkey %s/%s is not managed by Console", input.TeamSlug, input.Name) } - terminationProtection, found, err := unstructured.NestedBool(valkey.Object, specTerminationProtection...) - if err != nil { - return nil, err + annotations := valkey.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) } - if found && terminationProtection { - if err := unstructured.SetNestedField(valkey.Object, false, specTerminationProtection...); err != nil { - return nil, err - } + if annotations[api.AllowDeletionAnnotation] != "true" { + annotations[api.AllowDeletionAnnotation] = "true" + valkey.SetAnnotations(annotations) _, err = client.Update(ctx, valkey, metav1.UpdateOptions{}) if err != nil { - return nil, fmt.Errorf("removing deletion protection: %w", err) + return nil, fmt.Errorf("set allow deletion annotation: %w", err) } } - if err := fromContext(ctx).watcher.Delete(ctx, input.EnvironmentName, input.TeamSlug.String(), name); err != nil { + if err := client.Delete(ctx, input.Name, metav1.DeleteOptions{}); err != nil { return nil, err } @@ -477,7 +436,13 @@ func Delete(ctx context.Context, input DeleteValkeyInput) (*DeleteValkeyPayload, } func State(ctx context.Context, v *Valkey) (ValkeyState, error) { - s, err := fromContext(ctx).aivenClient.ServiceGet(ctx, v.AivenProject, v.FullyQualifiedName()) + project, err := aiven.GetProject(ctx, v.EnvironmentName) + if err != nil { + return ValkeyStateUnknown, err + } + aivenProject := project.ID + + s, err := fromContext(ctx).aivenClient.ServiceGet(ctx, aivenProject, v.FullyQualifiedName()) if err != nil { // The Valkey instance may not have been created in Aiven yet, or it has been deleted. // In both cases, we return "unknown" state rather than an error. @@ -500,3 +465,71 @@ func State(ctx context.Context, v *Valkey) (ValkeyState, error) { return ValkeyStateUnknown, nil } } + +func toMapperatorTier(tier ValkeyTier) naiscrd.ValkeyTier { + switch tier { + case ValkeyTierSingleNode: + return naiscrd.ValkeyTierSingleNode + case ValkeyTierHighAvailability: + return naiscrd.ValkeyTierHighAvailability + default: + return "" + } +} + +func fromMapperatorTier(tier naiscrd.ValkeyTier) ValkeyTier { + switch tier { + case naiscrd.ValkeyTierSingleNode: + return ValkeyTierSingleNode + case naiscrd.ValkeyTierHighAvailability: + return ValkeyTierHighAvailability + default: + return "" + } +} + +func toMapperatorMemory(memory ValkeyMemory) naiscrd.ValkeyMemory { + switch memory { + case ValkeyMemoryGB1: + return naiscrd.ValkeyMemory1GB + case ValkeyMemoryGB4: + return naiscrd.ValkeyMemory4GB + case ValkeyMemoryGB8: + return naiscrd.ValkeyMemory8GB + case ValkeyMemoryGB14: + return naiscrd.ValkeyMemory14GB + case ValkeyMemoryGB28: + return naiscrd.ValkeyMemory28GB + case ValkeyMemoryGB56: + return naiscrd.ValkeyMemory56GB + case ValkeyMemoryGB112: + return naiscrd.ValkeyMemory112GB + case ValkeyMemoryGB200: + return naiscrd.ValkeyMemory200GB + default: + return "" + } +} + +func fromMapperatorMemory(memory naiscrd.ValkeyMemory) ValkeyMemory { + switch memory { + case naiscrd.ValkeyMemory1GB: + return ValkeyMemoryGB1 + case naiscrd.ValkeyMemory4GB: + return ValkeyMemoryGB4 + case naiscrd.ValkeyMemory8GB: + return ValkeyMemoryGB8 + case naiscrd.ValkeyMemory14GB: + return ValkeyMemoryGB14 + case naiscrd.ValkeyMemory28GB: + return ValkeyMemoryGB28 + case naiscrd.ValkeyMemory56GB: + return ValkeyMemoryGB56 + case naiscrd.ValkeyMemory112GB: + return ValkeyMemoryGB112 + case naiscrd.ValkeyMemory200GB: + return ValkeyMemoryGB200 + default: + return "" + } +} diff --git a/internal/servicemaintenance/queries.go b/internal/servicemaintenance/queries.go index 1af0f5032..3467042b6 100644 --- a/internal/servicemaintenance/queries.go +++ b/internal/servicemaintenance/queries.go @@ -12,6 +12,7 @@ import ( "github.com/nais/api/internal/persistence/opensearch" "github.com/nais/api/internal/persistence/valkey" servicemaintenanceal "github.com/nais/api/internal/servicemaintenance/activitylog" + "github.com/nais/api/internal/thirdparty/aiven" ) func StartValkeyMaintenance(ctx context.Context, input StartValkeyMaintenanceInput) error { @@ -20,7 +21,12 @@ func StartValkeyMaintenance(ctx context.Context, input StartValkeyMaintenanceInp return err } - if err := fromContext(ctx).maintenanceMutator.aivenClient.ServiceMaintenanceStart(ctx, vk.AivenProject, vk.FullyQualifiedName()); err != nil { + project, err := aiven.GetProject(ctx, input.EnvironmentName) + if err != nil { + return err + } + + if err := fromContext(ctx).maintenanceMutator.aivenClient.ServiceMaintenanceStart(ctx, project.ID, vk.FullyQualifiedName()); err != nil { fromContext(ctx).log.WithError(err).Error("Failed to start Valkey maintenance") return err } @@ -41,7 +47,11 @@ func StartOpenSearchMaintenance(ctx context.Context, input StartOpenSearchMainte return err } - if err := fromContext(ctx).maintenanceMutator.aivenClient.ServiceMaintenanceStart(ctx, instance.AivenProject, instance.FullyQualifiedName()); err != nil { + project, err := aiven.GetProject(ctx, input.EnvironmentName) + if err != nil { + return err + } + if err := fromContext(ctx).maintenanceMutator.aivenClient.ServiceMaintenanceStart(ctx, project.ID, instance.FullyQualifiedName()); err != nil { fromContext(ctx).log.WithError(err).Error("Failed to start OpenSearch maintenance") return err }