diff --git a/Makefile b/Makefile index 96ded47b..b847f52f 100644 --- a/Makefile +++ b/Makefile @@ -134,11 +134,11 @@ mocks: $(MOCKGEN) @$(MOCKGEN) -destination ./pkg/mock/iaas/iaas.go -package iaas github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api DefaultAPI # client mocks - @$(MOCKGEN) -destination ./pkg/stackit/iaas_mock.go -package stackit ./pkg/stackit IaasClient - @$(MOCKGEN) -destination ./pkg/stackit/loadbalancer_mock.go -package stackit ./pkg/stackit LoadbalancerClient - @$(MOCKGEN) -destination ./pkg/stackit/server_mock.go -package stackit ./pkg/stackit NodeClient @$(MOCKGEN) -destination ./pkg/stackit/metadata/metadata_mock.go -package metadata ./pkg/stackit/metadata IMetadata @$(MOCKGEN) -destination ./pkg/csi/util/mount/mount_mock.go -package mount ./pkg/csi/util/mount IMount + @$(MOCKGEN) -destination ./pkg/stackit/client/mock/iaas_mock.go -typed -package client ./pkg/stackit/client IaaSClient + @$(MOCKGEN) -destination ./pkg/stackit/client/mock/loadbalancer_mock.go -typed -package client ./pkg/stackit/client LoadBalancingClient + @$(MOCKGEN) -destination ./pkg/stackit/client/mock/mock.go -package client ./pkg/stackit/client Factory .PHONY: generate generate: mocks diff --git a/cmd/stackit-csi-plugin/main.go b/cmd/stackit-csi-plugin/main.go index d952bd60..7510d185 100644 --- a/cmd/stackit-csi-plugin/main.go +++ b/cmd/stackit-csi-plugin/main.go @@ -6,15 +6,16 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" - "k8s.io/component-base/cli" - "k8s.io/klog/v2" - "github.com/stackitcloud/cloud-provider-stackit/pkg/csi" "github.com/stackitcloud/cloud-provider-stackit/pkg/csi/blockstorage" "github.com/stackitcloud/cloud-provider-stackit/pkg/csi/util/mount" - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit" + "github.com/stackitcloud/cloud-provider-stackit/pkg/metrics" + stackitclient "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client" "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/metadata" "github.com/stackitcloud/cloud-provider-stackit/pkg/version" + sdkconfig "github.com/stackitcloud/stackit-sdk-go/core/config" + "k8s.io/component-base/cli" + "k8s.io/klog/v2" ) var ( @@ -73,7 +74,7 @@ func main() { cmd.PersistentFlags().BoolVar(&provideNodeService, "provide-node-service", true, "If set to true then the CSI driver does provide the node service (default: true)") - stackit.AddExtraFlags(pflag.CommandLine) + stackitclient.AddExtraFlags(pflag.CommandLine) code := cli.Run(cmd) os.Exit(code) @@ -89,29 +90,32 @@ func handle() { if provideControllerService { var err error - cfg, err := stackit.GetConfigFromFile(cloudConfig) + cfg, err := stackitclient.GetConfigFromFile(cloudConfig) if err != nil { klog.Fatal(err) } - iaasClient, err := stackit.CreateIaaSClient(&cfg) - if err != nil { - klog.Fatalf("Failed to create IaaS client: %v", err) + iaasOpts := []sdkconfig.ConfigurationOption{ + sdkconfig.WithHTTPClient(metrics.NewInstrumentedHTTPClient()), // TODO: Ask if this is needed or not + } + + if cfg.Global.APIEndpoints.IaasAPI != "" { + iaasOpts = append(iaasOpts, sdkconfig.WithEndpoint(cfg.Global.APIEndpoints.IaasAPI)) } - stackitProvider, err := stackit.CreateSTACKITProvider(iaasClient, &cfg) + iaasClient, err := stackitclient.New(cfg.Global.Region, cfg.Global.ProjectID).IaaS(iaasOpts) if err != nil { - klog.Fatalf("Failed to create STACKIT provider: %v", err) + klog.Fatalf("Failed to create STACKIT stackitclient: %v", err) } - d.SetupControllerService(stackitProvider) + d.SetupControllerService(iaasClient) } if provideNodeService { // Initialize mount mountProvider := mount.GetMountProvider() - cfg, err := stackit.GetConfigFromFile(cloudConfig) + cfg, err := stackitclient.GetConfigFromFile(cloudConfig) if err != nil { klog.Fatal(err) } diff --git a/go.mod b/go.mod index 38fd68e3..81331f43 100644 --- a/go.mod +++ b/go.mod @@ -53,34 +53,36 @@ require ( github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.22.1 // indirect - github.com/go-openapi/jsonreference v0.21.2 // indirect - github.com/go-openapi/swag v0.25.1 // indirect - github.com/go-openapi/swag/cmdutils v0.25.1 // indirect - github.com/go-openapi/swag/conv v0.25.1 // indirect - github.com/go-openapi/swag/fileutils v0.25.1 // indirect - github.com/go-openapi/swag/jsonname v0.25.1 // indirect - github.com/go-openapi/swag/jsonutils v0.25.1 // indirect - github.com/go-openapi/swag/loading v0.25.1 // indirect - github.com/go-openapi/swag/mangling v0.25.1 // indirect - github.com/go-openapi/swag/netutils v0.25.1 // indirect - github.com/go-openapi/swag/stringutils v0.25.1 // indirect - github.com/go-openapi/swag/typeutils v0.25.1 // indirect - github.com/go-openapi/swag/yamlutils v0.25.1 // indirect + github.com/go-openapi/jsonpointer v0.22.4 // indirect + github.com/go-openapi/jsonreference v0.21.4 // indirect + github.com/go-openapi/swag v0.25.4 // indirect + github.com/go-openapi/swag/cmdutils v0.25.4 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect + github.com/go-openapi/swag/fileutils v0.25.4 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/mangling v0.25.4 // indirect + github.com/go-openapi/swag/netutils v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/goccy/go-yaml v1.19.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/cel-go v0.26.0 // indirect - github.com/google/gnostic-models v0.7.0 // indirect + github.com/google/cel-go v0.27.0 // indirect + github.com/google/gnostic-models v0.7.1 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect + github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect + github.com/klauspost/compress v1.18.4 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/term v0.5.2 // indirect @@ -90,9 +92,9 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect - github.com/prometheus/procfs v0.19.2 // indirect + github.com/prometheus/procfs v0.20.1 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.23.0 // indirect - github.com/stoewer/go-strcase v1.3.1 // indirect github.com/stretchr/objx v0.5.3 // indirect github.com/x448/float16 v0.8.4 // indirect go.etcd.io/etcd/api/v3 v3.6.8 // indirect @@ -102,8 +104,8 @@ require ( go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect go.opentelemetry.io/otel v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 // indirect go.opentelemetry.io/otel/metric v1.43.0 // indirect go.opentelemetry.io/otel/sdk v1.43.0 // indirect go.opentelemetry.io/otel/trace v1.43.0 // indirect @@ -113,13 +115,13 @@ require ( go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.50.0 // indirect - golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect + golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect golang.org/x/mod v0.35.0 // indirect golang.org/x/net v0.53.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect golang.org/x/term v0.42.0 // indirect golang.org/x/text v0.36.0 // indirect - golang.org/x/time v0.14.0 // indirect + golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.44.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect diff --git a/go.sum b/go.sum index ef1a613a..1f40c5f6 100644 --- a/go.sum +++ b/go.sum @@ -52,42 +52,46 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= -github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM= -github.com/go-openapi/jsonreference v0.21.2 h1:Wxjda4M/BBQllegefXrY/9aq1fxBA8sI5M/lFU6tSWU= -github.com/go-openapi/jsonreference v0.21.2/go.mod h1:pp3PEjIsJ9CZDGCNOyXIQxsNuroxm8FAJ/+quA0yKzQ= -github.com/go-openapi/swag v0.25.1 h1:6uwVsx+/OuvFVPqfQmOOPsqTcm5/GkBhNwLqIR916n8= -github.com/go-openapi/swag v0.25.1/go.mod h1:bzONdGlT0fkStgGPd3bhZf1MnuPkf2YAys6h+jZipOo= -github.com/go-openapi/swag/cmdutils v0.25.1 h1:nDke3nAFDArAa631aitksFGj2omusks88GF1VwdYqPY= -github.com/go-openapi/swag/cmdutils v0.25.1/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= -github.com/go-openapi/swag/conv v0.25.1 h1:+9o8YUg6QuqqBM5X6rYL/p1dpWeZRhoIt9x7CCP+he0= -github.com/go-openapi/swag/conv v0.25.1/go.mod h1:Z1mFEGPfyIKPu0806khI3zF+/EUXde+fdeksUl2NiDs= -github.com/go-openapi/swag/fileutils v0.25.1 h1:rSRXapjQequt7kqalKXdcpIegIShhTPXx7yw0kek2uU= -github.com/go-openapi/swag/fileutils v0.25.1/go.mod h1:+NXtt5xNZZqmpIpjqcujqojGFek9/w55b3ecmOdtg8M= -github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU= -github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo= -github.com/go-openapi/swag/jsonutils v0.25.1 h1:AihLHaD0brrkJoMqEZOBNzTLnk81Kg9cWr+SPtxtgl8= -github.com/go-openapi/swag/jsonutils v0.25.1/go.mod h1:JpEkAjxQXpiaHmRO04N1zE4qbUEg3b7Udll7AMGTNOo= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1 h1:DSQGcdB6G0N9c/KhtpYc71PzzGEIc/fZ1no35x4/XBY= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1/go.mod h1:kjmweouyPwRUEYMSrbAidoLMGeJ5p6zdHi9BgZiqmsg= -github.com/go-openapi/swag/loading v0.25.1 h1:6OruqzjWoJyanZOim58iG2vj934TysYVptyaoXS24kw= -github.com/go-openapi/swag/loading v0.25.1/go.mod h1:xoIe2EG32NOYYbqxvXgPzne989bWvSNoWoyQVWEZicc= -github.com/go-openapi/swag/mangling v0.25.1 h1:XzILnLzhZPZNtmxKaz/2xIGPQsBsvmCjrJOWGNz/ync= -github.com/go-openapi/swag/mangling v0.25.1/go.mod h1:CdiMQ6pnfAgyQGSOIYnZkXvqhnnwOn997uXZMAd/7mQ= -github.com/go-openapi/swag/netutils v0.25.1 h1:2wFLYahe40tDUHfKT1GRC4rfa5T1B4GWZ+msEFA4Fl4= -github.com/go-openapi/swag/netutils v0.25.1/go.mod h1:CAkkvqnUJX8NV96tNhEQvKz8SQo2KF0f7LleiJwIeRE= -github.com/go-openapi/swag/stringutils v0.25.1 h1:Xasqgjvk30eUe8VKdmyzKtjkVjeiXx1Iz0zDfMNpPbw= -github.com/go-openapi/swag/stringutils v0.25.1/go.mod h1:JLdSAq5169HaiDUbTvArA2yQxmgn4D6h4A+4HqVvAYg= -github.com/go-openapi/swag/typeutils v0.25.1 h1:rD/9HsEQieewNt6/k+JBwkxuAHktFtH3I3ysiFZqukA= -github.com/go-openapi/swag/typeutils v0.25.1/go.mod h1:9McMC/oCdS4BKwk2shEB7x17P6HmMmA6dQRtAkSnNb8= -github.com/go-openapi/swag/yamlutils v0.25.1 h1:mry5ez8joJwzvMbaTGLhw8pXUnhDK91oSJLDPF1bmGk= -github.com/go-openapi/swag/yamlutils v0.25.1/go.mod h1:cm9ywbzncy3y6uPm/97ysW8+wZ09qsks+9RS8fLWKqg= +github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= +github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= +github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= +github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= +github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= +github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= +github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= +github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= +github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= +github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= +github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= +github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= +github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= 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.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= -github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= @@ -98,10 +102,10 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.26.0 h1:DPGjXackMpJWH680oGY4lZhYjIameYmR+/6RBdDGmaI= -github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= -github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= -github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= +github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= +github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= +github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -115,20 +119,20 @@ github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0 h1:QGLs github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3 h1:B+8ClL/kCQkRiU82d9xajRPKYMrB7E0MbtzWVi1K4ns= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.3/go.mod h1:NbCUVmiS4foBGBHOYlCT25+YmGpJ32dZPi75pGEUpj4= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/Kgo2/7xNSUuC5G28VR8ljfrLKU2G4IjU= +github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12/go.mod h1:TBzl5BIHNXfS9+C35ZyJaklL7mLDbgUkcgXzSLa8Tk0= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= +github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -168,13 +172,13 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= -github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= -github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= +github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -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/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= @@ -192,17 +196,13 @@ github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.13.0 h1:UuLNwFHj github.com/stackitcloud/stackit-sdk-go/services/loadbalancer v1.13.0/go.mod h1:+Ld3dn648I+YKcBV3fEkYpDSr3fel421+LurJGywSBs= github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.23.0 h1:u2C3oHNcc41Ba5cUqSPuqviDrYSRhpaC5+ELbuHHdwM= github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.23.0/go.mod h1:NEz3f+GV5G++BE9/MmZCsXJyCih7jtg0pZuSyG2sLEs= -github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= @@ -244,10 +244,10 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 h1:zWWrB1U6nqhS/k6zYB74CjRpuiitRtLLi68VcgmOEto= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0/go.mod h1:2qXPNBX1OVRC0IwOnfo1ljoid+RD0QK3443EaqVlsOU= go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= @@ -275,8 +275,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= -golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0= -golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa h1:Zt3DZoOFFYkKhDT3v7Lm9FDMEV06GpzjG2jrqW+QTE0= +golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa/go.mod h1:K79w1Vqn7PoiZn+TkNpx3BUWUQksGO3JcVX6qIjytmA= golang.org/x/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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -313,8 +313,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= -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/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= diff --git a/pkg/ccm/instances.go b/pkg/ccm/instances.go index fedc143b..c4556a79 100644 --- a/pkg/ccm/instances.go +++ b/pkg/ccm/instances.go @@ -24,9 +24,8 @@ import ( "strings" "github.com/stackitcloud/cloud-provider-stackit/pkg/labels" - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit" + stackitclient "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client" iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" - corev1 "k8s.io/api/core/v1" cloudprovider "k8s.io/cloud-provider" "k8s.io/klog/v2" @@ -47,12 +46,12 @@ var oldProviderIDRegexp = regexp.MustCompile(`^` + oldProviderName + `://([^/]*) // Instances encapsulates an implementation of Instances for OpenStack. type Instances struct { regionProviderID bool - iaasClient stackit.NodeClient + iaasClient stackitclient.IaaSClient projectID string region string } -func NewInstance(client stackit.NodeClient, projectID, region string) (*Instances, error) { +func NewInstance(client stackitclient.IaaSClient, projectID, region string) (*Instances, error) { return &Instances{ iaasClient: client, projectID: projectID, @@ -203,8 +202,8 @@ func instanceIDFromProviderID(providerID string) (instanceID, region string, err } } -func getServerByName(ctx context.Context, client stackit.NodeClient, name, projectID, region string) (*iaas.Server, error) { - servers, err := client.ListServers(ctx, projectID, region) +func getServerByName(ctx context.Context, client stackitclient.IaaSClient, name string) (*iaas.Server, error) { + servers, err := client.ListServers(ctx) if err != nil { return nil, fmt.Errorf("failed to list servers: %w", err) } @@ -227,7 +226,7 @@ func getServerByName(ctx context.Context, client stackit.NodeClient, name, proje func (i *Instances) getInstance(ctx context.Context, node *corev1.Node) (*iaas.Server, error) { if node.Spec.ProviderID == "" { - return getServerByName(ctx, i.iaasClient, node.Name, i.projectID, i.region) + return getServerByName(ctx, i.iaasClient, node.Name) } instanceID, instanceRegion, err := instanceIDFromProviderID(node.Spec.ProviderID) @@ -239,8 +238,8 @@ func (i *Instances) getInstance(ctx context.Context, node *corev1.Node) (*iaas.S return nil, fmt.Errorf("ProviderID \"%s\" didn't match supported region \"%s\"", node.Spec.ProviderID, i.region) } - server, err := i.iaasClient.GetServer(ctx, i.projectID, i.region, instanceID) - if stackit.IsNotFound(err) { + server, err := i.iaasClient.GetServer(ctx, instanceID) + if stackitclient.IsNotFound(err) { return nil, cloudprovider.InstanceNotFound } if err != nil { diff --git a/pkg/ccm/instances_test.go b/pkg/ccm/instances_test.go index 4f022ac0..ad384c1b 100644 --- a/pkg/ccm/instances_test.go +++ b/pkg/ccm/instances_test.go @@ -23,9 +23,9 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit" + stackitclient "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client" + stackitclientmock "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client/mock" iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" - "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -33,7 +33,7 @@ import ( var _ = Describe("Node Controller", func() { var ( - nodeMockClient *stackit.MockNodeClient + nodeMockClient *stackitclientmock.MockIaaSClient instance *Instances projectID string @@ -47,7 +47,7 @@ var _ = Describe("Node Controller", func() { serverID = "my-server" ctrl := gomock.NewController(GinkgoT()) - nodeMockClient = stackit.NewMockNodeClient(ctrl) + nodeMockClient = stackitclientmock.NewMockIaaSClient(ctrl) var err error instance, err = NewInstance(nodeMockClient, projectID, "eu01") @@ -68,7 +68,7 @@ var _ = Describe("Node Controller", func() { Describe("InstanceExists", func() { It("does not error if instance not found", func() { - nodeMockClient.EXPECT().ListServers(gomock.Any(), projectID, region).Return(&[]iaas.Server{}, nil) + nodeMockClient.EXPECT().ListServers(gomock.Any()).Return(&[]iaas.Server{}, nil) node := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{Name: "foo"}, @@ -80,7 +80,7 @@ var _ = Describe("Node Controller", func() { }) It("successfully get the instance when provider ID not there", func() { - nodeMockClient.EXPECT().ListServers(gomock.Any(), projectID, region).Return(&[]iaas.Server{ + nodeMockClient.EXPECT().ListServers(gomock.Any()).Return(&[]iaas.Server{ { Name: "foo", }, @@ -96,7 +96,7 @@ var _ = Describe("Node Controller", func() { }) It("successfully get the instance when provider ID is there", func() { - nodeMockClient.EXPECT().GetServer(gomock.Any(), projectID, region, serverID).Return(&iaas.Server{ + nodeMockClient.EXPECT().GetServer(gomock.Any(), serverID).Return(&iaas.Server{ Name: "foo", }, nil) @@ -113,7 +113,7 @@ var _ = Describe("Node Controller", func() { }) It("successfully get the instance when old provider ID is there", func() { - nodeMockClient.EXPECT().GetServer(gomock.Any(), projectID, region, serverID).Return(&iaas.Server{ + nodeMockClient.EXPECT().GetServer(gomock.Any(), serverID).Return(&iaas.Server{ Name: "foo", }, nil) @@ -130,7 +130,7 @@ var _ = Describe("Node Controller", func() { }) It("successfully get the instance when old regional provider ID is there", func() { - nodeMockClient.EXPECT().GetServer(gomock.Any(), projectID, region, serverID).Return(&iaas.Server{ + nodeMockClient.EXPECT().GetServer(gomock.Any(), serverID).Return(&iaas.Server{ Name: "foo", }, nil) @@ -147,7 +147,7 @@ var _ = Describe("Node Controller", func() { }) It("error when list server fails", func() { - nodeMockClient.EXPECT().ListServers(gomock.Any(), projectID, region).Return(nil, fmt.Errorf("failed due to some reason")) + nodeMockClient.EXPECT().ListServers(gomock.Any()).Return(nil, fmt.Errorf("failed due to some reason")) node := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{Name: "foo"}, @@ -158,7 +158,7 @@ var _ = Describe("Node Controller", func() { }) It("does not error when get server instance not found", func() { - nodeMockClient.EXPECT().GetServer(gomock.Any(), projectID, region, serverID).Return(nil, stackit.ErrorNotFound) + nodeMockClient.EXPECT().GetServer(gomock.Any(), serverID).Return(nil, stackitclient.ErrorNotFound) node := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{Name: "foo"}, @@ -175,7 +175,7 @@ var _ = Describe("Node Controller", func() { Describe("InstanceShutdown", func() { It("successfully gets the instance status with provider ID", func() { - nodeMockClient.EXPECT().ListServers(gomock.Any(), projectID, region).Return(&[]iaas.Server{ + nodeMockClient.EXPECT().ListServers(gomock.Any()).Return(&[]iaas.Server{ { Name: "foo", Status: new(instanceStopping), @@ -192,7 +192,7 @@ var _ = Describe("Node Controller", func() { }) It("successfully gets the instance status without provider ID", func() { - nodeMockClient.EXPECT().GetServer(gomock.Any(), projectID, region, serverID).Return(&iaas.Server{ + nodeMockClient.EXPECT().GetServer(gomock.Any(), serverID).Return(&iaas.Server{ Name: "foo", Status: new("ACTIVE"), }, nil) @@ -210,7 +210,7 @@ var _ = Describe("Node Controller", func() { }) It("fails if server not found", func() { - nodeMockClient.EXPECT().ListServers(gomock.Any(), projectID, region).Return(nil, stackit.ErrorNotFound) + nodeMockClient.EXPECT().ListServers(gomock.Any()).Return(nil, stackitclient.ErrorNotFound) node := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{Name: "foo"}, @@ -224,7 +224,7 @@ var _ = Describe("Node Controller", func() { Describe("InstanceMetadata", func() { It("does not error if instance not found", func() { - nodeMockClient.EXPECT().ListServers(gomock.Any(), projectID, region).Return(&[]iaas.Server{}, nil) + nodeMockClient.EXPECT().ListServers(gomock.Any()).Return(&[]iaas.Server{}, nil) node := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{Name: "foo"}, @@ -236,7 +236,7 @@ var _ = Describe("Node Controller", func() { }) It("successfully get all the metadata values", func() { - nodeMockClient.EXPECT().ListServers(gomock.Any(), projectID, region).Return(&[]iaas.Server{ + nodeMockClient.EXPECT().ListServers(gomock.Any()).Return(&[]iaas.Server{ { Name: "foo", Id: new(serverID), @@ -271,7 +271,7 @@ var _ = Describe("Node Controller", func() { }) It("errors when list server fails", func() { - nodeMockClient.EXPECT().ListServers(gomock.Any(), projectID, region).Return(nil, fmt.Errorf("failed due to some reason")) + nodeMockClient.EXPECT().ListServers(gomock.Any()).Return(nil, fmt.Errorf("failed due to some reason")) node := &corev1.Node{ ObjectMeta: metav1.ObjectMeta{Name: "foo"}, diff --git a/pkg/ccm/loadbalancer.go b/pkg/ccm/loadbalancer.go index 46f485e1..d6c0e690 100644 --- a/pkg/ccm/loadbalancer.go +++ b/pkg/ccm/loadbalancer.go @@ -7,6 +7,8 @@ import ( "strings" "time" + "github.com/stackitcloud/cloud-provider-stackit/pkg/cmp" + stackitclient "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client" stackitconfig "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/config" loadbalancer "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api" lbwait "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api/wait" @@ -14,9 +16,6 @@ import ( "k8s.io/client-go/tools/record" cloudprovider "k8s.io/cloud-provider" "k8s.io/cloud-provider/api" - - "github.com/stackitcloud/cloud-provider-stackit/pkg/cmp" - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit" ) const ( @@ -40,7 +39,7 @@ type MetricsRemoteWrite struct { // LoadBalancer is used for creating and maintaining load balancers. type LoadBalancer struct { - client stackit.LoadbalancerClient + client stackitclient.LoadBalancingClient recorder record.EventRecorder // set in CloudControllerManager.Initialize projectID string opts stackitconfig.LoadBalancerOpts @@ -50,7 +49,7 @@ type LoadBalancer struct { var _ cloudprovider.LoadBalancer = (*LoadBalancer)(nil) -func NewLoadBalancer(client stackit.LoadbalancerClient, projectID string, opts stackitconfig.LoadBalancerOpts, metricsRemoteWrite *MetricsRemoteWrite) (*LoadBalancer, error) { //nolint:lll // looks weird when shortened +func NewLoadBalancer(client stackitclient.LoadBalancingClient, projectID string, opts stackitconfig.LoadBalancerOpts, metricsRemoteWrite *MetricsRemoteWrite) (*LoadBalancer, error) { //nolint:lll // looks weird when shortened // LoadBalancer.recorder is set in CloudControllerManager.Initialize return &LoadBalancer{ client: client, @@ -67,9 +66,9 @@ func NewLoadBalancer(client stackit.LoadbalancerClient, projectID string, opts s func (l *LoadBalancer) GetLoadBalancer(ctx context.Context, clusterName string, service *corev1.Service) ( status *corev1.LoadBalancerStatus, exists bool, err error, ) { - lb, err := l.client.GetLoadBalancer(ctx, l.projectID, l.GetLoadBalancerName(ctx, clusterName, service)) + lb, err := l.client.GetLoadBalancer(ctx, l.GetLoadBalancerName(ctx, clusterName, service)) switch { - case stackit.IsNotFound(err): + case stackitclient.IsNotFound(err): // Also for non-STACKIT load balancers in "update" & "updateAndCreate" mode return with no error if not found. return nil, false, nil case err != nil: @@ -112,11 +111,11 @@ func (l *LoadBalancer) EnsureLoadBalancer( //nolint:gocyclo // not really comple nodes []*corev1.Node, ) (*corev1.LoadBalancerStatus, error) { name := l.GetLoadBalancerName(ctx, clusterName, service) - lb, err := l.client.GetLoadBalancer(ctx, l.projectID, name) - if err != nil && !stackit.IsNotFound(err) { + lb, err := l.client.GetLoadBalancer(ctx, name) + if err != nil && !stackitclient.IsNotFound(err) { return nil, err } - if stackit.IsNotFound(err) { + if stackitclient.IsNotFound(err) { return l.createLoadBalancer(ctx, clusterName, service, nodes) } @@ -163,8 +162,7 @@ func (l *LoadBalancer) EnsureLoadBalancer( //nolint:gocyclo // not really comple TargetPools: spec.TargetPools, Version: spec.Version, } - lb, err = l.client.UpdateLoadBalancer(ctx, l.projectID, name, updatePayload) - if err != nil { + if err := l.client.UpdateLoadBalancer(ctx, name, updatePayload); err != nil { return nil, fmt.Errorf("failed to update load balancer: %w", err) } // Clean up observability credentials if Argus extension is enabled. @@ -172,7 +170,7 @@ func (l *LoadBalancer) EnsureLoadBalancer( //nolint:gocyclo // not really comple // At the latest, they will be removed when the service is deleted or Argus is enabled again. // This is preferred over listing all credentials in the project on each reconciliation. if l.metricsRemoteWrite == nil && credentialsRefBeforeUpdate != nil { - err = l.client.DeleteCredentials(ctx, l.projectID, *credentialsRefBeforeUpdate) + err = l.client.DeleteCredentials(ctx, *credentialsRefBeforeUpdate) if err != nil { return nil, fmt.Errorf("delete metricsRemoteWrite credentials %q: %w", *credentialsRefBeforeUpdate, err) } @@ -217,7 +215,7 @@ func (l *LoadBalancer) createLoadBalancer(ctx context.Context, clusterName strin } spec.Name = &name - lb, createErr := l.client.CreateLoadBalancer(ctx, l.projectID, spec) + lb, createErr := l.client.CreateLoadBalancer(ctx, spec) if createErr != nil { return nil, createErr } @@ -247,7 +245,7 @@ func (l *LoadBalancer) UpdateLoadBalancer(ctx context.Context, clusterName strin } for _, pool := range spec.TargetPools { - err := l.client.UpdateTargetPool(ctx, l.projectID, l.GetLoadBalancerName(ctx, clusterName, service), *pool.Name, loadbalancer.UpdateTargetPoolPayload(pool)) + err := l.client.UpdateTargetPool(ctx, l.GetLoadBalancerName(ctx, clusterName, service), *pool.Name, loadbalancer.UpdateTargetPoolPayload(pool)) if err != nil { return fmt.Errorf("failed to update target pool %q: %w", *pool.Name, err) } @@ -269,9 +267,9 @@ func (l *LoadBalancer) EnsureLoadBalancerDeleted( ) error { name := l.GetLoadBalancerName(ctx, clusterName, service) - lb, err := l.client.GetLoadBalancer(ctx, l.projectID, name) + lb, err := l.client.GetLoadBalancer(ctx, name) switch { - case stackit.IsNotFound(err): + case stackitclient.IsNotFound(err): return nil case err != nil: return err @@ -309,11 +307,11 @@ func (l *LoadBalancer) EnsureLoadBalancerDeleted( PlanId: lb.PlanId, Labels: lb.Labels, } - _, err = l.client.UpdateLoadBalancer(ctx, l.projectID, name, payload) + err = l.client.UpdateLoadBalancer(ctx, name, payload) if err != nil { return fmt.Errorf("failed to update load balancer: %w", err) } - if err = l.client.DeleteCredentials(ctx, l.projectID, *credentialsRef); err != nil { + if err = l.client.DeleteCredentials(ctx, *credentialsRef); err != nil { return fmt.Errorf("delete metricsRemoteWrite credentials %q: %w", *credentialsRef, err) } } @@ -329,7 +327,7 @@ func (l *LoadBalancer) EnsureLoadBalancerDeleted( return fmt.Errorf("failed to clean up orphaned observability credentials: %w", err) } - err = l.client.DeleteLoadBalancer(ctx, l.projectID, name) + err = l.client.DeleteLoadBalancer(ctx, name) // Deleting a load balancer doesn't return an error if the load balancer cannot be found. if err != nil { return err @@ -367,7 +365,7 @@ func (l *LoadBalancer) reconcileObservabilityCredentials( Username: &l.metricsRemoteWrite.username, Password: &l.metricsRemoteWrite.password, } - c, err := l.client.CreateCredentials(ctx, l.projectID, payload) + c, err := l.client.CreateCredentials(ctx, payload) if err != nil { return nil, fmt.Errorf("create credentials: %w", err) } @@ -385,7 +383,7 @@ func (l *LoadBalancer) reconcileObservabilityCredentials( Username: &l.metricsRemoteWrite.username, Password: &l.metricsRemoteWrite.password, } - if err := l.client.UpdateCredentials(ctx, l.projectID, *credentialsRef, payload); err != nil { + if err := l.client.UpdateCredentials(ctx, *credentialsRef, payload); err != nil { return nil, fmt.Errorf("update credentials %q: %w", *credentialsRef, err) } return &loadbalancer.LoadbalancerOptionObservability{ @@ -400,13 +398,13 @@ func (l *LoadBalancer) reconcileObservabilityCredentials( // This call is expensive. // Make sure that no credentials are referenced, otherwise the deletion fails. func (l *LoadBalancer) cleanUpCredentials(ctx context.Context, name string) error { - res, err := l.client.ListCredentials(ctx, l.projectID) + res, err := l.client.ListCredentials(ctx) if err != nil { return fmt.Errorf("failed to list credentials: %w", err) } for _, credentials := range res.Credentials { if credentials.DisplayName != nil && *credentials.DisplayName == name { - err = l.client.DeleteCredentials(ctx, l.projectID, *credentials.CredentialsRef) + err = l.client.DeleteCredentials(ctx, *credentials.CredentialsRef) if err != nil { return fmt.Errorf("failed to delete credentials %q: %w", *credentials.CredentialsRef, err) } diff --git a/pkg/ccm/loadbalancer_test.go b/pkg/ccm/loadbalancer_test.go index e46c9082..1ab1c42c 100644 --- a/pkg/ccm/loadbalancer_test.go +++ b/pkg/ccm/loadbalancer_test.go @@ -7,6 +7,8 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + stackitclient "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client" + stackitclientmock "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client/mock" stackitconfig "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/config" loadbalancer "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api" lbwait "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api/wait" @@ -14,8 +16,6 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/cloud-provider/api" - - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit" ) var notYetReadyError = api.NewRetryError("waiting for load balancer to become ready. This error is normal while the load balancer starts.", 10*time.Second).Error() @@ -27,7 +27,7 @@ const ( var _ = Describe("LoadBalancer", func() { var ( - mockClient *stackit.MockLoadbalancerClient + mockClient *stackitclientmock.MockLoadBalancingClient lbInModeIgnoreAndObs *LoadBalancer loadBalancer *LoadBalancer clusterName string @@ -41,7 +41,7 @@ var _ = Describe("LoadBalancer", func() { lbOpts = stackitconfig.LoadBalancerOpts{NetworkID: "my-network"} ctrl := gomock.NewController(GinkgoT()) - mockClient = stackit.NewMockLoadbalancerClient(ctrl) + mockClient = stackitclientmock.NewMockLoadBalancingClient(ctrl) var err error lbInModeIgnoreAndObs, err = NewLoadBalancer(mockClient, projectID, lbOpts, &MetricsRemoteWrite{ endpoint: "test-endpoint", @@ -100,7 +100,7 @@ var _ = Describe("LoadBalancer", func() { Describe("GetLoadBalancer", func() { It("should report LB does not exist", func() { - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(nil, stackit.ErrorNotFound) + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(nil, stackitclient.ErrorNotFound) svc := minimalLoadBalancerService() @@ -133,7 +133,7 @@ var _ = Describe("LoadBalancer", func() { // LB has no external address yet myLb.ExternalAddress = nil } - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(myLb, nil) + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(myLb, nil) status, found, err := loadBalancer.GetLoadBalancer(context.Background(), clusterName, svc) Expect(err).NotTo(HaveOccurred()) @@ -170,7 +170,7 @@ var _ = Describe("LoadBalancer", func() { if hasPrivateAddress { myLb.PrivateAddress = new("10.20.30.40") } - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(myLb, nil) + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(myLb, nil) status, found, err := loadBalancer.GetLoadBalancer(context.Background(), clusterName, svc) Expect(err).NotTo(HaveOccurred()) @@ -190,8 +190,8 @@ var _ = Describe("LoadBalancer", func() { Describe("EnsureLoadBalancer", func() { It("ensure load balancer should trigger load balancer creation if LB doesn't exist", func() { - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(nil, stackit.ErrorNotFound) - mockClient.EXPECT().CreateLoadBalancer(gomock.Any(), projectID, gomock.Any()).MinTimes(1).Return(&loadbalancer.LoadBalancer{}, nil) + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(nil, stackitclient.ErrorNotFound) + mockClient.EXPECT().CreateLoadBalancer(gomock.Any(), gomock.Any()).MinTimes(1).Return(&loadbalancer.LoadBalancer{}, nil) _, err := loadBalancer.EnsureLoadBalancer(context.Background(), clusterName, minimalLoadBalancerService(), []*corev1.Node{}) Expect(err).To(MatchError(notYetReadyError)) @@ -199,13 +199,13 @@ var _ = Describe("LoadBalancer", func() { }) It("should create a load balancer with observability configured", func() { - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(nil, stackit.ErrorNotFound) - mockClient.EXPECT().ListCredentials(gomock.Any(), projectID).Return(&loadbalancer.ListCredentialsResponse{ + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(nil, stackitclient.ErrorNotFound) + mockClient.EXPECT().ListCredentials(gomock.Any()).Return(&loadbalancer.ListCredentialsResponse{ Credentials: []loadbalancer.CredentialsResponse{}, }, nil) // TODO: match payload - mockClient.EXPECT().CreateCredentials(gomock.Any(), projectID, gomock.Any()).MinTimes(1). - DoAndReturn(func(_ context.Context, _ string, payload loadbalancer.CreateCredentialsPayload) (*loadbalancer.CreateCredentialsResponse, error) { + mockClient.EXPECT().CreateCredentials(gomock.Any(), gomock.Any()).MinTimes(1). + DoAndReturn(func(_ context.Context, payload loadbalancer.CreateCredentialsPayload) (*loadbalancer.CreateCredentialsResponse, error) { return &loadbalancer.CreateCredentialsResponse{ Credential: &loadbalancer.CredentialsResponse{ CredentialsRef: new("my-credential-ref"), @@ -214,7 +214,7 @@ var _ = Describe("LoadBalancer", func() { }, }, nil }) - mockClient.EXPECT().CreateLoadBalancer(gomock.Any(), projectID, gomock.Any()).MinTimes(1).Return(&loadbalancer.LoadBalancer{}, nil) + mockClient.EXPECT().CreateLoadBalancer(gomock.Any(), gomock.Any()).MinTimes(1).Return(&loadbalancer.LoadBalancer{}, nil) _, err := lbInModeIgnoreAndObs.EnsureLoadBalancer(context.Background(), clusterName, minimalLoadBalancerService(), []*corev1.Node{}) Expect(err).To(MatchError(notYetReadyError)) @@ -245,8 +245,8 @@ var _ = Describe("LoadBalancer", func() { PlanId: new(p10), } - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(myLb, nil) - mockClient.EXPECT().UpdateCredentials(gomock.Any(), projectID, sampleCredentialsRef, gomock.Any()).MinTimes(1).Return(nil) + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(myLb, nil) + mockClient.EXPECT().UpdateCredentials(gomock.Any(), sampleCredentialsRef, gomock.Any()).MinTimes(1).Return(nil) _, err = lbInModeIgnoreAndObs.EnsureLoadBalancer(context.Background(), clusterName, svc, []*corev1.Node{}) Expect(err).NotTo(HaveOccurred()) @@ -270,14 +270,13 @@ var _ = Describe("LoadBalancer", func() { Version: new("current-version"), } - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(myLb, nil) + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(myLb, nil) // For simplicity, we return the original load balancer. In reality, the updated load balancer should be returned. mockClient.EXPECT().UpdateLoadBalancer( gomock.Any(), - projectID, loadBalancer.GetLoadBalancerName(context.Background(), clusterName, svc), versionMatcher("current-version"), - ).MinTimes(1).Return(myLb, nil) + ).MinTimes(1).Return(nil) svc = svc.DeepCopy() svc.Spec.Ports = append(svc.Spec.Ports, corev1.ServicePort{ @@ -328,14 +327,13 @@ var _ = Describe("LoadBalancer", func() { Version: new("current-version"), } - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(myLb, nil) + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(myLb, nil) // For simplicity, we return the original load balancer. In reality, the updated load balancer should be returned. mockClient.EXPECT().UpdateLoadBalancer( gomock.Any(), - projectID, loadBalancer.GetLoadBalancerName(context.Background(), clusterName, svc), versionMatcher("current-version"), - ).MinTimes(1).Return(myLb, nil) + ).MinTimes(1).Return(nil) _, err = loadBalancer.EnsureLoadBalancer(context.Background(), clusterName, svc, []*corev1.Node{nodeA, nodeB}) Expect(err).NotTo(HaveOccurred()) @@ -364,21 +362,20 @@ var _ = Describe("LoadBalancer", func() { Version: new("current-version"), } - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(myLb, nil) + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(myLb, nil) // Check order to ensure that the reference is removed before the credentials are removed. // The API rejects deletions of used credentials. gomock.InOrder( // For simplicity, we return the original load balancer. In reality, the updated load balancer should be returned. mockClient.EXPECT().UpdateLoadBalancer( gomock.Any(), - projectID, loadBalancer.GetLoadBalancerName(context.Background(), clusterName, svc), gomock.All( versionMatcher("current-version"), hasNoObservabilityConfigured(), ), - ).MinTimes(1).Return(myLb, nil), - mockClient.EXPECT().DeleteCredentials(gomock.Any(), projectID, gomock.Any()).MinTimes(1).Return(nil), + ).MinTimes(1).Return(nil), + mockClient.EXPECT().DeleteCredentials(gomock.Any(), gomock.Any()).MinTimes(1).Return(nil), ) _, err = loadBalancer.EnsureLoadBalancer(context.Background(), clusterName, svc, []*corev1.Node{}) @@ -390,11 +387,11 @@ var _ = Describe("LoadBalancer", func() { Describe("EnsureLoadBalancerDeleted", func() { It("should trigger load balancer deletion", func() { - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(&loadbalancer.LoadBalancer{}, nil) - mockClient.EXPECT().ListCredentials(gomock.Any(), projectID).Return(&loadbalancer.ListCredentialsResponse{ + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(&loadbalancer.LoadBalancer{}, nil) + mockClient.EXPECT().ListCredentials(gomock.Any()).Return(&loadbalancer.ListCredentialsResponse{ Credentials: []loadbalancer.CredentialsResponse{}, }, nil) - mockClient.EXPECT().DeleteLoadBalancer(gomock.Any(), projectID, gomock.Any()).MinTimes(1).Return(nil) + mockClient.EXPECT().DeleteLoadBalancer(gomock.Any(), gomock.Any()).MinTimes(1).Return(nil) err := loadBalancer.EnsureLoadBalancerDeleted(context.Background(), clusterName, minimalLoadBalancerService()) Expect(err).NotTo(HaveOccurred()) @@ -402,7 +399,7 @@ var _ = Describe("LoadBalancer", func() { }) It("should finalize deletion if LB API returns not found", func() { - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(nil, stackit.ErrorNotFound) + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(nil, stackitclient.ErrorNotFound) err := loadBalancer.EnsureLoadBalancerDeleted(context.Background(), clusterName, minimalLoadBalancerService()) Expect(err).NotTo(HaveOccurred()) @@ -410,7 +407,7 @@ var _ = Describe("LoadBalancer", func() { }) It("should finalize deletion if load balancer is state terminating", func() { - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(&loadbalancer.LoadBalancer{ + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(&loadbalancer.LoadBalancer{ Status: new(lbwait.LOADBALANCERSTATUS_TERMINATING), }, nil) @@ -420,7 +417,7 @@ var _ = Describe("LoadBalancer", func() { }) It("should report no error if LB not found", func() { - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(nil, stackit.ErrorNotFound) + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(nil, stackitclient.ErrorNotFound) svc := minimalLoadBalancerService() @@ -429,11 +426,11 @@ var _ = Describe("LoadBalancer", func() { }) It("should trigger load balancer deletion", func() { - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(&loadbalancer.LoadBalancer{}, nil) - mockClient.EXPECT().ListCredentials(gomock.Any(), projectID).Return(&loadbalancer.ListCredentialsResponse{ + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(&loadbalancer.LoadBalancer{}, nil) + mockClient.EXPECT().ListCredentials(gomock.Any()).Return(&loadbalancer.ListCredentialsResponse{ Credentials: []loadbalancer.CredentialsResponse{}, }, nil) - mockClient.EXPECT().DeleteLoadBalancer(gomock.Any(), projectID, gomock.Any()).MinTimes(1).Return(nil) + mockClient.EXPECT().DeleteLoadBalancer(gomock.Any(), gomock.Any()).MinTimes(1).Return(nil) svc := minimalLoadBalancerService() @@ -446,7 +443,7 @@ var _ = Describe("LoadBalancer", func() { svc := minimalLoadBalancerService() name := loadBalancer.GetLoadBalancerName(context.Background(), "", svc) - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(&loadbalancer.LoadBalancer{ + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(&loadbalancer.LoadBalancer{ Options: &loadbalancer.LoadBalancerOptions{ Observability: &loadbalancer.LoadbalancerOptionObservability{ Metrics: &loadbalancer.LoadbalancerOptionMetrics{ @@ -460,14 +457,14 @@ var _ = Describe("LoadBalancer", func() { Listeners: []loadbalancer.Listener{}, }, nil) gomock.InOrder( - mockClient.EXPECT().UpdateLoadBalancer(gomock.Any(), projectID, name, gomock.All( + mockClient.EXPECT().UpdateLoadBalancer(gomock.Any(), name, gomock.All( hasNoObservabilityConfigured(), externalAddressSet("8.8.4.4"), - )).MinTimes(1).Return(&loadbalancer.LoadBalancer{}, nil), - mockClient.EXPECT().DeleteCredentials(gomock.Any(), projectID, sampleCredentialsRef).MinTimes(1).Return(nil), - mockClient.EXPECT().ListCredentials(gomock.Any(), projectID).Return(&loadbalancer.ListCredentialsResponse{ + )).MinTimes(1).Return(nil), + mockClient.EXPECT().DeleteCredentials(gomock.Any(), sampleCredentialsRef).MinTimes(1).Return(nil), + mockClient.EXPECT().ListCredentials(gomock.Any()).Return(&loadbalancer.ListCredentialsResponse{ Credentials: []loadbalancer.CredentialsResponse{}, }, nil), - mockClient.EXPECT().DeleteLoadBalancer(gomock.Any(), projectID, name).MinTimes(1).Return(nil), + mockClient.EXPECT().DeleteLoadBalancer(gomock.Any(), name).MinTimes(1).Return(nil), ) err := loadBalancer.EnsureLoadBalancerDeleted(context.Background(), clusterName, svc) @@ -480,7 +477,7 @@ var _ = Describe("LoadBalancer", func() { delete(svc.Annotations, externalIPAnnotation) name := loadBalancer.GetLoadBalancerName(context.Background(), "", svc) - mockClient.EXPECT().GetLoadBalancer(gomock.Any(), projectID, gomock.Any()).Return(&loadbalancer.LoadBalancer{ + mockClient.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any()).Return(&loadbalancer.LoadBalancer{ Options: &loadbalancer.LoadBalancerOptions{ Observability: &loadbalancer.LoadbalancerOptionObservability{ Metrics: &loadbalancer.LoadbalancerOptionMetrics{ @@ -494,14 +491,14 @@ var _ = Describe("LoadBalancer", func() { Listeners: []loadbalancer.Listener{}, }, nil) gomock.InOrder( - mockClient.EXPECT().UpdateLoadBalancer(gomock.Any(), projectID, name, gomock.All( + mockClient.EXPECT().UpdateLoadBalancer(gomock.Any(), name, gomock.All( hasNoObservabilityConfigured(), externalAddressNotSet(), ephemeralAddress(), - )).MinTimes(1).Return(&loadbalancer.LoadBalancer{}, nil), - mockClient.EXPECT().DeleteCredentials(gomock.Any(), projectID, sampleCredentialsRef).MinTimes(1).Return(nil), - mockClient.EXPECT().ListCredentials(gomock.Any(), projectID).Return(&loadbalancer.ListCredentialsResponse{ + )).MinTimes(1).Return(nil), + mockClient.EXPECT().DeleteCredentials(gomock.Any(), sampleCredentialsRef).MinTimes(1).Return(nil), + mockClient.EXPECT().ListCredentials(gomock.Any()).Return(&loadbalancer.ListCredentialsResponse{ Credentials: []loadbalancer.CredentialsResponse{}, }, nil), - mockClient.EXPECT().DeleteLoadBalancer(gomock.Any(), projectID, name).MinTimes(1).Return(nil), + mockClient.EXPECT().DeleteLoadBalancer(gomock.Any(), name).MinTimes(1).Return(nil), ) err := loadBalancer.EnsureLoadBalancerDeleted(context.Background(), clusterName, svc) @@ -511,7 +508,7 @@ var _ = Describe("LoadBalancer", func() { Describe("UpdateLoadBalancer", func() { It("should update targets", func() { - mockClient.EXPECT().UpdateTargetPool(gomock.Any(), projectID, gomock.Any(), "my-port", gomock.Any()).MinTimes(1) + mockClient.EXPECT().UpdateTargetPool(gomock.Any(), gomock.Any(), "my-port", gomock.Any()).MinTimes(1) svc := &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ @@ -546,7 +543,7 @@ var _ = Describe("LoadBalancer", func() { It("should update credentials if they exist", func() { pushURL := "test-endpoint" - mockClient.EXPECT().UpdateCredentials(gomock.Any(), projectID, sampleCredentialsRef, gomock.Any()).MinTimes(1).Return(nil) + mockClient.EXPECT().UpdateCredentials(gomock.Any(), sampleCredentialsRef, gomock.Any()).MinTimes(1).Return(nil) credentialRef, err := lbInModeIgnoreAndObs.reconcileObservabilityCredentials(context.Background(), &loadbalancer.LoadBalancer{ Name: new(sampleLBName), Options: &loadbalancer.LoadBalancerOptions{ @@ -568,7 +565,7 @@ var _ = Describe("LoadBalancer", func() { It("should try to update credentials if they exist", func() { errTest := errors.New("update credentials test error") - mockClient.EXPECT().UpdateCredentials(gomock.Any(), projectID, sampleCredentialsRef, gomock.Any()).MinTimes(1).Return(errTest) + mockClient.EXPECT().UpdateCredentials(gomock.Any(), sampleCredentialsRef, gomock.Any()).MinTimes(1).Return(errTest) credentialRef, err := lbInModeIgnoreAndObs.reconcileObservabilityCredentials(context.Background(), &loadbalancer.LoadBalancer{ Name: new(sampleLBName), Options: &loadbalancer.LoadBalancerOptions{ @@ -584,10 +581,10 @@ var _ = Describe("LoadBalancer", func() { }) It("should create credentials if they do not exist", func() { - mockClient.EXPECT().ListCredentials(gomock.Any(), projectID).Return(&loadbalancer.ListCredentialsResponse{ + mockClient.EXPECT().ListCredentials(gomock.Any()).Return(&loadbalancer.ListCredentialsResponse{ Credentials: []loadbalancer.CredentialsResponse{}, }, nil) - mockClient.EXPECT().CreateCredentials(gomock.Any(), projectID, gomock.Any()).MinTimes(1).Return(&loadbalancer.CreateCredentialsResponse{ + mockClient.EXPECT().CreateCredentials(gomock.Any(), gomock.Any()).MinTimes(1).Return(&loadbalancer.CreateCredentialsResponse{ Credential: &loadbalancer.CredentialsResponse{ CredentialsRef: new(sampleCredentialsRef), DisplayName: new(sampleLBName), @@ -607,11 +604,11 @@ var _ = Describe("LoadBalancer", func() { }) It("should return error if creating new credentials fails", func() { - mockClient.EXPECT().ListCredentials(gomock.Any(), projectID).Return(&loadbalancer.ListCredentialsResponse{ + mockClient.EXPECT().ListCredentials(gomock.Any()).Return(&loadbalancer.ListCredentialsResponse{ Credentials: []loadbalancer.CredentialsResponse{}, }, nil) errTest := errors.New("delete credentials test error") - mockClient.EXPECT().CreateCredentials(gomock.Any(), projectID, gomock.Any()).MinTimes(1).Return(nil, errTest) + mockClient.EXPECT().CreateCredentials(gomock.Any(), gomock.Any()).MinTimes(1).Return(nil, errTest) credentialRef, err := lbInModeIgnoreAndObs.reconcileObservabilityCredentials(context.Background(), &loadbalancer.LoadBalancer{ Name: new(sampleLBName), }, sampleLBName) @@ -623,7 +620,7 @@ var _ = Describe("LoadBalancer", func() { Describe("cleanUpCredentials", func() { It("should delete matching and only matching observability credentials", func() { gomock.InOrder( - mockClient.EXPECT().ListCredentials(gomock.Any(), projectID).Return(&loadbalancer.ListCredentialsResponse{ + mockClient.EXPECT().ListCredentials(gomock.Any()).Return(&loadbalancer.ListCredentialsResponse{ Credentials: []loadbalancer.CredentialsResponse{ { CredentialsRef: new("matching-1"), @@ -647,8 +644,8 @@ var _ = Describe("LoadBalancer", func() { }, }, }, nil).MinTimes(1), - mockClient.EXPECT().DeleteCredentials(gomock.Any(), projectID, "matching-1").MinTimes(1), - mockClient.EXPECT().DeleteCredentials(gomock.Any(), projectID, "matching-2").MinTimes(1), + mockClient.EXPECT().DeleteCredentials(gomock.Any(), "matching-1").MinTimes(1), + mockClient.EXPECT().DeleteCredentials(gomock.Any(), "matching-2").MinTimes(1), ) Expect(lbInModeIgnoreAndObs.cleanUpCredentials(context.Background(), "my-loadbalancer")).To(Succeed()) }) diff --git a/pkg/ccm/stackit.go b/pkg/ccm/stackit.go index 94e72246..d405425b 100644 --- a/pkg/ccm/stackit.go +++ b/pkg/ccm/stackit.go @@ -6,10 +6,9 @@ import ( "io" "os" + stackitclient "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client" stackitconfig "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/config" sdkconfig "github.com/stackitcloud/stackit-sdk-go/core/config" - iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" - loadbalancer "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api" "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/scheme" @@ -19,7 +18,6 @@ import ( "k8s.io/klog/v2" "github.com/stackitcloud/cloud-provider-stackit/pkg/metrics" - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit" ) const ( @@ -135,13 +133,9 @@ func NewCloudControllerManager(cfg *stackitconfig.CCMConfig, obs *MetricsRemoteW lbOpts = append(lbOpts, sdkconfig.WithToken(lbEmergencyAPIToken)) } - innerClient, err := loadbalancer.NewAPIClient(lbOpts...) + loadbalancingClient, err := stackitclient.New(cfg.Global.Region, cfg.Global.ProjectID).LoadBalancing(lbOpts) if err != nil { - return nil, err - } - client, err := stackit.NewLoadbalancerClient(innerClient.DefaultAPI, cfg.Global.Region) - if err != nil { - return nil, err + return nil, fmt.Errorf("failed to create Load Balancing stackitclient: %v", err) } iaasOpts := []sdkconfig.ConfigurationOption{ @@ -152,20 +146,17 @@ func NewCloudControllerManager(cfg *stackitconfig.CCMConfig, obs *MetricsRemoteW iaasOpts = append(iaasOpts, sdkconfig.WithEndpoint(cfg.Global.APIEndpoints.IaasAPI)) } - iaasInnerClient, err := iaas.NewAPIClient(iaasOpts...) + iaasClient, err := stackitclient.New(cfg.Global.Region, cfg.Global.ProjectID).IaaS(iaasOpts) if err != nil { - return nil, fmt.Errorf("failed to create IaaS client: %v", err) + return nil, fmt.Errorf("failed to create IaaS stackitclient: %v", err) } - nodeClient, err := stackit.NewNodeClient(iaasInnerClient) - if err != nil { - return nil, fmt.Errorf("failed to create Node client: %v", err) - } - instances, err := NewInstance(nodeClient, cfg.Global.ProjectID, cfg.Global.Region) + + instances, err := NewInstance(iaasClient, cfg.Global.ProjectID, cfg.Global.Region) if err != nil { return nil, err } - lb, err := NewLoadBalancer(client, cfg.Global.ProjectID, cfg.LoadBalancer, obs) + lb, err := NewLoadBalancer(loadbalancingClient, cfg.Global.ProjectID, cfg.LoadBalancer, obs) if err != nil { return nil, err } diff --git a/pkg/csi/blockstorage/controllerserver.go b/pkg/csi/blockstorage/controllerserver.go index 8de6237e..d01622f7 100644 --- a/pkg/csi/blockstorage/controllerserver.go +++ b/pkg/csi/blockstorage/controllerserver.go @@ -26,7 +26,10 @@ import ( "github.com/container-storage-interface/spec/lib/go/csi" "github.com/go-viper/mapstructure/v2" "github.com/kubernetes-csi/csi-lib-utils/protosanitizer" + sharedcsi "github.com/stackitcloud/cloud-provider-stackit/pkg/csi" "github.com/stackitcloud/cloud-provider-stackit/pkg/csi/util" + stackitclient "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client" + "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/stackiterrors" iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -34,15 +37,11 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/klog/v2" "k8s.io/utils/ptr" - - sharedcsi "github.com/stackitcloud/cloud-provider-stackit/pkg/csi" - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit" - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/stackiterrors" ) type controllerServer struct { Driver *Driver - Instance stackit.IaasClient + Instance stackitclient.IaaSClient csi.UnimplementedControllerServer } @@ -127,7 +126,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol if volSizeGB != *vols[0].Size { return nil, status.Error(codes.AlreadyExists, "Volume Already exists with same name and different capacity") } - if *vols[0].Status != stackit.VolumeAvailableStatus { + if *vols[0].Status != stackitclient.VolumeAvailableStatus { return nil, status.Error(codes.Internal, fmt.Sprintf("Volume %s is not in available state", *vols[0].Id)) } klog.V(4).Infof("Volume %s already exists in Availability Zone: %s of size %d GiB", *vols[0].Id, vols[0].AvailabilityZone, *vols[0].Size) @@ -151,21 +150,21 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol var sourceVolID string var sourceBackupID string var sourceSnapshotID string - var volumeSourceType stackit.VolumeSourceTypes + var volumeSourceType stackitclient.VolumeSourceTypes if content != nil && content.GetSnapshot() != nil { // Backups and Snapshots are the same for Kubernetes sourceSnapshotID = content.GetSnapshot().GetSnapshotId() sourceBackupID = content.GetSnapshot().GetSnapshotId() // By default, we try to clone volumes from snapshots - volumeSourceType = stackit.SnapshotSource + volumeSourceType = stackitclient.SnapshotSource - snap, err := cloud.GetSnapshotByID(ctx, sourceSnapshotID) + snap, err := cloud.GetSnapshot(ctx, sourceSnapshotID) if stackiterrors.IgnoreNotFound(err) != nil { return nil, status.Errorf(codes.Internal, "Failed to retrieve the source snapshot %s: %v", sourceSnapshotID, err) } // If the snapshot exists but is not yet available, fail. - if err == nil && *snap.Status != stackit.SnapshotReadyStatus { + if err == nil && *snap.Status != stackitclient.SnapshotReadyStatus { return nil, status.Errorf(codes.Unavailable, "VolumeContentSource Snapshot %s is not yet available. status: %s", sourceSnapshotID, *snap.Status) } // Only continue checking if the Snapshot is found @@ -184,17 +183,17 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol // check if a Backup with the same ID exists if stackiterrors.IsNotFound(err) { var back *iaas.Backup - back, err = cloud.GetBackupByID(ctx, sourceBackupID) + back, err = cloud.GetBackup(ctx, sourceBackupID) if err != nil { // If there is an error getting the backup as well, fail. return nil, status.Errorf(codes.NotFound, "VolumeContentSource Snapshot or Backup with ID %s not found", sourceBackupID) } - if *back.Status != stackit.SnapshotReadyStatus { + if *back.Status != stackitclient.SnapshotReadyStatus { // If the backup exists but is not yet available, fail. return nil, status.Errorf(codes.Unavailable, "VolumeContentSource Backup %s is not yet available. status: %s", sourceBackupID, *back.Status) } // If an available backup is found, create the volume from the backup. Implies that a Snapshot was not found. - volumeSourceType = stackit.BackupSource + volumeSourceType = stackitclient.BackupSource } } @@ -211,7 +210,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol if volAvailability != sourceVolume.AvailabilityZone { return nil, status.Errorf(codes.ResourceExhausted, "Volume must be in the same availability zone as source Volume. Got %s Required: %s", volAvailability, sourceVolume.AvailabilityZone) } - volumeSourceType = stackit.VolumeSource + volumeSourceType = stackitclient.VolumeSource } opts := &iaas.CreateVolumePayload{ @@ -225,7 +224,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol // Only set CreateVolumePayload.Source when actually creating volume from source/snapshot/backup if volumeSourceType != "" { - if volumeSourceType == stackit.SnapshotSource || volumeSourceType == stackit.VolumeSource { + if volumeSourceType == stackitclient.SnapshotSource || volumeSourceType == stackitclient.VolumeSource { // Changing the performance class while restoring from Snapshot or Volume is not supported opts.PerformanceClass = nil } @@ -241,7 +240,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol // The encryption config is already set for volumes created from snapshot or volume. We MUST never set it when // restoring from snapshot or volume. // TODO: Unclear if BackupSource is the same as the above or is actually changeable. IaaS is testing. - if volParams.Encrypted != nil && (volumeSourceType == "" || volumeSourceType == stackit.BackupSource) { + if volParams.Encrypted != nil && (volumeSourceType == "" || volumeSourceType == stackitclient.BackupSource) { encrypted, err := strconv.ParseBool(*volParams.Encrypted) if err != nil { return nil, status.Error(codes.InvalidArgument, "parameter encrypted must be of type boolean") @@ -259,7 +258,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol return nil, status.Errorf(codes.Internal, "CreateVolume failed with error %v", err) } - targetStatus := []string{stackit.VolumeAvailableStatus} + targetStatus := []string{stackitclient.VolumeAvailableStatus} // Recheck after: 0s (immediate), 20s, 45.6s, 78.36s, 120.31s err = cloud.WaitVolumeTargetStatusWithCustomBackoff(ctx, *vol.Id, targetStatus, &wait.Backoff{ @@ -360,7 +359,7 @@ func (cs *controllerServer) ControllerPublishVolume(ctx context.Context, req *cs return nil, status.Errorf(codes.Internal, "[ControllerPublishVolume] get volume failed with error %v", err) } - _, err = cloud.GetInstanceByID(ctx, instanceID) + _, err = cloud.GetServer(ctx, instanceID) if err != nil { if stackiterrors.IsNotFound(err) { return nil, status.Errorf(codes.NotFound, "[ControllerPublishVolume] Instance %s not found", instanceID) @@ -368,7 +367,10 @@ func (cs *controllerServer) ControllerPublishVolume(ctx context.Context, req *cs return nil, status.Errorf(codes.Internal, "[ControllerPublishVolume] GetInstanceByID failed with error %v", err) } - _, err = cloud.AttachVolume(ctx, instanceID, volumeID) + payload := iaas.AddVolumeToServerPayload{ + DeleteOnTermination: new(false), + } + _, err = cloud.AttachVolume(ctx, instanceID, volumeID, payload) if err != nil { klog.Errorf("Failed to AttachVolume: %v", err) return nil, status.Errorf(codes.Internal, "[ControllerPublishVolume] Attach Volume failed with error %v", err) @@ -397,7 +399,7 @@ func (cs *controllerServer) ControllerUnpublishVolume(ctx context.Context, req * if volumeID == "" { return nil, status.Error(codes.InvalidArgument, "[ControllerUnpublishVolume] Volume ID must be provided") } - _, err := cloud.GetInstanceByID(ctx, instanceID) + _, err := cloud.GetServer(ctx, instanceID) if err != nil { if stackiterrors.IsNotFound(err) { klog.V(3).Infof("ControllerUnpublishVolume assuming volume %s is detached, because node %s does not exist", volumeID, instanceID) @@ -449,7 +451,7 @@ func (cs *controllerServer) ListVolumes(ctx context.Context, req *csi.ListVolume var volumeList []iaas.Volume // TODO: There is not pagination for listing volumes so we will just pass empty to startingToken // It's not used anyway. - volumeList, _, err = cloud.ListVolumes(ctx, maxEntries, "") + volumeList, err = cloud.ListVolumes(ctx, maxEntries, "") if err != nil { klog.Errorf("Failed to ListVolumes: %v", err) if stackiterrors.IsInvalidError(err) { @@ -474,9 +476,9 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS name := req.Name volumeID := req.GetSourceVolumeId() - snapshotType := req.Parameters[stackit.SnapshotType] + snapshotType := req.Parameters[stackitclient.SnapshotType] filters := map[string]string{"Name": name} - backupMaxDurationSecondsPerGB := stackit.BackupMaxDurationSecondsPerGBDefault + backupMaxDurationSecondsPerGB := stackitclient.BackupMaxDurationSecondsPerGBDefault // Current time, used for CreatedAt var ctime *timestamppb.Timestamp @@ -535,7 +537,7 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS } // Get the max duration to wait in seconds per GB of snapshot and fail if parsing fails - if item, ok := (req.Parameters)[stackit.BackupMaxDurationPerGB]; ok { + if item, ok := (req.Parameters)[stackitclient.BackupMaxDurationPerGB]; ok { backupMaxDurationSecondsPerGB, err = strconv.Atoi(item) if err != nil { klog.Errorf("Setting backup-max-duration-seconds-per-gb failed due to a parsing error: %v", err) @@ -546,7 +548,7 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS // Create the snapshot if the backup does not already exist and wait for it to be ready if !backupAlreadyExists { - snap, err = cs.createSnapshot(ctx, cloud, name, volumeID, req.Parameters) + snap, err = cs.createSnapshot(ctx, name, volumeID, req.Parameters) if err != nil { return nil, err } @@ -599,7 +601,7 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS } // Necessary to get all the backup information, including size. - backup, err = cloud.GetBackupByID(ctx, *backup.Id) + backup, err = cloud.GetBackup(ctx, *backup.Id) if err != nil { klog.Errorf("Failed to GetBackupByID after backup creation: %v", err) return nil, status.Error(codes.Internal, fmt.Sprintf("GetBackupByID failed with error %v", err)) @@ -622,12 +624,12 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS }, nil } -func (cs *controllerServer) createSnapshot(ctx context.Context, cloud stackit.IaasClient, name, volumeID string, parameters map[string]string) (*iaas.Snapshot, error) { //nolint:lll // looks weird when shortened +func (cs *controllerServer) createSnapshot(ctx context.Context, name, volumeID string, parameters map[string]string) (*iaas.Snapshot, error) { //nolint:lll // looks weird when shortened filters := map[string]string{} filters["Name"] = name // List existing snapshots with the same name - snapshots, _, err := cloud.ListSnapshots(ctx, filters) + snapshots, err := cs.Instance.ListSnapshots(ctx, filters) if err != nil { klog.Errorf("Failed to query for existing Snapshot during CreateSnapshot: %v", err) return nil, status.Error(codes.Internal, "Failed to get snapshots") @@ -665,7 +667,15 @@ func (cs *controllerServer) createSnapshot(ctx context.Context, cloud stackit.Ia } } - snap, err := cloud.CreateSnapshot(ctx, name, volumeID, properties) + payload := &iaas.CreateSnapshotPayload{ + Name: new(name), + VolumeId: volumeID, + } + if len(properties) > 0 { + payload.Labels = stackitclient.LabelsFromTags(properties) + } + + snap, err := cs.Instance.CreateSnapshot(ctx, payload) if err != nil { klog.Errorf("Failed to Create snapshot: %v", err) return nil, status.Errorf(codes.Internal, "CreateSnapshot failed with error %v", err) @@ -676,7 +686,7 @@ func (cs *controllerServer) createSnapshot(ctx context.Context, cloud stackit.Ia return snap, nil } -func (cs *controllerServer) createBackup(ctx context.Context, cloud stackit.IaasClient, name, volumeID string, snap *iaas.Snapshot, parameters map[string]string) (*iaas.Backup, error) { //nolint:lll // looks weird when shortened +func (cs *controllerServer) createBackup(ctx context.Context, cloud stackitclient.IaaSClient, name, volumeID string, snap *iaas.Snapshot, parameters map[string]string) (*iaas.Backup, error) { //nolint:lll // looks weird when shortened // Add cluster ID to the snapshot metadata // TODO: Use once IaaS has extended the label regex to allow for forward slashes and dots // properties := map[string]string{blockStorageCSIClusterIDKey: cs.Driver.clusterID} @@ -684,7 +694,7 @@ func (cs *controllerServer) createBackup(ctx context.Context, cloud stackit.Iaas // see https://github.com/kubernetes-csi/external-snapshotter/pull/375/ // Also, we don't want to tag every param, but we do honor the RecognizedCSISnapshotterParams - for _, mKey := range append(sharedcsi.RecognizedCSISnapshotterParams, stackit.SnapshotType) { + for _, mKey := range append(sharedcsi.RecognizedCSISnapshotterParams, stackitclient.SnapshotType) { if v, ok := parameters[mKey]; ok { properties[mKey] = v } @@ -712,7 +722,7 @@ func (cs *controllerServer) DeleteSnapshot(ctx context.Context, req *csi.DeleteS } // If volumeSnapshot object was linked to a cinder backup, delete the backup. - back, err := cloud.GetBackupByID(ctx, id) + back, err := cloud.GetBackup(ctx, id) if err == nil && back != nil { err = cloud.DeleteBackup(ctx, id) if err != nil { @@ -739,7 +749,7 @@ func (cs *controllerServer) ListSnapshots(ctx context.Context, req *csi.ListSnap snapshotID := req.GetSnapshotId() if snapshotID != "" { - snap, err := cloud.GetSnapshotByID(ctx, snapshotID) + snap, err := cloud.GetSnapshot(ctx, snapshotID) if err != nil { if stackiterrors.IsNotFound(err) { klog.V(3).Infof("Snapshot %s not found", snapshotID) @@ -781,8 +791,8 @@ func (cs *controllerServer) ListSnapshots(ctx context.Context, req *csi.ListSnap } // Only retrieve snapshots that are available - filters["Status"] = stackit.SnapshotReadyStatus - slist, nextPageToken, err = cloud.ListSnapshots(ctx, filters) + filters["Status"] = stackitclient.SnapshotReadyStatus + slist, err = cloud.ListSnapshots(ctx, filters) if err != nil { klog.Errorf("Failed to ListSnapshots: %v", err) return nil, status.Errorf(codes.Internal, "ListSnapshots failed with error %v", err) @@ -940,13 +950,16 @@ func (cs *controllerServer) ControllerExpandVolume(ctx context.Context, req *csi }, nil } - err = cloud.ExpandVolume(ctx, volumeID, *volume.Status, volSizeGB) + payload := iaas.ResizeVolumePayload{ + Size: volSizeGB, + } + err = cloud.ExpandVolume(ctx, volumeID, *volume.Status, payload) if err != nil { return nil, status.Errorf(codes.Internal, "Could not resize volume %q to size %v: %v", volumeID, volSizeGB, err) } // we need wait for the volume to be available or InUse, it might be error_extending in some scenario - targetStatus := []string{stackit.VolumeAvailableStatus, stackit.VolumeAttachedStatus} + targetStatus := []string{stackitclient.VolumeAvailableStatus, stackitclient.VolumeAttachedStatus} err = cloud.WaitVolumeTargetStatus(ctx, volumeID, targetStatus) if err != nil { klog.Errorf("Failed to WaitVolumeTargetStatus of volume %s: %v", volumeID, err) @@ -963,13 +976,13 @@ func (cs *controllerServer) ControllerExpandVolume(ctx context.Context, req *csi func getCreateVolumeResponse(vol *iaas.Volume) *csi.CreateVolumeResponse { var volsrc *csi.VolumeContentSource - var volumeSourceType stackit.VolumeSourceTypes + var volumeSourceType stackitclient.VolumeSourceTypes volCnx := map[string]string{} if vol.Source != nil { - volumeSourceType = stackit.VolumeSourceTypes(vol.Source.Type) + volumeSourceType = stackitclient.VolumeSourceTypes(vol.Source.Type) switch volumeSourceType { - case stackit.VolumeSource: + case stackitclient.VolumeSource: volCnx[ResizeRequired] = "true" volsrc = &csi.VolumeContentSource{ @@ -979,7 +992,7 @@ func getCreateVolumeResponse(vol *iaas.Volume) *csi.CreateVolumeResponse { }, }, } - case stackit.BackupSource: + case stackitclient.BackupSource: volCnx[ResizeRequired] = "true" volsrc = &csi.VolumeContentSource{ @@ -989,7 +1002,7 @@ func getCreateVolumeResponse(vol *iaas.Volume) *csi.CreateVolumeResponse { }, }, } - case stackit.SnapshotSource: + case stackitclient.SnapshotSource: volCnx[ResizeRequired] = "true" volsrc = &csi.VolumeContentSource{ @@ -1021,14 +1034,14 @@ func getCreateVolumeResponse(vol *iaas.Volume) *csi.CreateVolumeResponse { return resp } -// determineSourceIDForSourceType returns the correct sourceID for the given stackit.VolumeSourceTypes -func determineSourceIDForSourceType(srcType stackit.VolumeSourceTypes, sourceSnapshotID, sourceVolID string) string { +// determineSourceIDForSourceType returns the correct sourceID for the given stackitclient.VolumeSourceTypes +func determineSourceIDForSourceType(srcType stackitclient.VolumeSourceTypes, sourceSnapshotID, sourceVolID string) string { switch srcType { - case stackit.BackupSource: + case stackitclient.BackupSource: return sourceSnapshotID - case stackit.SnapshotSource: + case stackitclient.SnapshotSource: return sourceSnapshotID - case stackit.VolumeSource: + case stackitclient.VolumeSource: return sourceVolID default: return "" diff --git a/pkg/csi/blockstorage/controllerserver_test.go b/pkg/csi/blockstorage/controllerserver_test.go index f745c2d9..6cee248d 100644 --- a/pkg/csi/blockstorage/controllerserver_test.go +++ b/pkg/csi/blockstorage/controllerserver_test.go @@ -11,23 +11,23 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/stackitcloud/cloud-provider-stackit/pkg/csi/util" + stackitclient "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client" + stackitclientmock "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client/mock" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" "go.uber.org/mock/gomock" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/timestamppb" - - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit" ) var _ = Describe("ControllerServer test", Ordered, func() { var ( fakeCs *controllerServer - iaasClient *stackit.MockIaasClient + iaasClient *stackitclientmock.MockIaaSClient FakeEndpoint = "tcp://127.0.0.1:10000" FakeCluster = "cluster" - expandTargetStatus = []string{stackit.VolumeAvailableStatus, stackit.VolumeAttachedStatus} + expandTargetStatus = []string{stackitclient.VolumeAvailableStatus, stackitclient.VolumeAttachedStatus} stdCapRange = &csi.CapacityRange{ RequiredBytes: util.GIBIBYTE * 20, } @@ -51,7 +51,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { d := NewDriver(&DriverOpts{Endpoint: FakeEndpoint, ClusterID: FakeCluster}) mockCtrl := gomock.NewController(GinkgoT()) - iaasClient = stackit.NewMockIaasClient(mockCtrl) + iaasClient = stackitclientmock.NewMockIaaSClient(mockCtrl) fakeCs = NewControllerServer(d, iaasClient) }) @@ -188,7 +188,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { Id: new("existing-available-volume-id"), Name: new("new volume"), Size: new(int64(20)), - Status: new(stackit.VolumeAvailableStatus), + Status: new(stackitclient.VolumeAvailableStatus), AvailabilityZone: "eu01", }, }, nil) @@ -212,7 +212,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { Id: new("existing-available-volume-id"), Name: new("new volume"), Size: new(int64(30)), - Status: new(stackit.VolumeAvailableStatus), + Status: new(stackitclient.VolumeAvailableStatus), AvailabilityZone: "eu01", }, }, nil) @@ -234,7 +234,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { Id: new("existing-available-volume-id"), Name: new("new volume"), Size: new(int64(20)), - Status: new(stackit.VolumeAttachedStatus), + Status: new(stackitclient.VolumeAttachedStatus), AvailabilityZone: "eu01", }, }, nil) @@ -296,7 +296,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { }, } - iaasClient.EXPECT().GetSnapshotByID(gomock.Any(), "snapshot-id").Return(&iaas.Snapshot{ + iaasClient.EXPECT().GetSnapshot(gomock.Any(), "snapshot-id").Return(&iaas.Snapshot{ Id: new("snapshot-id"), Status: new("AVAILABLE"), VolumeId: "snapshot-volume-id", @@ -305,17 +305,23 @@ var _ = Describe("ControllerServer test", Ordered, func() { Id: new("snapshot-volume-id"), AvailabilityZone: "eu01", }, nil) - iaasClient.EXPECT().CreateVolume(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, opts *iaas.CreateVolumePayload) { + iaasClient.EXPECT(). + CreateVolume(gomock.Any(), gomock.Any()). + DoAndReturn(func(_ context.Context, opts *iaas.CreateVolumePayload) (*iaas.Volume, error) { Expect(opts.Source.Id).To(Equal("snapshot-id")) Expect(opts.Source.Type).To(Equal("snapshot")) - }). - Return(&iaas.Volume{ - Id: new("volume-id"), - Name: new("new volume"), - AvailabilityZone: "eu01", - Size: new(int64(20)), - }, nil) + + volumeID := "volume-id" + name := "new volume" + size := int64(20) + + return &iaas.Volume{ + Id: &volumeID, + Name: &name, + AvailabilityZone: "eu01", + Size: &size, + }, nil + }) iaasClient.EXPECT().WaitVolumeTargetStatusWithCustomBackoff(gomock.Any(), "volume-id", gomock.Any(), gomock.Any()).Return(nil) _, err := fakeCs.CreateVolume(context.Background(), req) @@ -331,7 +337,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { }, } - iaasClient.EXPECT().GetSnapshotByID(gomock.Any(), "snapshot-id").Return(nil, fmt.Errorf("injected error")) + iaasClient.EXPECT().GetSnapshot(gomock.Any(), "snapshot-id").Return(nil, fmt.Errorf("injected error")) _, err := fakeCs.CreateVolume(context.Background(), req) Expect(err).To(HaveOccurred()) @@ -347,7 +353,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { }, } - iaasClient.EXPECT().GetSnapshotByID(gomock.Any(), "snapshot-id").Return(&iaas.Snapshot{ + iaasClient.EXPECT().GetSnapshot(gomock.Any(), "snapshot-id").Return(&iaas.Snapshot{ Id: new("snapshot-id"), Status: new("creating"), }, nil) @@ -367,25 +373,31 @@ var _ = Describe("ControllerServer test", Ordered, func() { }, } - iaasClient.EXPECT().GetSnapshotByID(gomock.Any(), "snapshot-id").Return(nil, + iaasClient.EXPECT().GetSnapshot(gomock.Any(), "snapshot-id").Return(nil, &oapierror.GenericOpenAPIError{ StatusCode: http.StatusNotFound, }) - iaasClient.EXPECT().GetBackupByID(gomock.Any(), "snapshot-id").Return(&iaas.Backup{ + iaasClient.EXPECT().GetBackup(gomock.Any(), "snapshot-id").Return(&iaas.Backup{ Status: new("AVAILABLE"), AvailabilityZone: new("eu01"), }, nil) - iaasClient.EXPECT().CreateVolume(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, opts *iaas.CreateVolumePayload) { + iaasClient.EXPECT(). + CreateVolume(gomock.Any(), gomock.Any()). + DoAndReturn(func(_ context.Context, opts *iaas.CreateVolumePayload) (*iaas.Volume, error) { Expect(opts.Source.Id).To(Equal("snapshot-id")) Expect(opts.Source.Type).To(Equal("backup")) - }). - Return(&iaas.Volume{ - Id: new("volume-id"), - Name: new("new volume"), - AvailabilityZone: "eu01", - Size: new(int64(20)), - }, nil) + + volumeID := "volume-id" + name := "new volume" + size := int64(20) + + return &iaas.Volume{ + Id: &volumeID, + Name: &name, + AvailabilityZone: "eu01", + Size: &size, + }, nil + }) iaasClient.EXPECT().WaitVolumeTargetStatusWithCustomBackoff(gomock.Any(), "volume-id", gomock.Any(), gomock.Any()).Return(nil) _, err := fakeCs.CreateVolume(context.Background(), req) @@ -406,7 +418,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { }, } - iaasClient.EXPECT().GetSnapshotByID(gomock.Any(), "snapshot-id").Return(&iaas.Snapshot{ + iaasClient.EXPECT().GetSnapshot(gomock.Any(), "snapshot-id").Return(&iaas.Snapshot{ Id: new("snapshot-id"), VolumeId: "volume-id", Status: new("AVAILABLE"), @@ -432,11 +444,11 @@ var _ = Describe("ControllerServer test", Ordered, func() { }, } - iaasClient.EXPECT().GetSnapshotByID(gomock.Any(), "snapshot-id").Return(nil, + iaasClient.EXPECT().GetSnapshot(gomock.Any(), "snapshot-id").Return(nil, &oapierror.GenericOpenAPIError{ StatusCode: http.StatusNotFound, }) - iaasClient.EXPECT().GetBackupByID(gomock.Any(), "snapshot-id").Return(nil, + iaasClient.EXPECT().GetBackup(gomock.Any(), "snapshot-id").Return(nil, &oapierror.GenericOpenAPIError{ StatusCode: http.StatusNotFound, }) @@ -456,11 +468,11 @@ var _ = Describe("ControllerServer test", Ordered, func() { }, } - iaasClient.EXPECT().GetSnapshotByID(gomock.Any(), "snapshot-id").Return(nil, + iaasClient.EXPECT().GetSnapshot(gomock.Any(), "snapshot-id").Return(nil, &oapierror.GenericOpenAPIError{ StatusCode: http.StatusNotFound, }) - iaasClient.EXPECT().GetBackupByID(gomock.Any(), "snapshot-id").Return(&iaas.Backup{ + iaasClient.EXPECT().GetBackup(gomock.Any(), "snapshot-id").Return(&iaas.Backup{ Status: new("creating"), }, nil) @@ -484,17 +496,23 @@ var _ = Describe("ControllerServer test", Ordered, func() { Status: new("AVAILABLE"), AvailabilityZone: "eu01", }, nil) - iaasClient.EXPECT().CreateVolume(gomock.Any(), gomock.Any()). - Do(func(_ context.Context, opts *iaas.CreateVolumePayload) { + iaasClient.EXPECT(). + CreateVolume(gomock.Any(), gomock.Any()). + DoAndReturn(func(_ context.Context, opts *iaas.CreateVolumePayload) (*iaas.Volume, error) { Expect(opts.Source.Id).To(Equal("volume-source-id")) Expect(opts.Source.Type).To(Equal("volume")) - }). - Return(&iaas.Volume{ - Id: new("volume-id"), - Name: new("new volume"), - AvailabilityZone: "eu01", - Size: new(int64(20)), - }, nil) + + name := "new volume" + volumeID := "volume-id" + size := int64(20) + + return &iaas.Volume{ + Id: &volumeID, + Name: &name, + AvailabilityZone: "eu01", + Size: &size, + }, nil + }) iaasClient.EXPECT().WaitVolumeTargetStatusWithCustomBackoff(gomock.Any(), "volume-id", gomock.Any(), gomock.Any()).Return(nil) _, err := fakeCs.CreateVolume(context.Background(), req) @@ -635,7 +653,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { Size: new(int64(10)), ServerId: new("serverID"), }, - }, "", nil) + }, nil) resp, err := fakeCs.ListVolumes(context.Background(), req) Expect(err).Should(Not(HaveOccurred())) Expect(resp.GetEntries()).Should(Equal(expectedVolumeResponseList)) @@ -649,8 +667,8 @@ var _ = Describe("ControllerServer test", Ordered, func() { VolumeCapability: stdVolCap, } iaasClient.EXPECT().GetVolume(gomock.Any(), req.VolumeId).Return(&iaas.Volume{}, nil) - iaasClient.EXPECT().GetInstanceByID(gomock.Any(), "fake").Return(&iaas.Server{}, nil) - iaasClient.EXPECT().AttachVolume(gomock.Any(), req.NodeId, req.VolumeId).Return(req.VolumeId, nil) + iaasClient.EXPECT().GetServer(gomock.Any(), "fake").Return(&iaas.Server{}, nil) + iaasClient.EXPECT().AttachVolume(gomock.Any(), req.NodeId, req.VolumeId, gomock.Any()).Return(req.VolumeId, nil) iaasClient.EXPECT().WaitDiskAttached(gomock.Any(), req.NodeId, req.VolumeId).Return(nil) _, err := fakeCs.ControllerPublishVolume(context.Background(), req) Expect(err).To(Not(HaveOccurred())) @@ -662,7 +680,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { VolumeId: "fake", NodeId: "fake", } - iaasClient.EXPECT().GetInstanceByID(gomock.Any(), "fake").Return(&iaas.Server{}, nil) + iaasClient.EXPECT().GetServer(gomock.Any(), "fake").Return(&iaas.Server{}, nil) iaasClient.EXPECT().DetachVolume(gomock.Any(), req.NodeId, req.VolumeId).Return(nil) iaasClient.EXPECT().WaitDiskDetached(gomock.Any(), req.NodeId, req.VolumeId).Return(nil) _, err := fakeCs.ControllerUnpublishVolume(context.Background(), req) @@ -694,9 +712,9 @@ var _ = Describe("ControllerServer test", Ordered, func() { volSizeGB := util.RoundUpSize(req.GetCapacityRange().GetRequiredBytes(), util.GIBIBYTE) iaasClient.EXPECT().GetVolume(gomock.Any(), req.VolumeId).Return(&iaas.Volume{ Size: new(int64(10)), - Status: new(stackit.VolumeAvailableStatus), + Status: new(stackitclient.VolumeAvailableStatus), }, nil) - iaasClient.EXPECT().ExpandVolume(gomock.Any(), req.VolumeId, stackit.VolumeAvailableStatus, volSizeGB).Return(nil) + iaasClient.EXPECT().ExpandVolume(gomock.Any(), req.VolumeId, stackitclient.VolumeAvailableStatus, iaas.ResizeVolumePayload{Size: volSizeGB}).Return(nil) iaasClient.EXPECT().WaitVolumeTargetStatus(gomock.Any(), req.VolumeId, expandTargetStatus).Return(nil) _, err := fakeCs.ControllerExpandVolume(context.Background(), req) Expect(err).To(Not(HaveOccurred())) @@ -706,12 +724,16 @@ var _ = Describe("ControllerServer test", Ordered, func() { VolumeId: "fake", CapacityRange: stdCapRange, } + volSizeGB := util.RoundUpSize(req.GetCapacityRange().GetRequiredBytes(), util.GIBIBYTE) iaasClient.EXPECT().GetVolume(gomock.Any(), req.VolumeId).Return(&iaas.Volume{ Size: new(int64(10)), Status: new("ERROR"), }, nil) - iaasClient.EXPECT().ExpandVolume(gomock.Any(), req.VolumeId, "ERROR", volSizeGB).Return(fmt.Errorf("volume cannot be resized, when status is ERROR")) + iaasClient.EXPECT().ExpandVolume(gomock.Any(), req.VolumeId, "ERROR", iaas.ResizeVolumePayload{ + Size: volSizeGB, + }).Return(fmt.Errorf("volume cannot be resized, when status is ERROR")) + _, err := fakeCs.ControllerExpandVolume(context.Background(), req) Expect(err).To(HaveOccurred()) Expect(status.Convert(err).Code()).To(Equal(codes.Internal)) @@ -730,8 +752,6 @@ var _ = Describe("ControllerServer test", Ordered, func() { }) It("should create backup successfully", func() { // TODO: Use once IaaS has extended the label regex to allow for forward slashes and dots - // properties := map[string]string{blockStorageCSIClusterIDKey: "cluster"} - properties := map[string]string{} expectedSnap := &iaas.Snapshot{ Id: new("fake-snapshot"), Name: new("fake-snapshot"), @@ -752,15 +772,15 @@ var _ = Describe("ControllerServer test", Ordered, func() { iaasClient.EXPECT().ListBackups(gomock.Any(), gomock.Any()).Return([]iaas.Backup{}, nil) // Backups are created from snapshots - iaasClient.EXPECT().ListSnapshots(gomock.Any(), gomock.Any()).Return([]iaas.Snapshot{}, "", nil) - iaasClient.EXPECT().CreateSnapshot(gomock.Any(), "fake-snapshot", req.SourceVolumeId, properties).Return(expectedSnap, nil) + iaasClient.EXPECT().ListSnapshots(gomock.Any(), gomock.Any()).Return([]iaas.Snapshot{}, nil) + iaasClient.EXPECT().CreateSnapshot(gomock.Any(), gomock.Any()).Return(expectedSnap, nil) iaasClient.EXPECT().WaitSnapshotReady(gomock.Any(), "fake-snapshot").Return(expectedSnap.Status, nil) // Actually create the backup from the snapshot iaasClient.EXPECT().CreateBackup(gomock.Any(), "fake-snapshot", req.GetSourceVolumeId(), "fake-snapshot", gomock.Any()).Return(expectedBackup, nil) - iaasClient.EXPECT().WaitBackupReady(gomock.Any(), "fake-backup", *expectedSnap.Size, stackit.BackupMaxDurationSecondsPerGBDefault). + iaasClient.EXPECT().WaitBackupReady(gomock.Any(), "fake-backup", *expectedSnap.Size, stackitclient.BackupMaxDurationSecondsPerGBDefault). Return(new("AVAILABLE"), nil) - iaasClient.EXPECT().GetBackupByID(gomock.Any(), "fake-backup").Return(expectedBackup, nil) + iaasClient.EXPECT().GetBackup(gomock.Any(), "fake-backup").Return(expectedBackup, nil) // Remove the snapshot after the backup is created iaasClient.EXPECT().DeleteSnapshot(gomock.Any(), "fake-snapshot").Return(nil) @@ -780,8 +800,8 @@ var _ = Describe("ControllerServer test", Ordered, func() { } iaasClient.EXPECT().ListBackups(gomock.Any(), gomock.Any()).Return([]iaas.Backup{*expectedBackup}, nil) - iaasClient.EXPECT().WaitBackupReady(gomock.Any(), "fake-backup", int64(0), stackit.BackupMaxDurationSecondsPerGBDefault).Return(new("AVAILABLE"), nil) - iaasClient.EXPECT().GetBackupByID(gomock.Any(), "fake-backup").Return(expectedBackup, nil) + iaasClient.EXPECT().WaitBackupReady(gomock.Any(), "fake-backup", int64(0), stackitclient.BackupMaxDurationSecondsPerGBDefault).Return(new("AVAILABLE"), nil) + iaasClient.EXPECT().GetBackup(gomock.Any(), "fake-backup").Return(expectedBackup, nil) // Remove the snapshot after the backup is created iaasClient.EXPECT().DeleteSnapshot(gomock.Any(), *expectedBackup.SnapshotId).Return(nil) @@ -817,16 +837,14 @@ var _ = Describe("ControllerServer test", Ordered, func() { }) It("should honor custom wait time for backup creation", func() { req.Parameters = map[string]string{ - stackit.BackupMaxDurationPerGB: "120", - stackit.SnapshotType: "backup", + stackitclient.BackupMaxDurationPerGB: "120", + stackitclient.SnapshotType: "backup", } - customWaitTime, err := strconv.Atoi((req.Parameters)[stackit.BackupMaxDurationPerGB]) + customWaitTime, err := strconv.Atoi((req.Parameters)[stackitclient.BackupMaxDurationPerGB]) Expect(err).To(Not(HaveOccurred())) // TODO: Use once IaaS has extended the label regex to allow for forward slashes and dots - // properties := map[string]string{blockStorageCSIClusterIDKey: "cluster"} - properties := map[string]string{} expectedSnap := &iaas.Snapshot{ Id: new("fake-snapshot"), Name: new("fake-snapshot"), @@ -847,14 +865,14 @@ var _ = Describe("ControllerServer test", Ordered, func() { iaasClient.EXPECT().ListBackups(gomock.Any(), gomock.Any()).Return([]iaas.Backup{}, nil) // Backups are created from snapshots - iaasClient.EXPECT().ListSnapshots(gomock.Any(), gomock.Any()).Return([]iaas.Snapshot{}, "", nil) - iaasClient.EXPECT().CreateSnapshot(gomock.Any(), "fake-snapshot", req.SourceVolumeId, properties).Return(expectedSnap, nil) + iaasClient.EXPECT().ListSnapshots(gomock.Any(), gomock.Any()).Return([]iaas.Snapshot{}, nil) + iaasClient.EXPECT().CreateSnapshot(gomock.Any(), gomock.Any()).Return(expectedSnap, nil) iaasClient.EXPECT().WaitSnapshotReady(gomock.Any(), "fake-snapshot").Return(expectedSnap.Status, nil) // Actually create the backup from the snapshot iaasClient.EXPECT().CreateBackup(gomock.Any(), "fake-snapshot", req.GetSourceVolumeId(), "fake-snapshot", gomock.Any()).Return(expectedBackup, nil) iaasClient.EXPECT().WaitBackupReady(gomock.Any(), "fake-backup", *expectedSnap.Size, customWaitTime).Return(new("AVAILABLE"), nil) - iaasClient.EXPECT().GetBackupByID(gomock.Any(), "fake-backup").Return(expectedBackup, nil) + iaasClient.EXPECT().GetBackup(gomock.Any(), "fake-backup").Return(expectedBackup, nil) // Remove the snapshot after the backup is created iaasClient.EXPECT().DeleteSnapshot(gomock.Any(), "fake-snapshot").Return(nil) @@ -880,13 +898,9 @@ var _ = Describe("ControllerServer test", Ordered, func() { Size: new(int64(10)), CreatedAt: new(time.Now()), } - // TODO: Use once IaaS has extended the label regex to allow for forward slashes and dots - // properties := map[string]string{blockStorageCSIClusterIDKey: "cluster"} - properties := map[string]string{} - // TODO: Again filters are not implemented yet by the API - iaasClient.EXPECT().ListSnapshots(gomock.Any(), gomock.Any()).Return([]iaas.Snapshot{}, "", nil) - iaasClient.EXPECT().CreateSnapshot(gomock.Any(), "fake-snapshot", req.SourceVolumeId, properties).Return(expectedSnap, nil) + iaasClient.EXPECT().ListSnapshots(gomock.Any(), gomock.Any()).Return([]iaas.Snapshot{}, nil) + iaasClient.EXPECT().CreateSnapshot(gomock.Any(), gomock.Any()).Return(expectedSnap, nil) iaasClient.EXPECT().WaitSnapshotReady(gomock.Any(), "fake-snapshot").Return(expectedSnap.Status, nil) _, err := fakeCs.CreateSnapshot(context.Background(), req) Expect(err).ToNot(HaveOccurred()) @@ -902,7 +916,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { } // TODO: Again filters are not implemented yet by the API - iaasClient.EXPECT().ListSnapshots(gomock.Any(), gomock.Any()).Return([]iaas.Snapshot{*expectedSnap}, "", nil) + iaasClient.EXPECT().ListSnapshots(gomock.Any(), gomock.Any()).Return([]iaas.Snapshot{*expectedSnap}, nil) iaasClient.EXPECT().WaitSnapshotReady(gomock.Any(), "fake-snapshot").Return(new("AVAILABLE"), nil) _, err := fakeCs.CreateSnapshot(context.Background(), req) Expect(err).ToNot(HaveOccurred()) @@ -916,7 +930,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { { Id: new("fake-snapshot2"), }, - }, "", nil) + }, nil) _, err := fakeCs.CreateSnapshot(context.Background(), req) Expect(err).To(HaveOccurred()) Expect(status.Convert(err).Code()).To(Equal(codes.Internal)) @@ -929,7 +943,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { Id: new("fake-snapshot"), VolumeId: "something-different", }, - }, "", nil) + }, nil) _, err := fakeCs.CreateSnapshot(context.Background(), req) Expect(err).To(HaveOccurred()) Expect(status.Convert(err).Code()).To(Equal(codes.AlreadyExists)) @@ -954,7 +968,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { }, }, } - iaasClient.EXPECT().GetSnapshotByID(gomock.Any(), "special-snapshot").Return(&iaas.Snapshot{ + iaasClient.EXPECT().GetSnapshot(gomock.Any(), "special-snapshot").Return(&iaas.Snapshot{ Id: new("special-snapshot"), VolumeId: "fake", Size: new(int64(10)), @@ -987,7 +1001,7 @@ var _ = Describe("ControllerServer test", Ordered, func() { VolumeId: "something-different", Size: new(int64(10)), CreatedAt: new(snapShotCreationTime), - }}, "", nil) + }}, nil) resp, err := fakeCs.ListSnapshots(context.Background(), req) Expect(err).To(Not(HaveOccurred())) Expect(resp.GetEntries()).Should(Equal(expectedSnapshotListResponse)) diff --git a/pkg/csi/blockstorage/driver.go b/pkg/csi/blockstorage/driver.go index 56fd1818..db641777 100644 --- a/pkg/csi/blockstorage/driver.go +++ b/pkg/csi/blockstorage/driver.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/container-storage-interface/spec/lib/go/csi" - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit" + stackitclient "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client" stackitconfig "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/config" corev1 "k8s.io/client-go/listers/core/v1" "k8s.io/klog/v2" @@ -134,7 +134,7 @@ func (d *Driver) AddNodeServiceCapabilities(nl []csi.NodeServiceCapability_RPC_T return nil } -func (d *Driver) SetupControllerService(instance stackit.IaasClient) { +func (d *Driver) SetupControllerService(instance stackitclient.IaaSClient) { klog.Info("Providing controller service") d.cs = NewControllerServer(d, instance) } diff --git a/pkg/csi/blockstorage/sanity_test.go b/pkg/csi/blockstorage/sanity_test.go index b17b0b6e..055cdb10 100644 --- a/pkg/csi/blockstorage/sanity_test.go +++ b/pkg/csi/blockstorage/sanity_test.go @@ -12,16 +12,16 @@ import ( "github.com/google/uuid" "github.com/kubernetes-csi/csi-test/v5/pkg/sanity" . "github.com/onsi/ginkgo/v2" - stackitconfig "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/config" - mountutils "k8s.io/mount-utils" - exec "k8s.io/utils/exec/testing" - "github.com/stackitcloud/cloud-provider-stackit/pkg/csi/util/mount" - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit" + stackitclient "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client" + stackitclientmock "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client/mock" + stackitconfig "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/config" "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/metadata" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" "go.uber.org/mock/gomock" + mountutils "k8s.io/mount-utils" + exec "k8s.io/utils/exec/testing" ) var _ = Describe("CSI sanity test", Ordered, func() { @@ -29,7 +29,7 @@ var _ = Describe("CSI sanity test", Ordered, func() { var ( driver *Driver opts *DriverOpts - iaasClient *stackit.MockIaasClient + iaasClient *stackitclientmock.MockIaaSClient mountMock *mount.MockIMount metadataMock *metadata.MockIMetadata FakeEndpoint string @@ -57,7 +57,7 @@ var _ = Describe("CSI sanity test", Ordered, func() { }) // --- Initialize Mocks --- - iaasClient = stackit.NewMockIaasClient(ctrl) + iaasClient = stackitclientmock.NewMockIaaSClient(ctrl) mountMock = mount.NewMockIMount(ctrl) metadataMock = metadata.NewMockIMetadata(ctrl) @@ -90,7 +90,7 @@ var _ = Describe("CSI sanity test", Ordered, func() { Id: new(uuid.New().String()), // Create a random ID Name: opts.Name, Size: size, - Status: new(stackit.VolumeAvailableStatus), + Status: new(stackitclient.VolumeAvailableStatus), AvailabilityZone: opts.AvailabilityZone, Source: opts.Source, } @@ -124,12 +124,12 @@ var _ = Describe("CSI sanity test", Ordered, func() { iaasClient.EXPECT().ListVolumes( gomock.Any(), gomock.Any(), gomock.Eq(""), - ).DoAndReturn(func(_ context.Context, _ int, _ string) ([]iaas.Volume, string, error) { + ).DoAndReturn(func(_ context.Context, _ int, _ string) ([]iaas.Volume, error) { var volList []iaas.Volume for _, vol := range createdVolumes { volList = append(volList, *vol) // Append the value } - return volList, "", nil + return volList, nil }).AnyTimes() iaasClient.EXPECT().DeleteVolume( @@ -161,23 +161,21 @@ var _ = Describe("CSI sanity test", Ordered, func() { iaasClient.EXPECT().CreateSnapshot( gomock.Any(), // context - gomock.Any(), // name - gomock.Any(), // volID - gomock.Any(), // tags - ).DoAndReturn(func(_ context.Context, name string, volID string, _ map[string]string) (*iaas.Snapshot, error) { + gomock.Any(), // payload + ).DoAndReturn(func(_ context.Context, payload *iaas.CreateSnapshotPayload) (*iaas.Snapshot, error) { newSnap := &iaas.Snapshot{ Id: new(uuid.New().String()), - Name: new(name), - Status: new(stackit.SnapshotReadyStatus), + Name: payload.Name, + Status: new(stackitclient.SnapshotReadyStatus), CreatedAt: new(time.Now()), Size: new(int64(10)), // 10 GiB - VolumeId: volID, + VolumeId: payload.VolumeId, } createdSnapshots[*newSnap.Id] = newSnap return newSnap, nil }).AnyTimes() - iaasClient.EXPECT().GetSnapshotByID( + iaasClient.EXPECT().GetSnapshot( gomock.Any(), // context gomock.Any(), // snapshotID ).DoAndReturn(func(_ context.Context, snapshotID string) (*iaas.Snapshot, error) { @@ -191,7 +189,7 @@ var _ = Describe("CSI sanity test", Ordered, func() { iaasClient.EXPECT().ListSnapshots( gomock.Any(), // context gomock.Any(), // filters - ).DoAndReturn(func(_ context.Context, filters map[string]string) ([]iaas.Snapshot, string, error) { + ).DoAndReturn(func(_ context.Context, filters map[string]string) ([]iaas.Snapshot, error) { var snapshots []iaas.Snapshot markerFilter := filters["Marker"] @@ -225,16 +223,14 @@ var _ = Describe("CSI sanity test", Ordered, func() { } } - retToken := "" if limitFilter != "" { limit, _ := strconv.Atoi(limitFilter) if limit > 0 && limit <= len(snapshots) { snapshots = snapshots[:limit] } - retToken = limitFilter } - return snapshots, retToken, nil + return snapshots, nil }).AnyTimes() iaasClient.EXPECT().DeleteSnapshot( @@ -249,7 +245,7 @@ var _ = Describe("CSI sanity test", Ordered, func() { gomock.Any(), // context gomock.Any(), // snapshotID ).Return( - new(string(stackit.SnapshotReadyStatus)), + new(stackitclient.SnapshotReadyStatus), nil, ).AnyTimes() @@ -274,7 +270,7 @@ var _ = Describe("CSI sanity test", Ordered, func() { return newBackup, nil }).AnyTimes() - iaasClient.EXPECT().GetBackupByID( + iaasClient.EXPECT().GetBackup( gomock.Any(), // context gomock.Any(), // backupID ).DoAndReturn(func(_ context.Context, backupID string) (*iaas.Backup, error) { @@ -306,7 +302,7 @@ var _ = Describe("CSI sanity test", Ordered, func() { // --- 4. Mock IaaS Client (Instances & Attach/Detach) --- - iaasClient.EXPECT().GetInstanceByID( + iaasClient.EXPECT().GetServer( gomock.Any(), // context gomock.Any(), // instanceID ).DoAndReturn(func(_ context.Context, instanceID string) (*iaas.Server, error) { @@ -324,7 +320,8 @@ var _ = Describe("CSI sanity test", Ordered, func() { gomock.Any(), // context gomock.Any(), // instanceID gomock.Any(), // volumeID - ).DoAndReturn(func(_ context.Context, instanceID string, volumeID string) (string, error) { + gomock.Any(), // payload + ).DoAndReturn(func(_ context.Context, instanceID string, volumeID string, _ iaas.AddVolumeToServerPayload) (string, error) { vol, ok := createdVolumes[volumeID] if !ok { return "", &oapierror.GenericOpenAPIError{StatusCode: http.StatusNotFound} diff --git a/pkg/csi/blockstorage/utils.go b/pkg/csi/blockstorage/utils.go index aaafc864..9e385c24 100644 --- a/pkg/csi/blockstorage/utils.go +++ b/pkg/csi/blockstorage/utils.go @@ -7,7 +7,7 @@ import ( "sync/atomic" "github.com/stackitcloud/cloud-provider-stackit/pkg/csi/util/mount" - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit" + stackitclient "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client" stackitconfig "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/config" "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/metadata" @@ -44,7 +44,7 @@ func NewVolumeCapabilityAccessMode(mode csi.VolumeCapability_AccessMode_Mode) *c } //revive:disable:unexported-return -func NewControllerServer(d *Driver, instance stackit.IaasClient) *controllerServer { +func NewControllerServer(d *Driver, instance stackitclient.IaaSClient) *controllerServer { return &controllerServer{ Driver: d, Instance: instance, diff --git a/pkg/stackit/backups.go b/pkg/stackit/backups.go deleted file mode 100644 index 84294519..00000000 --- a/pkg/stackit/backups.go +++ /dev/null @@ -1,182 +0,0 @@ -package stackit - -import ( - "context" - "errors" - "fmt" - "net/http" - "time" - - "github.com/stackitcloud/stackit-sdk-go/core/runtime" - iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" - wait "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api/wait" - - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/stackiterrors" -) - -const ( - backupReadyStatus = "AVAILABLE" - backupErrorStatus = "error" - backupDescription = "Created by STACKIT CSI driver" - BackupMaxDurationSecondsPerGBDefault = 20 - BackupMaxDurationPerGB = "backup-max-duration-seconds-per-gb" - backupBaseDurationSeconds = 30 - backupReadyCheckIntervalSeconds = 7 -) - -func (os *iaasClient) CreateBackup(ctx context.Context, name, volID, snapshotID string, tags map[string]string) (*iaas.Backup, error) { - opts, err := buildCreateBackupPayload(name, volID, snapshotID, tags) - if err != nil { - return nil, err - } - - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - backup, err := os.iaas.CreateBackup(ctxWithHTTPResp, os.projectID, os.region).CreateBackupPayload(opts).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(wait.XRequestIDHeader) - return nil, stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return nil, err - } - - return backup, nil -} - -func buildCreateBackupPayload(name, volID, snapshotID string, tags map[string]string) (iaas.CreateBackupPayload, error) { - if name == "" { - return iaas.CreateBackupPayload{}, errors.New("backup name cannot be empty") - } - - if volID == "" && snapshotID == "" { - return iaas.CreateBackupPayload{}, errors.New("either volID or snapshotID must be provided") - } - - var backupSource VolumeSourceTypes - var backupSourceID string - if volID != "" { - backupSource = VolumeSource - backupSourceID = volID - } - if snapshotID != "" { - backupSource = SnapshotSource - backupSourceID = snapshotID - } - - opts := iaas.CreateBackupPayload{ - Name: new(name), - Description: new(backupDescription), - Source: iaas.BackupSource{ - Type: string(backupSource), - Id: backupSourceID, - }, - } - if tags != nil { - opts.Labels = labelsFromTags(tags) - } - - return opts, nil -} - -func (os *iaasClient) ListBackups(ctx context.Context, filters map[string]string) ([]iaas.Backup, error) { - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - // TODO: Add API filter once available. - backups, err := os.iaas.ListBackups(ctxWithHTTPResp, os.projectID, os.region).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(wait.XRequestIDHeader) - return nil, stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return nil, err - } - - filteredBackups := filterBackups(backups.Items, filters) - return filteredBackups, nil -} - -func (os *iaasClient) DeleteBackup(ctx context.Context, backupID string) error { - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - err := os.iaas.DeleteBackup(ctxWithHTTPResp, os.projectID, backupID, os.region).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(wait.XRequestIDHeader) - return stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return err - } - return nil -} - -func (os *iaasClient) GetBackupByID(ctx context.Context, backupID string) (*iaas.Backup, error) { - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - backup, err := os.iaas.GetBackup(ctxWithHTTPResp, os.projectID, os.region, backupID).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(wait.XRequestIDHeader) - return nil, stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return nil, err - } - return backup, nil -} - -func (os *iaasClient) WaitBackupReady(ctx context.Context, backupID string, snapshotSize int64, backupMaxDurationSecondsPerGB int) (*string, error) { - var err error - - duration := time.Duration(int64(backupMaxDurationSecondsPerGB)*snapshotSize + backupBaseDurationSeconds) - err = os.waitBackupReadyWithContext(backupID, duration) - if errors.Is(err, context.DeadlineExceeded) { - err = fmt.Errorf("timeout, Backup %s is still not Ready: %v", backupID, err) - } - - back, _ := os.GetBackupByID(ctx, backupID) - - if back != nil { - return back.Status, err - } - return new("Failed to get backup status"), err -} - -func (os *iaasClient) waitBackupReadyWithContext(backupID string, duration time.Duration) error { - ctx, cancel := context.WithTimeout(context.Background(), duration*time.Second) - defer cancel() - var done bool - var err error - ticker := time.NewTicker(backupReadyCheckIntervalSeconds * time.Second) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - done, err = os.backupIsReady(ctx, backupID) - if err != nil { - return err - } - - if done { - return nil - } - case <-ctx.Done(): - return ctx.Err() - } - } -} - -// Supporting function for waitBackupReadyWithContext(). -// Returns true when the backup is ready. -func (os *iaasClient) backupIsReady(ctx context.Context, backupID string) (bool, error) { - backup, err := os.GetBackupByID(ctx, backupID) - if err != nil { - return false, err - } - - if *backup.Status == backupErrorStatus { - return false, errors.New("backup is in error state") - } - - return *backup.Status == backupReadyStatus, nil -} diff --git a/pkg/stackit/backups_test.go b/pkg/stackit/backups_test.go deleted file mode 100644 index 7c110c43..00000000 --- a/pkg/stackit/backups_test.go +++ /dev/null @@ -1,187 +0,0 @@ -package stackit - -import ( - "context" - "fmt" - "os" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - stackitconfig "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/config" - iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" - "go.uber.org/mock/gomock" - - mock "github.com/stackitcloud/cloud-provider-stackit/pkg/mock/iaas" -) - -var _ = Describe("Backup", func() { - var ( - err error - mockCtrl *gomock.Controller - mockAPI *mock.MockDefaultAPI - openStack IaasClient - config *stackitconfig.CSIConfig - ) - - const projectID = "project-id" - const region = "eu01" - - BeforeEach(func() { - t := GinkgoT() - mockCtrl = gomock.NewController(t) - mockAPI = mock.NewMockDefaultAPI(mockCtrl) - t.Setenv("STACKIT_REGION", region) - Expect(os.Getenv("STACKIT_REGION")).To(Equal(region)) - }) - - Context("buildCreateBackupPayload", func() { - DescribeTable("successful payload variants", - func( - name, volID, snapshotID string, - tags map[string]string, - expectedPayload iaas.CreateBackupPayload, - ) { - actualPayload, err := buildCreateBackupPayload(name, volID, snapshotID, tags) - Expect(err).ToNot(HaveOccurred()) - Expect(actualPayload).To(Equal(expectedPayload)) - }, - Entry( - "with volume source and nil tags", - "expected-name", "volume-id", "", nil, - iaas.CreateBackupPayload{ - Name: new("expected-name"), - Description: new(backupDescription), - Source: iaas.BackupSource{ - Type: "volume", - Id: "volume-id", - }, - Labels: nil, - }, - ), - Entry( - "with snapshot source and special characters in tags", - "expected-name", "", "snapshot-id", - map[string]string{ - "special": "tag with spaces and !@#$%^&*()", - "normal": "value", - }, - iaas.CreateBackupPayload{ - Name: new("expected-name"), - Description: new(backupDescription), - Source: iaas.BackupSource{ - Type: "snapshot", - Id: "snapshot-id", - }, - Labels: map[string]any{ - "special": "tag with spaces and !@#$%^&*()", - "normal": "value", - }, - }, - ), - Entry( - "with empty tags map", - "expected-name", "volume-id", "", map[string]string{}, - iaas.CreateBackupPayload{ - Name: new("expected-name"), - Description: new(backupDescription), - Source: iaas.BackupSource{ - Type: "volume", - Id: "volume-id", - }, - Labels: map[string]any{}, - }, - ), - Entry( - "with long backup name", - "very-long-backup-name-"+string(make([]byte, 200)), "volume-id", "", nil, - iaas.CreateBackupPayload{ - Name: new("very-long-backup-name-" + string(make([]byte, 200))), - Description: new(backupDescription), - Source: iaas.BackupSource{ - Type: "volume", - Id: "volume-id", - }, - }, - ), - Entry( - "when both volume and snapshot are provided snapshot wins", - "expected-name", "volume-id", "snapshot-id", nil, - iaas.CreateBackupPayload{ - Name: new("expected-name"), - Description: new(backupDescription), - Source: iaas.BackupSource{ - Type: "snapshot", - Id: "snapshot-id", - }, - }, - ), - ) - - DescribeTable("validation error variants", - func(name, volID, snapshotID, expectedError string) { - actualPayload, err := buildCreateBackupPayload(name, volID, snapshotID, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal(expectedError)) - Expect(actualPayload).To(Equal(iaas.CreateBackupPayload{})) - }, - Entry("empty name", "", "volume-id", "", "backup name cannot be empty"), - Entry("missing volume and snapshot IDs", "expected-name", "", "", "either volID or snapshotID must be provided"), - ) - }) - - Context("CreateBackup", func() { - BeforeEach(func() { - config = &stackitconfig.CSIConfig{ - Global: stackitconfig.GlobalOpts{ - ProjectID: projectID, - }, - } - openStack, err = CreateSTACKITProvider(mockAPI, config) - Expect(err).ToNot(HaveOccurred()) - }) - - It("returns backup on successful API call and uses configured project and region", func() { - mockCreateBackup(mockAPI) - - backup, err := openStack.CreateBackup(context.Background(), "expected-name", "volume-id", "", nil) - Expect(err).ToNot(HaveOccurred()) - Expect(backup).ToNot(BeNil()) - Expect(backup.Id).ToNot(BeNil()) - Expect(*backup.Id).To(Equal("expected backup")) - }) - - It("returns error when API fails", func() { - mockAPI.EXPECT().CreateBackup(gomock.Any(), projectID, region).Return(iaas.ApiCreateBackupRequest{ApiService: mockAPI}) - mockAPI.EXPECT().CreateBackupExecute(gomock.Any()).Return(nil, fmt.Errorf("API error")) - - backup, err := openStack.CreateBackup(context.Background(), "expected-name", "volume-id", "", nil) - Expect(err).To(HaveOccurred()) - Expect(backup).To(BeNil()) - }) - - DescribeTable("returns error when payload is invalid", - func(name, volID, snapshotID, expectedError string) { - mockAPI.EXPECT().CreateBackup(gomock.Any(), gomock.Any(), gomock.Any()).Times(0) - mockAPI.EXPECT().CreateBackupExecute(gomock.Any()).Times(0) - - backup, err := openStack.CreateBackup(context.Background(), name, volID, snapshotID, nil) - Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(Equal(expectedError)) - Expect(backup).To(BeNil()) - }, - Entry("empty name", "", "volume-id", "", "backup name cannot be empty"), - Entry("missing volume and snapshot IDs", "expected-name", "", "", "either volID or snapshotID must be provided"), - ) - }) - -}) - -func mockCreateBackup(mockAPI *mock.MockDefaultAPI) { - const ( - projectID = "project-id" - region = "eu01" - ) - - mockAPI.EXPECT().CreateBackup(gomock.Any(), projectID, region).Return(iaas.ApiCreateBackupRequest{ApiService: mockAPI}) - mockAPI.EXPECT().CreateBackupExecute(gomock.Any()).Return(&iaas.Backup{Id: new("expected backup")}, nil) -} diff --git a/pkg/stackit/client.go b/pkg/stackit/client.go deleted file mode 100644 index 12960832..00000000 --- a/pkg/stackit/client.go +++ /dev/null @@ -1,218 +0,0 @@ -/* -Copyright 2017 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package stackit - -import ( - "context" - "errors" - "fmt" - "io" - "net/http" - "os" - "strings" - - stackitconfig "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/config" - sdkconfig "github.com/stackitcloud/stackit-sdk-go/core/config" - oapiError "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" - loadbalancer "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api" - "gopkg.in/yaml.v3" - "k8s.io/apimachinery/pkg/util/wait" - - "github.com/stackitcloud/cloud-provider-stackit/pkg/version" - - "github.com/spf13/pflag" - "k8s.io/klog/v2" -) - -// userAgentData is used to add extra information to the STACKIT SDK user-agent -var ( - userAgentData []string - ErrorNotFound = errors.New("not found") -) - -// AddExtraFlags is called by the main package to add component specific command line flags -func AddExtraFlags(fs *pflag.FlagSet) { - fs.StringArrayVar(&userAgentData, "user-agent", nil, "Extra data to add to STACKIT SDK user-agent. Use multiple times to add more than one component.") -} - -type IaasClient interface { - CreateVolume(context.Context, *iaas.CreateVolumePayload) (*iaas.Volume, error) - DeleteVolume(ctx context.Context, volumeID string) error - AttachVolume(ctx context.Context, instanceID, volumeID string) (string, error) - ListVolumes(ctx context.Context, limit int, startingToken string) ([]iaas.Volume, string, error) - WaitDiskAttached(ctx context.Context, instanceID string, volumeID string) error - DetachVolume(ctx context.Context, instanceID, volumeID string) error - WaitDiskDetached(ctx context.Context, instanceID string, volumeID string) error - WaitVolumeTargetStatus(ctx context.Context, volumeID string, tStatus []string) error - GetVolume(ctx context.Context, volumeID string) (*iaas.Volume, error) - GetVolumesByName(ctx context.Context, name string) ([]iaas.Volume, error) - GetVolumeByName(ctx context.Context, name string) (*iaas.Volume, error) - CreateSnapshot(ctx context.Context, name, volID string, tags map[string]string) (*iaas.Snapshot, error) - ListSnapshots(ctx context.Context, filters map[string]string) ([]iaas.Snapshot, string, error) - DeleteSnapshot(ctx context.Context, snapID string) error - GetSnapshotByID(ctx context.Context, snapshotID string) (*iaas.Snapshot, error) - WaitSnapshotReady(ctx context.Context, snapshotID string) (*string, error) - CreateBackup(ctx context.Context, name, volID, snapshotID string, tags map[string]string) (*iaas.Backup, error) - ListBackups(ctx context.Context, filters map[string]string) ([]iaas.Backup, error) - DeleteBackup(ctx context.Context, backupID string) error - GetBackupByID(ctx context.Context, backupID string) (*iaas.Backup, error) - WaitBackupReady(ctx context.Context, backupID string, snapshotSize int64, backupMaxDurationSecondsPerGB int) (*string, error) - GetInstanceByID(ctx context.Context, instanceID string) (*iaas.Server, error) - ExpandVolume(ctx context.Context, volumeID string, status string, size int64) error - GetBlockStorageOpts() stackitconfig.BlockStorageOpts - WaitVolumeTargetStatusWithCustomBackoff(ctx context.Context, volumeID string, targetStatus []string, backoff *wait.Backoff) error -} - -type LoadbalancerClient interface { - GetLoadBalancer(ctx context.Context, projectID string, name string) (*loadbalancer.LoadBalancer, error) - // DeleteLoadBalancer returns no error if the load balancer doesn't exist. - DeleteLoadBalancer(ctx context.Context, projectID string, name string) error - // CreateLoadBalancer returns ErrorNotFound if the project is not enabled. - CreateLoadBalancer(ctx context.Context, projectID string, loadbalancer *loadbalancer.CreateLoadBalancerPayload) (*loadbalancer.LoadBalancer, error) - UpdateLoadBalancer(ctx context.Context, projectID, name string, update *loadbalancer.UpdateLoadBalancerPayload) (*loadbalancer.LoadBalancer, error) - UpdateTargetPool(ctx context.Context, projectID string, name string, targetPoolName string, payload loadbalancer.UpdateTargetPoolPayload) error - CreateCredentials(ctx context.Context, projectID string, payload loadbalancer.CreateCredentialsPayload) (*loadbalancer.CreateCredentialsResponse, error) - ListCredentials(ctx context.Context, projectID string) (*loadbalancer.ListCredentialsResponse, error) - GetCredentials(ctx context.Context, projectID string, credentialRef string) (*loadbalancer.GetCredentialsResponse, error) - UpdateCredentials(ctx context.Context, projectID, credentialRef string, payload loadbalancer.UpdateCredentialsPayload) error - DeleteCredentials(ctx context.Context, projectID string, credentialRef string) error -} - -// NodeClient is the API client wrapper for the cloud-controller-manager's node-controller -type NodeClient interface { - GetServer(ctx context.Context, projectID, region, serverID string) (*iaas.Server, error) - DeleteServer(ctx context.Context, projectID, region, serverID string) error - CreateServer(ctx context.Context, projectID string, region string, create *iaas.CreateServerPayload) (*iaas.Server, error) - UpdateServer(ctx context.Context, projectID, region, serverID string, update *iaas.UpdateServerPayload) (*iaas.Server, error) - ListServers(ctx context.Context, projectID, region string) (*[]iaas.Server, error) -} - -type iaasClient struct { - iaas iaas.DefaultAPI - projectID string - region string - bsOpts stackitconfig.BlockStorageOpts -} - -type lbClient struct { - client loadbalancer.DefaultAPI - region string -} - -type nodeClient struct { - client *iaas.APIClient -} - -//nolint:gocritic // The openstack package currently shadows but will be renamed anyway. -func (os *iaasClient) GetBlockStorageOpts() stackitconfig.BlockStorageOpts { - return os.bsOpts -} - -func GetConfig(reader io.Reader) (stackitconfig.CSIConfig, error) { - var cfg stackitconfig.CSIConfig - - content, err := io.ReadAll(reader) - if err != nil { - klog.ErrorS(err, "Failed to read config content") - return cfg, err - } - - err = yaml.Unmarshal(content, &cfg) - if err != nil { - klog.ErrorS(err, "Failed to parse config as YAML") - return cfg, err - } - - return cfg, nil -} - -func GetConfigFromFile(path string) (stackitconfig.CSIConfig, error) { - var cfg stackitconfig.CSIConfig - - config, err := os.Open(path) - if err != nil { - klog.ErrorS(err, "Failed to open stackitconfig file", "path", path) - return cfg, err - } - defer config.Close() - - return GetConfig(config) -} - -// CreateSTACKITProvider creates STACKIT Instance -func CreateSTACKITProvider(client iaas.DefaultAPI, cfg *stackitconfig.CSIConfig) (IaasClient, error) { - region := os.Getenv("STACKIT_REGION") - if region == "" { - panic("STACKIT_REGION environment variable not set") - } - // Init iaasClient - instance := &iaasClient{ - iaas: client, - bsOpts: cfg.BlockStorage, - projectID: cfg.Global.ProjectID, - region: region, - } - - return instance, nil -} - -func CreateIaaSClient(cfg *stackitconfig.CSIConfig) (iaas.DefaultAPI, error) { - var userAgent []string - var opts []sdkconfig.ConfigurationOption - userAgent = append(userAgent, fmt.Sprintf("%s/%s", "block-storage-csi-driver", version.Version)) - for _, data := range userAgentData { - // Prepend userAgents - userAgent = append([]string{data}, userAgent...) - } - klog.V(4).Infof("Using user-agent: %s", userAgent) - - if cfg.Global.APIEndpoints.IaasAPI != "" { - opts = append(opts, sdkconfig.WithEndpoint(cfg.Global.APIEndpoints.IaasAPI)) - } - - opts = append(opts, sdkconfig.WithUserAgent(strings.Join(userAgent, " "))) - - client, err := iaas.NewAPIClient(opts...) - if err != nil { - return nil, err - } - return client.DefaultAPI, nil -} - -func NewLoadbalancerClient(cl loadbalancer.DefaultAPI, region string) (LoadbalancerClient, error) { - return &lbClient{ - client: cl, - region: region, - }, nil -} - -func NewNodeClient(cl *iaas.APIClient) (NodeClient, error) { - return &nodeClient{client: cl}, nil -} - -func isOpenAPINotFound(err error) bool { - apiErr := &oapiError.GenericOpenAPIError{} - if !errors.As(err, &apiErr) { - return false - } - return apiErr.StatusCode == http.StatusNotFound -} - -func IsNotFound(err error) bool { - return errors.Is(err, ErrorNotFound) -} diff --git a/pkg/stackit/client/factory.go b/pkg/stackit/client/factory.go new file mode 100644 index 00000000..77e2e3b2 --- /dev/null +++ b/pkg/stackit/client/factory.go @@ -0,0 +1,87 @@ +package client + +import ( + "errors" + "io" + "os" + + "github.com/spf13/pflag" + stackitconfig "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/config" + sdkconfig "github.com/stackitcloud/stackit-sdk-go/core/config" + "gopkg.in/yaml.v3" + "k8s.io/klog/v2" +) + +func AddExtraFlags(fs *pflag.FlagSet) { + fs.StringArrayVar(&userAgentData, "user-agent", nil, "Extra data to add to STACKIT SDK user-agent. Use multiple times to add more than one component.") +} + +const ( + UserAgent = "cloud-provider-stackit" +) + +// userAgentData is used to add extra information to the STACKIT SDK user-agent +var ( + userAgentData []string + ErrorNotFound = errors.New("not found") +) + +// Factory produces clients for various STACKIT services. +type Factory interface { + // LoadBalancing returns a STACKIT load balancing service client. + LoadBalancing(ptions []sdkconfig.ConfigurationOption) (LoadBalancingClient, error) + + // IaaS returns a STACKIT IaaS service client. + IaaS(options []sdkconfig.ConfigurationOption) (IaaSClient, error) +} + +type factory struct { + StackitRegion string + StackitProjectID string +} + +func New(region, projectID string) Factory { + return &factory{ + StackitRegion: region, + StackitProjectID: projectID, + } +} + +func (f factory) LoadBalancing(options []sdkconfig.ConfigurationOption) (LoadBalancingClient, error) { + return NewLoadBalancingClient(f.StackitRegion, f.StackitProjectID, options) +} + +func (f factory) IaaS(options []sdkconfig.ConfigurationOption) (IaaSClient, error) { + return NewIaaSClient(f.StackitRegion, f.StackitProjectID, options) +} + +func GetConfigFromFile(path string) (stackitconfig.CSIConfig, error) { + var cfg stackitconfig.CSIConfig + + config, err := os.Open(path) + if err != nil { + klog.ErrorS(err, "Failed to open stackitconfig file", "path", path) + return cfg, err + } + defer config.Close() + + return GetConfig(config) +} + +func GetConfig(reader io.Reader) (stackitconfig.CSIConfig, error) { + var cfg stackitconfig.CSIConfig + + content, err := io.ReadAll(reader) + if err != nil { + klog.ErrorS(err, "Failed to read config content") + return cfg, err + } + + err = yaml.Unmarshal(content, &cfg) + if err != nil { + klog.ErrorS(err, "Failed to parse config as YAML") + return cfg, err + } + + return cfg, nil +} diff --git a/pkg/stackit/client_test.go b/pkg/stackit/client/factory_test.go similarity index 99% rename from pkg/stackit/client_test.go rename to pkg/stackit/client/factory_test.go index 2fe6d1a3..5b095c70 100644 --- a/pkg/stackit/client_test.go +++ b/pkg/stackit/client/factory_test.go @@ -1,4 +1,4 @@ -package stackit +package client import ( "fmt" diff --git a/pkg/stackit/client/iaas.go b/pkg/stackit/client/iaas.go new file mode 100644 index 00000000..7a4cb4aa --- /dev/null +++ b/pkg/stackit/client/iaas.go @@ -0,0 +1,560 @@ +package client + +import ( + "context" + "errors" + "fmt" + "slices" + "time" + + "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/stackiterrors" + sdkconfig "github.com/stackitcloud/stackit-sdk-go/core/config" + iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/klog/v2" + "k8s.io/utils/ptr" +) + +type IaaSClient interface { + GetServer(ctx context.Context, serverID string) (*iaas.Server, error) + DeleteServer(ctx context.Context, serverID string) error + CreateServer(ctx context.Context, create *iaas.CreateServerPayload) (*iaas.Server, error) + UpdateServer(ctx context.Context, serverID string, update iaas.UpdateServerPayload) (*iaas.Server, error) + ListServers(ctx context.Context) (*[]iaas.Server, error) + + CreateSnapshot(ctx context.Context, payload *iaas.CreateSnapshotPayload) (*iaas.Snapshot, error) + ListSnapshots(ctx context.Context, filters map[string]string) ([]iaas.Snapshot, error) + DeleteSnapshot(ctx context.Context, snapshotID string) error + GetSnapshot(ctx context.Context, snapshotID string) (*iaas.Snapshot, error) + WaitSnapshotReady(ctx context.Context, snapshotID string) (*string, error) + + CreateBackup(ctx context.Context, name, volID, snapshotID string, tags map[string]string) (*iaas.Backup, error) + ListBackups(ctx context.Context, filters map[string]string) ([]iaas.Backup, error) + DeleteBackup(ctx context.Context, backupID string) error + GetBackup(ctx context.Context, backupID string) (*iaas.Backup, error) + WaitBackupReady(ctx context.Context, backupID string, snapshotSize int64, backupMaxDurationSecondsPerGB int) (*string, error) + + CreateVolume(ctx context.Context, payload *iaas.CreateVolumePayload) (*iaas.Volume, error) + DeleteVolume(ctx context.Context, volumeID string) error + AttachVolume(ctx context.Context, serverID, volumeID string, payload iaas.AddVolumeToServerPayload) (string, error) + DetachVolume(ctx context.Context, serverID, volumeID string) error + GetVolume(ctx context.Context, volumeID string) (*iaas.Volume, error) + GetVolumesByName(ctx context.Context, volName string) ([]iaas.Volume, error) + GetVolumeByName(ctx context.Context, name string) (*iaas.Volume, error) + ListVolumes(ctx context.Context, _ int, _ string) ([]iaas.Volume, error) + ExpandVolume(ctx context.Context, volumeID, volumeStatus string, payload iaas.ResizeVolumePayload) error + WaitVolumeTargetStatus(ctx context.Context, volumeID string, tStatus []string) error + WaitDiskAttached(ctx context.Context, instanceID, volumeID string) error + WaitDiskDetached(ctx context.Context, instanceID, volumeID string) error + WaitVolumeTargetStatusWithCustomBackoff(ctx context.Context, volumeID string, tStatus []string, backoff *wait.Backoff) error +} + +const ( + VolumeAvailableStatus = "AVAILABLE" + VolumeAttachedStatus = "ATTACHED" + operationFinishInitDelay = 1 * time.Second + operationFinishFactor = 1.1 + operationFinishSteps = 10 + diskAttachInitDelay = 1 * time.Second + diskAttachFactor = 1.2 + diskAttachSteps = 15 + diskDetachInitDelay = 1 * time.Second + diskDetachFactor = 1.2 + diskDetachSteps = 13 + VolumeDescription = "Created by STACKIT CSI driver" +) + +const ( + BackupDescription = "Created by STACKIT CSI driver" + backupReadyStatus = "AVAILABLE" + backupErrorStatus = "error" + BackupMaxDurationSecondsPerGBDefault = 20 + BackupMaxDurationPerGB = "backup-max-duration-seconds-per-gb" + backupBaseDurationSeconds = 30 + backupReadyCheckIntervalSeconds = 7 +) + +const ( + SnapshotReadyStatus = "AVAILABLE" + SnapshotType = "type" +) + +type VolumeSourceTypes string + +const ( + VolumeSource VolumeSourceTypes = "volume" + SnapshotSource VolumeSourceTypes = "snapshot" + BackupSource VolumeSourceTypes = "backup" +) + +var volumeErrorStates = [...]string{"ERROR", "ERROR_RESIZING", "ERROR_DELETING"} + +type iaasClient struct { + Client iaas.DefaultAPI + projectID string + region string +} + +func NewIaaSClient(region, projectID string, options []sdkconfig.ConfigurationOption) (IaaSClient, error) { + apiClient, err := iaas.NewAPIClient(options...) + if err != nil { + return nil, err + } + return &iaasClient{ + Client: apiClient.DefaultAPI, + projectID: projectID, + region: region, + }, nil +} + +func (i iaasClient) GetServer(ctx context.Context, serverID string) (*iaas.Server, error) { + server, err := i.Client.GetServer(ctx, i.projectID, i.region, serverID).Execute() + if isOpenAPINotFound(err) { + return nil, ErrorNotFound + } + + return server, nil +} + +func (i iaasClient) DeleteServer(ctx context.Context, serverID string) error { + return i.Client.DeleteServer(ctx, i.projectID, i.region, serverID).Execute() +} + +func (i iaasClient) CreateServer(ctx context.Context, create *iaas.CreateServerPayload) (*iaas.Server, error) { + server, err := i.Client.CreateServer(ctx, i.projectID, i.region).CreateServerPayload(*create).Execute() + if isOpenAPINotFound(err) { + return nil, ErrorNotFound + } + return server, err +} + +func (i iaasClient) UpdateServer(ctx context.Context, serverID string, update iaas.UpdateServerPayload) (*iaas.Server, error) { + return i.Client.UpdateServer(ctx, i.projectID, i.region, serverID).UpdateServerPayload(update).Execute() +} + +func (i iaasClient) ListServers(ctx context.Context) (*[]iaas.Server, error) { + resp, err := i.Client.ListServers(ctx, i.projectID, i.region).Details(true).Execute() + if err != nil { + return nil, err + } + return &resp.Items, nil +} + +func (i iaasClient) CreateSnapshot(ctx context.Context, payload *iaas.CreateSnapshotPayload) (*iaas.Snapshot, error) { + snapshot, err := i.Client.CreateSnapshot(ctx, i.projectID, i.region).CreateSnapshotPayload(*payload).Execute() + if err != nil { + return nil, err + } + + return snapshot, nil +} + +func (i iaasClient) ListSnapshots(ctx context.Context, filters map[string]string) ([]iaas.Snapshot, error) { + snaps, err := i.Client.ListSnapshotsInProject(ctx, i.projectID, i.region).Execute() + if err != nil { + return nil, err + } + + filteredSnapshots := FilterSnapshots(snaps.Items, filters) + + return filteredSnapshots, nil +} + +func (i iaasClient) DeleteSnapshot(ctx context.Context, snapshotID string) error { + return i.Client.DeleteSnapshot(ctx, i.projectID, i.region, snapshotID).Execute() +} + +func (i iaasClient) GetSnapshot(ctx context.Context, snapshotID string) (*iaas.Snapshot, error) { + snapshot, err := i.Client.GetSnapshot(ctx, i.projectID, i.region, snapshotID).Execute() + if err != nil { + return nil, err + } + + return snapshot, nil +} + +func (i iaasClient) WaitSnapshotReady(ctx context.Context, snapshotID string) (*string, error) { + snap, err := i.GetSnapshot(ctx, snapshotID) + if err != nil { + return nil, err + } + + if snap != nil { + return snap.Status, nil + } + + return new("Failed to get Snapshot status"), nil +} + +func (i iaasClient) CreateBackup(ctx context.Context, name, volID, snapshotID string, tags map[string]string) (*iaas.Backup, error) { + payload, err := BuildCreateBackupPayload(name, volID, snapshotID, tags) + if err != nil { + return nil, err + } + + backup, err := i.Client.CreateBackup(ctx, i.projectID, i.region).CreateBackupPayload(payload).Execute() + if err != nil { + return nil, err + } + + return backup, nil +} + +func BuildCreateBackupPayload(name, volID, snapshotID string, tags map[string]string) (iaas.CreateBackupPayload, error) { + if name == "" { + return iaas.CreateBackupPayload{}, errors.New("backup name cannot be empty") + } + + if volID == "" && snapshotID == "" { + return iaas.CreateBackupPayload{}, errors.New("either volID or snapshotID must be provided") + } + + var backupSource VolumeSourceTypes + var backupSourceID string + if volID != "" { + backupSource = VolumeSource + backupSourceID = volID + } + if snapshotID != "" { + backupSource = SnapshotSource + backupSourceID = snapshotID + } + + opts := iaas.CreateBackupPayload{ + Name: new(name), + Description: new(BackupDescription), + Source: iaas.BackupSource{ + Type: string(backupSource), + Id: backupSourceID, + }, + } + if tags != nil { + opts.Labels = LabelsFromTags(tags) + } + + return opts, nil +} + +func (i iaasClient) ListBackups(ctx context.Context, filters map[string]string) ([]iaas.Backup, error) { + backups, err := i.Client.ListBackups(ctx, i.projectID, i.region).Execute() + if err != nil { + return nil, err + } + + filteredBackups := FilterBackups(backups.Items, filters) + + return filteredBackups, nil +} + +func (i iaasClient) DeleteBackup(ctx context.Context, backupID string) error { + return i.Client.DeleteBackup(ctx, i.projectID, i.region, backupID).Execute() +} + +func (i iaasClient) GetBackup(ctx context.Context, backupID string) (*iaas.Backup, error) { + backup, err := i.Client.GetBackup(ctx, i.projectID, i.region, backupID).Execute() + if err != nil { + return nil, err + } + + return backup, nil +} + +func (i iaasClient) WaitBackupReady(ctx context.Context, backupID string, snapshotSize int64, backupMaxDurationSecondsPerGB int) (*string, error) { + duration := time.Duration(int64(backupMaxDurationSecondsPerGB)*snapshotSize + backupBaseDurationSeconds) + err := i.waitBackupReadyWithContext(backupID, duration) + if err != nil { + if errors.Is(err, context.DeadlineExceeded) { + return nil, fmt.Errorf("timeout, Backup %s is still not Ready: %w", backupID, err) + } + return nil, err + } + + backup, err := i.GetBackup(ctx, backupID) + if err != nil { + return nil, err + } + + if backup != nil { + return backup.Status, nil + } + + return new("Failed to get backup status"), nil +} + +func (i iaasClient) waitBackupReadyWithContext(backupID string, duration time.Duration) error { + ctx, cancel := context.WithTimeout(context.Background(), duration*time.Second) + defer cancel() + var done bool + var err error + ticker := time.NewTicker(backupReadyCheckIntervalSeconds * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + done, err = i.backupIsReady(ctx, backupID) + if err != nil { + return err + } + + if done { + return nil + } + case <-ctx.Done(): + return ctx.Err() + } + } +} + +// Supporting function for waitBackupReadyWithContext(). +// Returns true when the backup is ready. +func (i iaasClient) backupIsReady(ctx context.Context, backupID string) (bool, error) { + backup, err := i.GetBackup(ctx, backupID) + if err != nil { + return false, err + } + + if *backup.Status == backupErrorStatus { + return false, errors.New("backup is in error state") + } + + return *backup.Status == backupReadyStatus, nil +} + +func (i iaasClient) CreateVolume(ctx context.Context, payload *iaas.CreateVolumePayload) (*iaas.Volume, error) { + payload.Description = new(VolumeDescription) + volume, err := i.Client.CreateVolume(ctx, i.projectID, i.region).CreateVolumePayload(*payload).Execute() + if err != nil { + return nil, err + } + + return volume, nil +} + +func (i iaasClient) DeleteVolume(ctx context.Context, volumeID string) error { + used, err := i.diskIsUsed(ctx, volumeID) + if err != nil { + return err + } + if used { + return fmt.Errorf("cannot delete the volume %q, it's still attached to a node", volumeID) + } + + return i.Client.DeleteVolume(ctx, i.projectID, i.region, volumeID).Execute() +} + +func (i iaasClient) AttachVolume(ctx context.Context, serverID, volumeID string, payload iaas.AddVolumeToServerPayload) (string, error) { + volume, err := i.GetVolume(ctx, volumeID) + if err != nil { + return "", err + } + if volume.ServerId != nil && serverID == *volume.ServerId { + klog.V(4).Infof("Disk %s is already attached to instance %s", volumeID, serverID) + return *volume.Id, nil + } + payload.DeleteOnTermination = new(false) + + if _, err = i.Client.AddVolumeToServer(ctx, i.projectID, i.region, serverID, volumeID). + AddVolumeToServerPayload(payload). + Execute(); err != nil { + return "", nil + } + + return volume.GetId(), nil +} + +func (i iaasClient) GetVolume(ctx context.Context, volumeID string) (*iaas.Volume, error) { + volume, err := i.Client.GetVolume(ctx, i.projectID, i.region, volumeID).Execute() + if err != nil { + return nil, err + } + + return volume, nil +} + +func (i iaasClient) GetVolumesByName(ctx context.Context, volName string) ([]iaas.Volume, error) { + volumes, err := i.Client.ListVolumes(ctx, i.projectID, i.region).Execute() + if err != nil { + return nil, err + } + + filterMap := map[string]string{"Name": volName} + filteredVolumes := FilterVolumes(volumes.Items, filterMap) + + return filteredVolumes, nil +} + +// GetVolumeByName(ctx context.Context, name string) (*iaas.Volume, error) +func (i iaasClient) GetVolumeByName(ctx context.Context, name string) (*iaas.Volume, error) { + vols, err := i.GetVolumesByName(ctx, name) + if err != nil { + return nil, err + } + + if len(vols) == 0 { + return nil, stackiterrors.ErrNotFound + } + + if len(vols) > 1 { + return nil, fmt.Errorf("found %d volumes with name %q", len(vols), name) + } + + return &vols[0], nil +} + +func (i iaasClient) ListVolumes(ctx context.Context, _ int, _ string) ([]iaas.Volume, error) { + volumes, err := i.Client.ListVolumes(ctx, i.projectID, i.region).Execute() + if err != nil { + return nil, err + } + + return volumes.Items, nil +} + +// ExpandVolume(ctx context.Context, volumeID, volumeStatus string, newSize int64) error +func (i iaasClient) ExpandVolume(ctx context.Context, volumeID, volumeStatus string, payload iaas.ResizeVolumePayload) error { + switch volumeStatus { + case VolumeAttachedStatus, VolumeAvailableStatus: + return i.Client.ResizeVolume(ctx, i.projectID, i.region, volumeID).ResizeVolumePayload(payload).Execute() + default: + return fmt.Errorf("volume cannot be resized, when status is %s", volumeStatus) + } +} + +func (i iaasClient) WaitVolumeTargetStatus(ctx context.Context, volumeID string, tStatus []string) error { + backoff := wait.Backoff{ + Duration: operationFinishInitDelay, + Factor: operationFinishFactor, + Steps: operationFinishSteps, + } + + waitErr := wait.ExponentialBackoff(backoff, func() (bool, error) { + vol, err := i.GetVolume(ctx, volumeID) + if err != nil { + return false, err + } + if slices.Contains(tStatus, *vol.Status) { + return true, nil + } + for _, eState := range volumeErrorStates { + if *vol.Status == eState { + return false, fmt.Errorf("volume is in Error State : %s", ptr.Deref(vol.Status, "")) + } + } + return false, nil + }) + + if wait.Interrupted(waitErr) { + waitErr = fmt.Errorf("timeout on waiting for volume %s status to be in %v", volumeID, tStatus) + } + + return waitErr +} + +func (i iaasClient) WaitDiskAttached(ctx context.Context, instanceID, volumeID string) error { + backoff := wait.Backoff{ + Duration: diskAttachInitDelay, + Factor: diskAttachFactor, + Steps: diskAttachSteps, + } + + err := wait.ExponentialBackoff(backoff, func() (bool, error) { + attached, err := i.diskIsAttached(ctx, instanceID, volumeID) + if err != nil && !stackiterrors.IsNotFound(err) { + // if this is a race condition indicate the volume is deleted + // during sleep phase, ignore the error and return attach=false + return false, err + } + return attached, nil + }) + + if wait.Interrupted(err) { + err = fmt.Errorf("volume %q failed to be attached within the allowed time", volumeID) + } + + return err +} + +func (i iaasClient) WaitDiskDetached(ctx context.Context, instanceID, volumeID string) error { + backoff := wait.Backoff{ + Duration: diskDetachInitDelay, + Factor: diskDetachFactor, + Steps: diskDetachSteps, + } + + err := wait.ExponentialBackoff(backoff, func() (bool, error) { + attached, err := i.diskIsAttached(ctx, instanceID, volumeID) + if err != nil { + return false, err + } + return !attached, nil + }) + + if wait.Interrupted(err) { + err = fmt.Errorf("volume %q failed to detach within the allowed time", volumeID) + } + + return err +} + +func (i iaasClient) DetachVolume(ctx context.Context, serverID, volumeID string) error { + volume, err := i.GetVolume(ctx, volumeID) + if err != nil { + return err + } + + if *volume.Status != VolumeAvailableStatus { + return fmt.Errorf("can not detach volume %s, its status is %s", *volume.Name, *volume.Status) + } + + return i.Client.RemoveVolumeFromServer(ctx, i.projectID, i.region, serverID, volumeID).Execute() +} + +func (i iaasClient) WaitVolumeTargetStatusWithCustomBackoff(ctx context.Context, volumeID string, tStatus []string, backoff *wait.Backoff) error { + waitErr := wait.ExponentialBackoff(*backoff, func() (bool, error) { + vol, err := i.GetVolume(ctx, volumeID) + if err != nil { + return false, err + } + if slices.Contains(tStatus, *vol.Status) { + return true, nil + } + for _, eState := range volumeErrorStates { + if *vol.Status == eState { + return false, fmt.Errorf("volume is in error state: %s", *vol.Status) + } + } + return false, nil + }) + + if wait.Interrupted(waitErr) { + waitErr = fmt.Errorf("timeout on waiting for volume %s status to be in %v", volumeID, tStatus) + } + + return waitErr +} + +// diskIsAttached queries if a volume is attached to a compute instance +func (i iaasClient) diskIsAttached(ctx context.Context, instanceID, volumeID string) (bool, error) { + volume, err := i.GetVolume(ctx, volumeID) + if err != nil { + return false, err + } + + if volume.ServerId != nil && *volume.ServerId == instanceID { + return true, nil + } + return false, nil +} + +// diskIsUsed returns true whether a disk is attached to any node +func (i iaasClient) diskIsUsed(ctx context.Context, volumeID string) (bool, error) { + volume, err := i.GetVolume(ctx, volumeID) + if err != nil { + return false, err + } + + diskUsed := volume.ServerId != nil && *volume.ServerId != "" + + return diskUsed, nil +} diff --git a/pkg/stackit/client/iaas_test.go b/pkg/stackit/client/iaas_test.go new file mode 100644 index 00000000..b898e796 --- /dev/null +++ b/pkg/stackit/client/iaas_test.go @@ -0,0 +1,520 @@ +package client + +import ( + "context" + "fmt" + "net/http" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + oapiError "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" + "go.uber.org/mock/gomock" + + mock "github.com/stackitcloud/cloud-provider-stackit/pkg/mock/iaas" +) + +var _ = Describe("Server", func() { + var ( + mockCtrl *gomock.Controller + mockIaaSClient *mock.MockDefaultAPI + client *iaasClient + ) + + const ( + serverID = "server-uuid-123" + ) + + BeforeEach(func() { + mockCtrl = gomock.NewController(GinkgoT()) + mockIaaSClient = mock.NewMockDefaultAPI(mockCtrl) + + client = &iaasClient{ + Client: mockIaaSClient, + } + }) + + AfterEach(func() { + mockCtrl.Finish() + }) + + Context("GetServer", func() { + It("returns a server on success", func() { + mockIaaSClient.EXPECT(). + GetServer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiGetServerRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().GetServerExecute(gomock.Any()).Return(&iaas.Server{Id: new(serverID), Name: "my-server"}, nil) + + server, err := client.GetServer(context.Background(), "my-server") + Expect(err).ToNot(HaveOccurred()) + Expect(*server.Id).To(Equal(serverID)) + }) + + It("returns ErrorNotFound when API returns 404", func() { + mockIaaSClient.EXPECT(). + GetServer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiGetServerRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().GetServerExecute(gomock.Any()).Return(nil, &oapiError.GenericOpenAPIError{ + StatusCode: http.StatusNotFound, + }) + + _, err := client.GetServer(context.Background(), "my-server") + Expect(err).To(HaveOccurred()) + }) + }) + + Context("CreateServer", func() { + It("successfully creates a server", func() { + mockIaaSClient.EXPECT(). + CreateServer(gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiCreateServerRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().CreateServerExecute(gomock.Any()).Return(&iaas.Server{ + Id: new(serverID), Name: "new-server"}, nil) + + payload := &iaas.CreateServerPayload{Name: "new-server"} + + server, err := client.CreateServer(context.Background(), payload) + Expect(err).ToNot(HaveOccurred()) + Expect(*server.Id).To(Equal(serverID)) + }) + }) + + Context("DeleteServer", func() { + It("deletes the server successfully", func() { + mockIaaSClient.EXPECT(). + DeleteServer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiDeleteServerRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().DeleteServerExecute(gomock.Any()).Return(nil) + + err := client.DeleteServer(context.Background(), serverID) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + Context("ListServers", func() { + It("returns a list of servers with details", func() { + mockItems := []iaas.Server{ + {Id: new("id-1"), Name: "server-1"}, + } + + mockIaaSClient.EXPECT(). + ListServers(gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiListServersRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().ListServersExecute(gomock.Any()).Return(&iaas.ServerListResponse{Items: mockItems}, nil) + + resp, err := client.ListServers(context.Background()) + Expect(err).ToNot(HaveOccurred()) + items := *resp + Expect(items).To(HaveLen(1)) + Expect(*items[0].Id).To(Equal("id-1")) + }) + }) + + Context("UpdateServer", func() { + It("updates server properties", func() { + mockIaaSClient.EXPECT(). + UpdateServer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiUpdateServerRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().UpdateServerExecute(gomock.Any()).Return(&iaas.Server{Id: new(serverID), Name: "updated-name"}, nil) + + updatePayload := iaas.UpdateServerPayload{Name: new("updated-name")} + server, err := client.UpdateServer(context.Background(), serverID, updatePayload) + Expect(err).ToNot(HaveOccurred()) + Expect(server.Name).To(Equal("updated-name")) + }) + }) +}) + +var _ = Describe("Snapshot", func() { + var ( + mockCtrl *gomock.Controller + mockIaaSClient *mock.MockDefaultAPI + client *iaasClient + ) + + const ( + snapshotID = "snapshot-uuid-123" + ) + + BeforeEach(func() { + mockCtrl = gomock.NewController(GinkgoT()) + mockIaaSClient = mock.NewMockDefaultAPI(mockCtrl) + + client = &iaasClient{ + Client: mockIaaSClient, + } + }) + + AfterEach(func() { + mockCtrl.Finish() + }) + + Context("CreateSnapshot", func() { + It("successfully creates a snapshot", func() { + mockIaaSClient.EXPECT(). + CreateSnapshot(gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiCreateSnapshotRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().CreateSnapshotExecute(gomock.Any()).Return(&iaas.Snapshot{Id: new(snapshotID)}, nil) + + payload := &iaas.CreateSnapshotPayload{Name: new("new-snapshot")} + snapshot, err := client.CreateSnapshot(context.Background(), payload) + Expect(err).ToNot(HaveOccurred()) + Expect(*snapshot.Id).To(Equal(snapshotID)) + }) + }) + + Context("ListSnapshots", func() { + It("returns a filtered list of snapshots", func() { + mockItems := &iaas.SnapshotListResponse{ + Items: []iaas.Snapshot{ + {Id: new("id-1"), Name: new("snap-1")}, + {Id: new("id-2"), Name: new("snap-2")}, + }, + } + + mockIaaSClient.EXPECT(). + ListSnapshotsInProject(gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiListSnapshotsInProjectRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().ListSnapshotsInProjectExecute(gomock.Any()).Return(mockItems, nil) + + resp, err := client.ListSnapshots(context.Background(), nil) + Expect(err).ToNot(HaveOccurred()) + Expect(resp).To(HaveLen(2)) + }) + }) + + Context("GetSnapshot", func() { + It("returns a specific snapshot on success", func() { + mockIaaSClient.EXPECT(). + GetSnapshot(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiGetSnapshotRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().GetSnapshotExecute(gomock.Any()).Return(&iaas.Snapshot{Id: new(snapshotID), Status: new("AVAILABLE")}, nil) + + snapshot, err := client.GetSnapshot(context.Background(), snapshotID) + Expect(err).ToNot(HaveOccurred()) + Expect(*snapshot.Id).To(Equal(snapshotID)) + }) + }) + + Context("DeleteSnapshot", func() { + It("deletes the snapshot successfully", func() { + mockIaaSClient.EXPECT(). + DeleteSnapshot(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiDeleteSnapshotRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().DeleteSnapshotExecute(gomock.Any()).Return(nil) + + err := client.DeleteSnapshot(context.Background(), snapshotID) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + Context("WaitSnapshotReady", func() { + It("returns the current status of the snapshot", func() { + mockIaaSClient.EXPECT(). + GetSnapshot(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiGetSnapshotRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().GetSnapshotExecute(gomock.Any()).Return(&iaas.Snapshot{Id: new(snapshotID), Status: new("READY")}, nil) + + status, err := client.WaitSnapshotReady(context.Background(), snapshotID) + Expect(err).ToNot(HaveOccurred()) + Expect(*status).To(Equal("READY")) + }) + + It("returns an error if the snapshot retrieval fails", func() { + mockIaaSClient.EXPECT(). + GetSnapshot(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiGetSnapshotRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().GetSnapshotExecute(gomock.Any()).Return(nil, fmt.Errorf("api error")) + + status, err := client.WaitSnapshotReady(context.Background(), snapshotID) + Expect(err).To(HaveOccurred()) + Expect(status).To(BeNil()) + }) + }) +}) + +var _ = Describe("Backup", func() { + var ( + mockCtrl *gomock.Controller + mockIaaSClient *mock.MockDefaultAPI + client *iaasClient + ) + + BeforeEach(func() { + mockCtrl = gomock.NewController(GinkgoT()) + mockIaaSClient = mock.NewMockDefaultAPI(mockCtrl) + + client = &iaasClient{ + Client: mockIaaSClient, + } + }) + + AfterEach(func() { + mockCtrl.Finish() + }) + + Context("buildCreateBackupPayload", func() { + DescribeTable("successful payload variants", + func(name, volID, snapshotID string, tags map[string]string, expectedPayload iaas.CreateBackupPayload) { + actualPayload, err := BuildCreateBackupPayload(name, volID, snapshotID, tags) + Expect(err).ToNot(HaveOccurred()) + Expect(actualPayload).To(Equal(expectedPayload)) + }, + Entry("with volume source", "expected-name", "volume-id", "", nil, iaas.CreateBackupPayload{ + Name: new("expected-name"), + Description: new(BackupDescription), + Source: iaas.BackupSource{Type: "volume", Id: "volume-id"}, + }), + Entry("with snapshot source", "expected-name", "", "snapshot-id", nil, iaas.CreateBackupPayload{ + Name: new("expected-name"), + Description: new(BackupDescription), + Source: iaas.BackupSource{Type: "snapshot", Id: "snapshot-id"}, + }), + ) + }) + + Context("CreateBackup API Calls", func() { + It("returns backup on successful API call", func() { + mockIaaSClient.EXPECT(). + CreateBackup(gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiCreateBackupRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().CreateBackupExecute(gomock.Any()).Return(&iaas.Backup{Id: new("expected-backup-id")}, nil) + + backup, err := client.CreateBackup(context.Background(), "expected-name", "volume-id", "", nil) + + Expect(err).ToNot(HaveOccurred()) + Expect(*backup.Id).To(Equal("expected-backup-id")) + }) + + It("returns error when API fails", func() { + mockIaaSClient.EXPECT(). + CreateBackup(gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiCreateBackupRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().CreateBackupExecute(gomock.Any()).Return(nil, fmt.Errorf("API error")) + + _, err := client.CreateBackup(context.Background(), "expected-name", "volume-id", "", nil) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("API error")) + }) + }) + + Context("ListBackups", func() { + It("returns a filtered list of backups on success", func() { + mockBackups := &iaas.BackupListResponse{ + Items: []iaas.Backup{ + {Id: new("id-1"), Name: new("backup-1")}, + {Id: new("id-2"), Name: new("backup-2")}, + }, + } + + mockIaaSClient.EXPECT(). + ListBackups(gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiListBackupsRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().ListBackupsExecute(gomock.Any()).Return(mockBackups, nil) + + backups, err := client.ListBackups(context.Background(), nil) + + Expect(err).ToNot(HaveOccurred()) + Expect(backups).To(HaveLen(2)) + Expect(*backups[0].Id).To(Equal("id-1")) + }) + + It("returns error when list API fails", func() { + mockIaaSClient.EXPECT(). + ListBackups(gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiListBackupsRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().ListBackupsExecute(gomock.Any()).Return(nil, fmt.Errorf("execute error")) + + _, err := client.ListBackups(context.Background(), nil) + Expect(err).To(HaveOccurred()) + }) + }) + + Context("GetBackup", func() { + It("returns a specific backup", func() { + mockIaaSClient.EXPECT(). + GetBackup(gomock.Any(), gomock.Any(), gomock.Any(), "backup-id"). + Return(iaas.ApiGetBackupRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().GetBackupExecute(gomock.Any()).Return(&iaas.Backup{Id: new("backup-id")}, nil) + + backup, err := client.GetBackup(context.Background(), "backup-id") + Expect(err).ToNot(HaveOccurred()) + Expect(*backup.Id).To(Equal("backup-id")) + }) + }) + + Context("DeleteBackup", func() { + It("calls delete successfully", func() { + mockIaaSClient.EXPECT(). + DeleteBackup(gomock.Any(), gomock.Any(), gomock.Any(), "backup-id"). + Return(iaas.ApiDeleteBackupRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().DeleteBackupExecute(gomock.Any()).Return(nil) + + err := client.DeleteBackup(context.Background(), "backup-id") + Expect(err).ToNot(HaveOccurred()) + }) + + It("returns error if delete fails", func() { + mockIaaSClient.EXPECT(). + DeleteBackup(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiDeleteBackupRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().DeleteBackupExecute(gomock.Any()).Return(fmt.Errorf("delete failed")) + + err := client.DeleteBackup(context.Background(), "any-id") + Expect(err).To(HaveOccurred()) + }) + }) + + Context("WaitBackupReady", func() { + It("returns the backup status when it becomes ready", func() { + mockIaaSClient.EXPECT(). + GetBackup(gomock.Any(), gomock.Any(), gomock.Any(), "backup-id"). + Return(iaas.ApiGetBackupRequest{ApiService: mockIaaSClient}).AnyTimes() + mockIaaSClient.EXPECT().GetBackupExecute(gomock.Any()).Return(&iaas.Backup{Id: new("backup-id"), Status: new(backupReadyStatus)}, nil).AnyTimes() + + status, err := client.WaitBackupReady(context.Background(), "backup-id", 1, 1) + Expect(err).ToNot(HaveOccurred()) + Expect(*status).To(Equal(backupReadyStatus)) + }) + + It("returns error on timeout or wait failure", func() { + mockIaaSClient.EXPECT(). + GetBackup(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiGetBackupRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().GetBackupExecute(gomock.Any()).Return(nil, fmt.Errorf("timeout waiting for backup")) + + status, err := client.WaitBackupReady(context.Background(), "id", 1, 1) + Expect(err).To(HaveOccurred()) + Expect(status).To(BeNil()) + }) + }) +}) + +var _ = Describe("Volume", func() { + var ( + mockCtrl *gomock.Controller + mockIaaSClient *mock.MockDefaultAPI + client *iaasClient + ) + + const ( + projectID = "project-id" + region = "eu01" + volumeID = "vol-123" + serverID = "server-123" + ) + + BeforeEach(func() { + mockCtrl = gomock.NewController(GinkgoT()) + mockIaaSClient = mock.NewMockDefaultAPI(mockCtrl) + + client = &iaasClient{ + Client: mockIaaSClient, + } + }) + + AfterEach(func() { + mockCtrl.Finish() + }) + + Context("Volume Lifecycle", func() { + It("CreateVolume successfully calls the API", func() { + mockIaaSClient.EXPECT().CreateVolume(gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiCreateVolumeRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().CreateVolumeExecute(gomock.Any()).Return(&iaas.Volume{Id: new(volumeID)}, nil) + + vol, err := client.CreateVolume(context.Background(), &iaas.CreateVolumePayload{}) + Expect(err).ToNot(HaveOccurred()) + Expect(*vol.Id).To(Equal(volumeID)) + }) + + It("GetVolume returns a specific volume", func() { + mockIaaSClient.EXPECT().GetVolume(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiGetVolumeRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().GetVolumeExecute(gomock.Any()).Return(&iaas.Volume{Id: new(volumeID), Name: new("test-vol")}, nil) + + vol, err := client.GetVolume(context.Background(), volumeID) + Expect(err).ToNot(HaveOccurred()) + Expect(*vol.Name).To(Equal("test-vol")) + }) + + It("DeleteVolume fails if volume is still attached (diskIsUsed logic)", func() { + mockIaaSClient.EXPECT().GetVolume(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiGetVolumeRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().GetVolumeExecute(gomock.Any()).Return(&iaas.Volume{Id: new(volumeID), ServerId: new(serverID)}, nil) + + err := client.DeleteVolume(context.Background(), volumeID) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("still attached")) + }) + }) + + Context("Attach/Detach Volume", func() { + It("AttachVolume calls API when not already attached", func() { + mockIaaSClient.EXPECT().GetVolume(gomock.Any(), gomock.Any(), gomock.Any(), volumeID). + Return(iaas.ApiGetVolumeRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().GetVolumeExecute(gomock.Any()).Return(&iaas.Volume{Id: new(volumeID), ServerId: nil}, nil) + + mockIaaSClient.EXPECT().AddVolumeToServer(gomock.Any(), gomock.Any(), gomock.Any(), serverID, volumeID). + Return(iaas.ApiAddVolumeToServerRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().AddVolumeToServerExecute(gomock.Any()).Return( + &iaas.VolumeAttachment{VolumeId: new(volumeID), ServerId: new(serverID)}, nil) + + id, err := client.AttachVolume(context.Background(), serverID, volumeID, iaas.AddVolumeToServerPayload{}) + Expect(err).ToNot(HaveOccurred()) + Expect(id).To(Equal(volumeID)) + }) + + It("DetachVolume fails if status is not Available", func() { + mockIaaSClient.EXPECT().GetVolume(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiGetVolumeRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().GetVolumeExecute(gomock.Any()).Return(&iaas.Volume{Name: new("volume-1"), Id: new(volumeID), Status: new("CREATING")}, nil) + + err := client.DetachVolume(context.Background(), serverID, volumeID) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("its status is CREATING")) + }) + }) + + Context("ExpandVolume", func() { + It("successfully resizes an Available volume", func() { + mockIaaSClient.EXPECT(). + ResizeVolume(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiResizeVolumeRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().ResizeVolumeExecute(gomock.Any()).Return(nil) + + err := client.ExpandVolume(context.Background(), volumeID, VolumeAvailableStatus, iaas.ResizeVolumePayload{}) + Expect(err).ToNot(HaveOccurred()) + }) + + It("errors when volume is in a bad state for resize", func() { + err := client.ExpandVolume(context.Background(), volumeID, "ERROR", iaas.ResizeVolumePayload{}) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("cannot be resized")) + }) + }) + + Context("Waiting Logic", func() { + It("WaitVolumeTargetStatus returns nil when target status is reached", func() { + mockIaaSClient.EXPECT(). + GetVolume(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiGetVolumeRequest{ApiService: mockIaaSClient}) + mockIaaSClient.EXPECT().GetVolumeExecute(gomock.Any()).Return(&iaas.Volume{Id: new(volumeID), Status: new("available")}, nil) + + err := client.WaitVolumeTargetStatus(context.Background(), volumeID, []string{"available"}) + Expect(err).ToNot(HaveOccurred()) + }) + + It("WaitDiskAttached returns error on timeout", func() { + mockIaaSClient.EXPECT(). + GetVolume(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). + Return(iaas.ApiGetVolumeRequest{ + ApiService: mockIaaSClient, + }) + mockIaaSClient.EXPECT().GetVolumeExecute(gomock.Any()).Return(nil, fmt.Errorf("timeout")) + + err := client.WaitDiskAttached(context.Background(), serverID, volumeID) + Expect(err).To(HaveOccurred()) + }) + }) +}) diff --git a/pkg/stackit/labels.go b/pkg/stackit/client/labels.go similarity index 61% rename from pkg/stackit/labels.go rename to pkg/stackit/client/labels.go index 0948daac..695c20dc 100644 --- a/pkg/stackit/labels.go +++ b/pkg/stackit/client/labels.go @@ -1,8 +1,6 @@ -package stackit +package client -type labels map[string]any - -func labelsFromTags(tags map[string]string) labels { +func LabelsFromTags(tags map[string]string) map[string]any { // Create a new map of type map[string]any l := make(map[string]any, len(tags)) @@ -11,5 +9,5 @@ func labelsFromTags(tags map[string]string) labels { l[key] = value } - return labels(l) + return l } diff --git a/pkg/stackit/labels_test.go b/pkg/stackit/client/labels_test.go similarity index 84% rename from pkg/stackit/labels_test.go rename to pkg/stackit/client/labels_test.go index 00753051..926d4e52 100644 --- a/pkg/stackit/labels_test.go +++ b/pkg/stackit/client/labels_test.go @@ -1,4 +1,4 @@ -package stackit +package client import ( . "github.com/onsi/ginkgo/v2" @@ -13,7 +13,7 @@ var _ = Describe("Labels", func() { "key2": "value2", } - labels := labelsFromTags(tags) + labels := LabelsFromTags(tags) Expect(labels).To(HaveKeyWithValue("key1", "value1")) Expect(labels).To(HaveKeyWithValue("key2", "value2")) @@ -22,7 +22,7 @@ var _ = Describe("Labels", func() { It("should handle empty tags", func() { tags := map[string]string{} - labels := labelsFromTags(tags) + labels := LabelsFromTags(tags) Expect(labels).To(BeEmpty()) }) @@ -30,7 +30,7 @@ var _ = Describe("Labels", func() { It("should handle nil tags", func() { var tags map[string]string - labels := labelsFromTags(tags) + labels := LabelsFromTags(tags) Expect(labels).To(BeEmpty()) }) diff --git a/pkg/stackit/client/loadbalancer.go b/pkg/stackit/client/loadbalancer.go new file mode 100644 index 00000000..f3727fb2 --- /dev/null +++ b/pkg/stackit/client/loadbalancer.go @@ -0,0 +1,121 @@ +package client + +import ( + "context" + + sdkconfig "github.com/stackitcloud/stackit-sdk-go/core/config" + loadbalancer "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api" +) + +type LoadBalancingClient interface { + CreateLoadBalancer(ctx context.Context, payload *loadbalancer.CreateLoadBalancerPayload) (*loadbalancer.LoadBalancer, error) + ListLoadBalancers(ctx context.Context) ([]loadbalancer.LoadBalancer, error) + DeleteLoadBalancer(ctx context.Context, lbName string) error + GetLoadBalancer(ctx context.Context, id string) (*loadbalancer.LoadBalancer, error) + UpdateLoadBalancer(ctx context.Context, lbName string, updates *loadbalancer.UpdateLoadBalancerPayload) error + UpdateTargetPool(ctx context.Context, name, targetPoolName string, payload loadbalancer.UpdateTargetPoolPayload) error + + CreateCredentials(ctx context.Context, payload loadbalancer.CreateCredentialsPayload) (*loadbalancer.CreateCredentialsResponse, error) + GetCredentials(ctx context.Context, credentialsRef string) (*loadbalancer.GetCredentialsResponse, error) + ListCredentials(ctx context.Context) (*loadbalancer.ListCredentialsResponse, error) + UpdateCredentials(ctx context.Context, credentialsRef string, payload loadbalancer.UpdateCredentialsPayload) error + DeleteCredentials(ctx context.Context, credentialsRef string) error +} + +type loadBalancingClient struct { + Client loadbalancer.DefaultAPI + projectID string + region string +} + +func NewLoadBalancingClient(region, projectID string, options []sdkconfig.ConfigurationOption) (LoadBalancingClient, error) { + apiClient, err := loadbalancer.NewAPIClient(options...) + if err != nil { + return nil, err + } + return &loadBalancingClient{ + Client: apiClient.DefaultAPI, + projectID: projectID, + region: region, + }, nil +} + +func (l loadBalancingClient) CreateLoadBalancer(ctx context.Context, payload *loadbalancer.CreateLoadBalancerPayload) (*loadbalancer.LoadBalancer, error) { + lb, err := l.Client.CreateLoadBalancer(ctx, l.projectID, l.region).CreateLoadBalancerPayload(*payload).Execute() + if isOpenAPINotFound(err) { + return lb, ErrorNotFound + } + return lb, err +} + +func (l loadBalancingClient) ListLoadBalancers(ctx context.Context) ([]loadbalancer.LoadBalancer, error) { + lbResponse, err := l.Client.ListLoadBalancers(ctx, l.projectID, l.region).Execute() + if err != nil { + return nil, err + } + return lbResponse.GetLoadBalancers(), nil +} + +func (l loadBalancingClient) DeleteLoadBalancer(ctx context.Context, lbName string) error { + _, err := l.Client.DeleteLoadBalancer(ctx, l.projectID, l.region, lbName).Execute() + return err +} + +func (l loadBalancingClient) GetLoadBalancer(ctx context.Context, lbName string) (*loadbalancer.LoadBalancer, error) { + return l.Client.GetLoadBalancer(ctx, l.projectID, l.region, lbName).Execute() +} + +func (l loadBalancingClient) UpdateLoadBalancer(ctx context.Context, lbName string, updates *loadbalancer.UpdateLoadBalancerPayload) error { + if _, err := l.Client.UpdateLoadBalancer(ctx, l.projectID, l.region, lbName).UpdateLoadBalancerPayload(*updates).Execute(); err != nil { + return err + } + + return nil +} + +func (l loadBalancingClient) UpdateTargetPool(ctx context.Context, name, targetPoolName string, payload loadbalancer.UpdateTargetPoolPayload) error { + _, err := l.Client.UpdateTargetPool(ctx, l.projectID, l.region, name, targetPoolName).UpdateTargetPoolPayload(payload).Execute() + return err +} + +func (l loadBalancingClient) UpdateTargatPool(ctx context.Context, lbName, targetPoolName string, payload loadbalancer.UpdateTargetPoolPayload) error { + _, err := l.Client.UpdateTargetPool(ctx, l.projectID, l.region, lbName, targetPoolName).UpdateTargetPoolPayload(payload).Execute() + return err +} + +func (l loadBalancingClient) CreateCredentials(ctx context.Context, payload loadbalancer.CreateCredentialsPayload) (*loadbalancer.CreateCredentialsResponse, error) { + resp, err := l.Client.CreateCredentials(ctx, l.projectID, l.region).CreateCredentialsPayload(payload).Execute() + if err != nil { + return nil, err + } + + return resp, nil +} + +func (l loadBalancingClient) GetCredentials(ctx context.Context, credentialsRef string) (*loadbalancer.GetCredentialsResponse, error) { + resp, err := l.Client.GetCredentials(ctx, l.projectID, l.region, credentialsRef).Execute() + if err != nil { + return nil, err + } + + return resp, nil +} + +func (l loadBalancingClient) ListCredentials(ctx context.Context) (*loadbalancer.ListCredentialsResponse, error) { + resp, err := l.Client.ListCredentials(ctx, l.projectID, l.region).Execute() + if err != nil { + return nil, err + } + + return resp, nil +} + +func (l loadBalancingClient) UpdateCredentials(ctx context.Context, credentialsRef string, payload loadbalancer.UpdateCredentialsPayload) error { + _, err := l.Client.UpdateCredentials(ctx, l.projectID, l.region, credentialsRef).UpdateCredentialsPayload(payload).Execute() + return err +} + +func (l loadBalancingClient) DeleteCredentials(ctx context.Context, credentialsRef string) error { + _, err := l.Client.DeleteCredentials(ctx, l.projectID, l.region, credentialsRef).Execute() + return err +} diff --git a/pkg/stackit/client/loadbalancer_test.go b/pkg/stackit/client/loadbalancer_test.go new file mode 100644 index 00000000..20ff481f --- /dev/null +++ b/pkg/stackit/client/loadbalancer_test.go @@ -0,0 +1,149 @@ +package client + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + loadbalancer "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api" + "go.uber.org/mock/gomock" + + mock "github.com/stackitcloud/cloud-provider-stackit/pkg/mock/loadbalancer" +) + +var _ = Describe("LoadBalancer", func() { + var ( + mockCtrl *gomock.Controller + mockLBClient *mock.MockDefaultAPI + client *loadBalancingClient + ) + + const ( + lbName = "my-lb" + ) + + BeforeEach(func() { + mockCtrl = gomock.NewController(GinkgoT()) + mockLBClient = mock.NewMockDefaultAPI(mockCtrl) + + client = &loadBalancingClient{ + Client: mockLBClient, + } + }) + + AfterEach(func() { + mockCtrl.Finish() + }) + + Context("LoadBalancer Management", func() { + It("CreateLoadBalancer successfully calls the API", func() { + payload := &loadbalancer.CreateLoadBalancerPayload{Name: new(lbName)} + mockLBClient.EXPECT(). + CreateLoadBalancer(gomock.Any(), gomock.Any(), gomock.Any()). + Return(loadbalancer.ApiCreateLoadBalancerRequest{ApiService: mockLBClient}) + mockLBClient.EXPECT().CreateLoadBalancerExecute(gomock.Any()).Return(&loadbalancer.LoadBalancer{Name: new(lbName)}, nil) + + lb, err := client.CreateLoadBalancer(context.Background(), payload) + Expect(err).ToNot(HaveOccurred()) + Expect(*lb.Name).To(Equal(lbName)) + }) + + It("ListLoadBalancers returns a slice of load balancers", func() { + mockItems := &loadbalancer.ListLoadBalancersResponse{ + LoadBalancers: []loadbalancer.LoadBalancer{ + {Name: new("lb-1")}, + {Name: new("lb-2")}, + }, + } + mockLBClient.EXPECT(). + ListLoadBalancers(gomock.Any(), gomock.Any(), gomock.Any()). + Return(loadbalancer.ApiListLoadBalancersRequest{ApiService: mockLBClient}) + mockLBClient.EXPECT().ListLoadBalancersExecute(gomock.Any()).Return(mockItems, nil) + + lbs, err := client.ListLoadBalancers(context.Background()) + Expect(err).ToNot(HaveOccurred()) + Expect(lbs).To(HaveLen(2)) + Expect(*lbs[0].Name).To(Equal("lb-1")) + }) + + It("GetLoadBalancer returns a specific LB", func() { + mockLBClient.EXPECT(). + GetLoadBalancer(gomock.Any(), gomock.Any(), gomock.Any(), lbName). + Return(loadbalancer.ApiGetLoadBalancerRequest{ApiService: mockLBClient}) + mockLBClient.EXPECT().GetLoadBalancerExecute(gomock.Any()).Return(&loadbalancer.LoadBalancer{Name: new(lbName)}, nil) + + lb, err := client.GetLoadBalancer(context.Background(), lbName) + Expect(err).ToNot(HaveOccurred()) + Expect(*lb.Name).To(Equal(lbName)) + }) + + It("UpdateLoadBalancer calls API successfully", func() { + mockLBClient.EXPECT(). + UpdateLoadBalancer(gomock.Any(), gomock.Any(), gomock.Any(), lbName). + Return(loadbalancer.ApiUpdateLoadBalancerRequest{ApiService: mockLBClient}) + mockLBClient.EXPECT().UpdateLoadBalancerExecute(gomock.Any()).Return(nil, nil) + + err := client.UpdateLoadBalancer(context.Background(), lbName, &loadbalancer.UpdateLoadBalancerPayload{}) + Expect(err).ToNot(HaveOccurred()) + }) + + It("DeleteLoadBalancer calls API successfully", func() { + mockLBClient.EXPECT(). + DeleteLoadBalancer(gomock.Any(), gomock.Any(), gomock.Any(), lbName). + Return(loadbalancer.ApiDeleteLoadBalancerRequest{ApiService: mockLBClient}) + mockLBClient.EXPECT().DeleteLoadBalancerExecute(gomock.Any()).Return(nil, nil) + + err := client.DeleteLoadBalancer(context.Background(), lbName) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + Context("Target Pools", func() { + It("UpdateTargetPool calls API successfully", func() { + payload := loadbalancer.UpdateTargetPoolPayload{} + mockLBClient.EXPECT(). + UpdateTargetPool(gomock.Any(), gomock.Any(), gomock.Any(), lbName, "pool-1"). + Return(loadbalancer.ApiUpdateTargetPoolRequest{ApiService: mockLBClient}) + mockLBClient.EXPECT().UpdateTargetPoolExecute(gomock.Any()).Return(nil, nil) + + err := client.UpdateTargetPool(context.Background(), lbName, "pool-1", payload) + Expect(err).ToNot(HaveOccurred()) + }) + }) + + Context("Credentials", func() { + It("CreateCredentials returns response on success", func() { + mockLBClient.EXPECT(). + CreateCredentials(gomock.Any(), gomock.Any(), gomock.Any()). + Return(loadbalancer.ApiCreateCredentialsRequest{ApiService: mockLBClient}) + mockLBClient.EXPECT().CreateCredentialsExecute(gomock.Any()).Return(&loadbalancer.CreateCredentialsResponse{Credential: &loadbalancer.CredentialsResponse{ + DisplayName: new("cred-1"), + }}, nil) + + resp, err := client.CreateCredentials(context.Background(), loadbalancer.CreateCredentialsPayload{}) + Expect(err).ToNot(HaveOccurred()) + Expect(*resp.Credential.DisplayName).To(Equal("cred-1")) + }) + + It("ListCredentials returns all credentials", func() { + mockLBClient.EXPECT(). + ListCredentials(gomock.Any(), gomock.Any(), gomock.Any()). + Return(loadbalancer.ApiListCredentialsRequest{ApiService: mockLBClient}) + mockLBClient.EXPECT().ListCredentialsExecute(gomock.Any()).Return(&loadbalancer.ListCredentialsResponse{Credentials: []loadbalancer.CredentialsResponse{{DisplayName: new("cred-1")}}}, nil) + + resp, err := client.ListCredentials(context.Background()) + Expect(err).ToNot(HaveOccurred()) + Expect(resp.Credentials).To(HaveLen(1)) + }) + + It("DeleteCredentials calls API successfully", func() { + mockLBClient.EXPECT(). + DeleteCredentials(gomock.Any(), gomock.Any(), gomock.Any(), "cred-ref"). + Return(loadbalancer.ApiDeleteCredentialsRequest{ApiService: mockLBClient}) + mockLBClient.EXPECT().DeleteCredentialsExecute(gomock.Any()).Return(nil, nil) + + err := client.DeleteCredentials(context.Background(), "cred-ref") + Expect(err).ToNot(HaveOccurred()) + }) + }) +}) diff --git a/pkg/stackit/client/mock/iaas_mock.go b/pkg/stackit/client/mock/iaas_mock.go new file mode 100644 index 00000000..32cf3095 --- /dev/null +++ b/pkg/stackit/client/mock/iaas_mock.go @@ -0,0 +1,1125 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./pkg/stackit/client (interfaces: IaaSClient) +// +// Generated by this command: +// +// mockgen -destination ./pkg/stackit/client/mock/iaas_mock.go -typed -package client ./pkg/stackit/client IaaSClient +// + +// Package client is a generated GoMock package. +package client + +import ( + context "context" + reflect "reflect" + + v2api "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" + gomock "go.uber.org/mock/gomock" + wait "k8s.io/apimachinery/pkg/util/wait" +) + +// MockIaaSClient is a mock of IaaSClient interface. +type MockIaaSClient struct { + ctrl *gomock.Controller + recorder *MockIaaSClientMockRecorder + isgomock struct{} +} + +// MockIaaSClientMockRecorder is the mock recorder for MockIaaSClient. +type MockIaaSClientMockRecorder struct { + mock *MockIaaSClient +} + +// NewMockIaaSClient creates a new mock instance. +func NewMockIaaSClient(ctrl *gomock.Controller) *MockIaaSClient { + mock := &MockIaaSClient{ctrl: ctrl} + mock.recorder = &MockIaaSClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIaaSClient) EXPECT() *MockIaaSClientMockRecorder { + return m.recorder +} + +// AttachVolume mocks base method. +func (m *MockIaaSClient) AttachVolume(ctx context.Context, serverID, volumeID string, payload v2api.AddVolumeToServerPayload) (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AttachVolume", ctx, serverID, volumeID, payload) + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AttachVolume indicates an expected call of AttachVolume. +func (mr *MockIaaSClientMockRecorder) AttachVolume(ctx, serverID, volumeID, payload any) *MockIaaSClientAttachVolumeCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttachVolume", reflect.TypeOf((*MockIaaSClient)(nil).AttachVolume), ctx, serverID, volumeID, payload) + return &MockIaaSClientAttachVolumeCall{Call: call} +} + +// MockIaaSClientAttachVolumeCall wrap *gomock.Call +type MockIaaSClientAttachVolumeCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientAttachVolumeCall) Return(arg0 string, arg1 error) *MockIaaSClientAttachVolumeCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientAttachVolumeCall) Do(f func(context.Context, string, string, v2api.AddVolumeToServerPayload) (string, error)) *MockIaaSClientAttachVolumeCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientAttachVolumeCall) DoAndReturn(f func(context.Context, string, string, v2api.AddVolumeToServerPayload) (string, error)) *MockIaaSClientAttachVolumeCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// CreateBackup mocks base method. +func (m *MockIaaSClient) CreateBackup(ctx context.Context, name, volID, snapshotID string, tags map[string]string) (*v2api.Backup, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateBackup", ctx, name, volID, snapshotID, tags) + ret0, _ := ret[0].(*v2api.Backup) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateBackup indicates an expected call of CreateBackup. +func (mr *MockIaaSClientMockRecorder) CreateBackup(ctx, name, volID, snapshotID, tags any) *MockIaaSClientCreateBackupCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBackup", reflect.TypeOf((*MockIaaSClient)(nil).CreateBackup), ctx, name, volID, snapshotID, tags) + return &MockIaaSClientCreateBackupCall{Call: call} +} + +// MockIaaSClientCreateBackupCall wrap *gomock.Call +type MockIaaSClientCreateBackupCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientCreateBackupCall) Return(arg0 *v2api.Backup, arg1 error) *MockIaaSClientCreateBackupCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientCreateBackupCall) Do(f func(context.Context, string, string, string, map[string]string) (*v2api.Backup, error)) *MockIaaSClientCreateBackupCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientCreateBackupCall) DoAndReturn(f func(context.Context, string, string, string, map[string]string) (*v2api.Backup, error)) *MockIaaSClientCreateBackupCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// CreateServer mocks base method. +func (m *MockIaaSClient) CreateServer(ctx context.Context, create *v2api.CreateServerPayload) (*v2api.Server, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateServer", ctx, create) + ret0, _ := ret[0].(*v2api.Server) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateServer indicates an expected call of CreateServer. +func (mr *MockIaaSClientMockRecorder) CreateServer(ctx, create any) *MockIaaSClientCreateServerCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateServer", reflect.TypeOf((*MockIaaSClient)(nil).CreateServer), ctx, create) + return &MockIaaSClientCreateServerCall{Call: call} +} + +// MockIaaSClientCreateServerCall wrap *gomock.Call +type MockIaaSClientCreateServerCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientCreateServerCall) Return(arg0 *v2api.Server, arg1 error) *MockIaaSClientCreateServerCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientCreateServerCall) Do(f func(context.Context, *v2api.CreateServerPayload) (*v2api.Server, error)) *MockIaaSClientCreateServerCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientCreateServerCall) DoAndReturn(f func(context.Context, *v2api.CreateServerPayload) (*v2api.Server, error)) *MockIaaSClientCreateServerCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// CreateSnapshot mocks base method. +func (m *MockIaaSClient) CreateSnapshot(ctx context.Context, payload *v2api.CreateSnapshotPayload) (*v2api.Snapshot, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateSnapshot", ctx, payload) + ret0, _ := ret[0].(*v2api.Snapshot) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateSnapshot indicates an expected call of CreateSnapshot. +func (mr *MockIaaSClientMockRecorder) CreateSnapshot(ctx, payload any) *MockIaaSClientCreateSnapshotCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSnapshot", reflect.TypeOf((*MockIaaSClient)(nil).CreateSnapshot), ctx, payload) + return &MockIaaSClientCreateSnapshotCall{Call: call} +} + +// MockIaaSClientCreateSnapshotCall wrap *gomock.Call +type MockIaaSClientCreateSnapshotCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientCreateSnapshotCall) Return(arg0 *v2api.Snapshot, arg1 error) *MockIaaSClientCreateSnapshotCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientCreateSnapshotCall) Do(f func(context.Context, *v2api.CreateSnapshotPayload) (*v2api.Snapshot, error)) *MockIaaSClientCreateSnapshotCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientCreateSnapshotCall) DoAndReturn(f func(context.Context, *v2api.CreateSnapshotPayload) (*v2api.Snapshot, error)) *MockIaaSClientCreateSnapshotCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// CreateVolume mocks base method. +func (m *MockIaaSClient) CreateVolume(ctx context.Context, payload *v2api.CreateVolumePayload) (*v2api.Volume, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateVolume", ctx, payload) + ret0, _ := ret[0].(*v2api.Volume) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateVolume indicates an expected call of CreateVolume. +func (mr *MockIaaSClientMockRecorder) CreateVolume(ctx, payload any) *MockIaaSClientCreateVolumeCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateVolume", reflect.TypeOf((*MockIaaSClient)(nil).CreateVolume), ctx, payload) + return &MockIaaSClientCreateVolumeCall{Call: call} +} + +// MockIaaSClientCreateVolumeCall wrap *gomock.Call +type MockIaaSClientCreateVolumeCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientCreateVolumeCall) Return(arg0 *v2api.Volume, arg1 error) *MockIaaSClientCreateVolumeCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientCreateVolumeCall) Do(f func(context.Context, *v2api.CreateVolumePayload) (*v2api.Volume, error)) *MockIaaSClientCreateVolumeCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientCreateVolumeCall) DoAndReturn(f func(context.Context, *v2api.CreateVolumePayload) (*v2api.Volume, error)) *MockIaaSClientCreateVolumeCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// DeleteBackup mocks base method. +func (m *MockIaaSClient) DeleteBackup(ctx context.Context, backupID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteBackup", ctx, backupID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteBackup indicates an expected call of DeleteBackup. +func (mr *MockIaaSClientMockRecorder) DeleteBackup(ctx, backupID any) *MockIaaSClientDeleteBackupCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBackup", reflect.TypeOf((*MockIaaSClient)(nil).DeleteBackup), ctx, backupID) + return &MockIaaSClientDeleteBackupCall{Call: call} +} + +// MockIaaSClientDeleteBackupCall wrap *gomock.Call +type MockIaaSClientDeleteBackupCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientDeleteBackupCall) Return(arg0 error) *MockIaaSClientDeleteBackupCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientDeleteBackupCall) Do(f func(context.Context, string) error) *MockIaaSClientDeleteBackupCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientDeleteBackupCall) DoAndReturn(f func(context.Context, string) error) *MockIaaSClientDeleteBackupCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// DeleteServer mocks base method. +func (m *MockIaaSClient) DeleteServer(ctx context.Context, serverID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteServer", ctx, serverID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteServer indicates an expected call of DeleteServer. +func (mr *MockIaaSClientMockRecorder) DeleteServer(ctx, serverID any) *MockIaaSClientDeleteServerCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteServer", reflect.TypeOf((*MockIaaSClient)(nil).DeleteServer), ctx, serverID) + return &MockIaaSClientDeleteServerCall{Call: call} +} + +// MockIaaSClientDeleteServerCall wrap *gomock.Call +type MockIaaSClientDeleteServerCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientDeleteServerCall) Return(arg0 error) *MockIaaSClientDeleteServerCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientDeleteServerCall) Do(f func(context.Context, string) error) *MockIaaSClientDeleteServerCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientDeleteServerCall) DoAndReturn(f func(context.Context, string) error) *MockIaaSClientDeleteServerCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// DeleteSnapshot mocks base method. +func (m *MockIaaSClient) DeleteSnapshot(ctx context.Context, snapshotID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteSnapshot", ctx, snapshotID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteSnapshot indicates an expected call of DeleteSnapshot. +func (mr *MockIaaSClientMockRecorder) DeleteSnapshot(ctx, snapshotID any) *MockIaaSClientDeleteSnapshotCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSnapshot", reflect.TypeOf((*MockIaaSClient)(nil).DeleteSnapshot), ctx, snapshotID) + return &MockIaaSClientDeleteSnapshotCall{Call: call} +} + +// MockIaaSClientDeleteSnapshotCall wrap *gomock.Call +type MockIaaSClientDeleteSnapshotCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientDeleteSnapshotCall) Return(arg0 error) *MockIaaSClientDeleteSnapshotCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientDeleteSnapshotCall) Do(f func(context.Context, string) error) *MockIaaSClientDeleteSnapshotCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientDeleteSnapshotCall) DoAndReturn(f func(context.Context, string) error) *MockIaaSClientDeleteSnapshotCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// DeleteVolume mocks base method. +func (m *MockIaaSClient) DeleteVolume(ctx context.Context, volumeID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteVolume", ctx, volumeID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteVolume indicates an expected call of DeleteVolume. +func (mr *MockIaaSClientMockRecorder) DeleteVolume(ctx, volumeID any) *MockIaaSClientDeleteVolumeCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVolume", reflect.TypeOf((*MockIaaSClient)(nil).DeleteVolume), ctx, volumeID) + return &MockIaaSClientDeleteVolumeCall{Call: call} +} + +// MockIaaSClientDeleteVolumeCall wrap *gomock.Call +type MockIaaSClientDeleteVolumeCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientDeleteVolumeCall) Return(arg0 error) *MockIaaSClientDeleteVolumeCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientDeleteVolumeCall) Do(f func(context.Context, string) error) *MockIaaSClientDeleteVolumeCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientDeleteVolumeCall) DoAndReturn(f func(context.Context, string) error) *MockIaaSClientDeleteVolumeCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// DetachVolume mocks base method. +func (m *MockIaaSClient) DetachVolume(ctx context.Context, serverID, volumeID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DetachVolume", ctx, serverID, volumeID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DetachVolume indicates an expected call of DetachVolume. +func (mr *MockIaaSClientMockRecorder) DetachVolume(ctx, serverID, volumeID any) *MockIaaSClientDetachVolumeCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DetachVolume", reflect.TypeOf((*MockIaaSClient)(nil).DetachVolume), ctx, serverID, volumeID) + return &MockIaaSClientDetachVolumeCall{Call: call} +} + +// MockIaaSClientDetachVolumeCall wrap *gomock.Call +type MockIaaSClientDetachVolumeCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientDetachVolumeCall) Return(arg0 error) *MockIaaSClientDetachVolumeCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientDetachVolumeCall) Do(f func(context.Context, string, string) error) *MockIaaSClientDetachVolumeCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientDetachVolumeCall) DoAndReturn(f func(context.Context, string, string) error) *MockIaaSClientDetachVolumeCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ExpandVolume mocks base method. +func (m *MockIaaSClient) ExpandVolume(ctx context.Context, volumeID, volumeStatus string, payload v2api.ResizeVolumePayload) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExpandVolume", ctx, volumeID, volumeStatus, payload) + ret0, _ := ret[0].(error) + return ret0 +} + +// ExpandVolume indicates an expected call of ExpandVolume. +func (mr *MockIaaSClientMockRecorder) ExpandVolume(ctx, volumeID, volumeStatus, payload any) *MockIaaSClientExpandVolumeCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExpandVolume", reflect.TypeOf((*MockIaaSClient)(nil).ExpandVolume), ctx, volumeID, volumeStatus, payload) + return &MockIaaSClientExpandVolumeCall{Call: call} +} + +// MockIaaSClientExpandVolumeCall wrap *gomock.Call +type MockIaaSClientExpandVolumeCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientExpandVolumeCall) Return(arg0 error) *MockIaaSClientExpandVolumeCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientExpandVolumeCall) Do(f func(context.Context, string, string, v2api.ResizeVolumePayload) error) *MockIaaSClientExpandVolumeCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientExpandVolumeCall) DoAndReturn(f func(context.Context, string, string, v2api.ResizeVolumePayload) error) *MockIaaSClientExpandVolumeCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GetBackup mocks base method. +func (m *MockIaaSClient) GetBackup(ctx context.Context, backupID string) (*v2api.Backup, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetBackup", ctx, backupID) + ret0, _ := ret[0].(*v2api.Backup) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetBackup indicates an expected call of GetBackup. +func (mr *MockIaaSClientMockRecorder) GetBackup(ctx, backupID any) *MockIaaSClientGetBackupCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBackup", reflect.TypeOf((*MockIaaSClient)(nil).GetBackup), ctx, backupID) + return &MockIaaSClientGetBackupCall{Call: call} +} + +// MockIaaSClientGetBackupCall wrap *gomock.Call +type MockIaaSClientGetBackupCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientGetBackupCall) Return(arg0 *v2api.Backup, arg1 error) *MockIaaSClientGetBackupCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientGetBackupCall) Do(f func(context.Context, string) (*v2api.Backup, error)) *MockIaaSClientGetBackupCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientGetBackupCall) DoAndReturn(f func(context.Context, string) (*v2api.Backup, error)) *MockIaaSClientGetBackupCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GetServer mocks base method. +func (m *MockIaaSClient) GetServer(ctx context.Context, serverID string) (*v2api.Server, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetServer", ctx, serverID) + ret0, _ := ret[0].(*v2api.Server) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetServer indicates an expected call of GetServer. +func (mr *MockIaaSClientMockRecorder) GetServer(ctx, serverID any) *MockIaaSClientGetServerCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServer", reflect.TypeOf((*MockIaaSClient)(nil).GetServer), ctx, serverID) + return &MockIaaSClientGetServerCall{Call: call} +} + +// MockIaaSClientGetServerCall wrap *gomock.Call +type MockIaaSClientGetServerCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientGetServerCall) Return(arg0 *v2api.Server, arg1 error) *MockIaaSClientGetServerCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientGetServerCall) Do(f func(context.Context, string) (*v2api.Server, error)) *MockIaaSClientGetServerCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientGetServerCall) DoAndReturn(f func(context.Context, string) (*v2api.Server, error)) *MockIaaSClientGetServerCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GetSnapshot mocks base method. +func (m *MockIaaSClient) GetSnapshot(ctx context.Context, snapshotID string) (*v2api.Snapshot, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSnapshot", ctx, snapshotID) + ret0, _ := ret[0].(*v2api.Snapshot) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSnapshot indicates an expected call of GetSnapshot. +func (mr *MockIaaSClientMockRecorder) GetSnapshot(ctx, snapshotID any) *MockIaaSClientGetSnapshotCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapshot", reflect.TypeOf((*MockIaaSClient)(nil).GetSnapshot), ctx, snapshotID) + return &MockIaaSClientGetSnapshotCall{Call: call} +} + +// MockIaaSClientGetSnapshotCall wrap *gomock.Call +type MockIaaSClientGetSnapshotCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientGetSnapshotCall) Return(arg0 *v2api.Snapshot, arg1 error) *MockIaaSClientGetSnapshotCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientGetSnapshotCall) Do(f func(context.Context, string) (*v2api.Snapshot, error)) *MockIaaSClientGetSnapshotCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientGetSnapshotCall) DoAndReturn(f func(context.Context, string) (*v2api.Snapshot, error)) *MockIaaSClientGetSnapshotCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GetVolume mocks base method. +func (m *MockIaaSClient) GetVolume(ctx context.Context, volumeID string) (*v2api.Volume, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVolume", ctx, volumeID) + ret0, _ := ret[0].(*v2api.Volume) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetVolume indicates an expected call of GetVolume. +func (mr *MockIaaSClientMockRecorder) GetVolume(ctx, volumeID any) *MockIaaSClientGetVolumeCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolume", reflect.TypeOf((*MockIaaSClient)(nil).GetVolume), ctx, volumeID) + return &MockIaaSClientGetVolumeCall{Call: call} +} + +// MockIaaSClientGetVolumeCall wrap *gomock.Call +type MockIaaSClientGetVolumeCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientGetVolumeCall) Return(arg0 *v2api.Volume, arg1 error) *MockIaaSClientGetVolumeCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientGetVolumeCall) Do(f func(context.Context, string) (*v2api.Volume, error)) *MockIaaSClientGetVolumeCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientGetVolumeCall) DoAndReturn(f func(context.Context, string) (*v2api.Volume, error)) *MockIaaSClientGetVolumeCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GetVolumeByName mocks base method. +func (m *MockIaaSClient) GetVolumeByName(ctx context.Context, name string) (*v2api.Volume, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVolumeByName", ctx, name) + ret0, _ := ret[0].(*v2api.Volume) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetVolumeByName indicates an expected call of GetVolumeByName. +func (mr *MockIaaSClientMockRecorder) GetVolumeByName(ctx, name any) *MockIaaSClientGetVolumeByNameCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeByName", reflect.TypeOf((*MockIaaSClient)(nil).GetVolumeByName), ctx, name) + return &MockIaaSClientGetVolumeByNameCall{Call: call} +} + +// MockIaaSClientGetVolumeByNameCall wrap *gomock.Call +type MockIaaSClientGetVolumeByNameCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientGetVolumeByNameCall) Return(arg0 *v2api.Volume, arg1 error) *MockIaaSClientGetVolumeByNameCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientGetVolumeByNameCall) Do(f func(context.Context, string) (*v2api.Volume, error)) *MockIaaSClientGetVolumeByNameCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientGetVolumeByNameCall) DoAndReturn(f func(context.Context, string) (*v2api.Volume, error)) *MockIaaSClientGetVolumeByNameCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GetVolumesByName mocks base method. +func (m *MockIaaSClient) GetVolumesByName(ctx context.Context, volName string) ([]v2api.Volume, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVolumesByName", ctx, volName) + ret0, _ := ret[0].([]v2api.Volume) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetVolumesByName indicates an expected call of GetVolumesByName. +func (mr *MockIaaSClientMockRecorder) GetVolumesByName(ctx, volName any) *MockIaaSClientGetVolumesByNameCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumesByName", reflect.TypeOf((*MockIaaSClient)(nil).GetVolumesByName), ctx, volName) + return &MockIaaSClientGetVolumesByNameCall{Call: call} +} + +// MockIaaSClientGetVolumesByNameCall wrap *gomock.Call +type MockIaaSClientGetVolumesByNameCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientGetVolumesByNameCall) Return(arg0 []v2api.Volume, arg1 error) *MockIaaSClientGetVolumesByNameCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientGetVolumesByNameCall) Do(f func(context.Context, string) ([]v2api.Volume, error)) *MockIaaSClientGetVolumesByNameCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientGetVolumesByNameCall) DoAndReturn(f func(context.Context, string) ([]v2api.Volume, error)) *MockIaaSClientGetVolumesByNameCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ListBackups mocks base method. +func (m *MockIaaSClient) ListBackups(ctx context.Context, filters map[string]string) ([]v2api.Backup, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListBackups", ctx, filters) + ret0, _ := ret[0].([]v2api.Backup) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListBackups indicates an expected call of ListBackups. +func (mr *MockIaaSClientMockRecorder) ListBackups(ctx, filters any) *MockIaaSClientListBackupsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBackups", reflect.TypeOf((*MockIaaSClient)(nil).ListBackups), ctx, filters) + return &MockIaaSClientListBackupsCall{Call: call} +} + +// MockIaaSClientListBackupsCall wrap *gomock.Call +type MockIaaSClientListBackupsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientListBackupsCall) Return(arg0 []v2api.Backup, arg1 error) *MockIaaSClientListBackupsCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientListBackupsCall) Do(f func(context.Context, map[string]string) ([]v2api.Backup, error)) *MockIaaSClientListBackupsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientListBackupsCall) DoAndReturn(f func(context.Context, map[string]string) ([]v2api.Backup, error)) *MockIaaSClientListBackupsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ListServers mocks base method. +func (m *MockIaaSClient) ListServers(ctx context.Context) (*[]v2api.Server, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListServers", ctx) + ret0, _ := ret[0].(*[]v2api.Server) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListServers indicates an expected call of ListServers. +func (mr *MockIaaSClientMockRecorder) ListServers(ctx any) *MockIaaSClientListServersCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListServers", reflect.TypeOf((*MockIaaSClient)(nil).ListServers), ctx) + return &MockIaaSClientListServersCall{Call: call} +} + +// MockIaaSClientListServersCall wrap *gomock.Call +type MockIaaSClientListServersCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientListServersCall) Return(arg0 *[]v2api.Server, arg1 error) *MockIaaSClientListServersCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientListServersCall) Do(f func(context.Context) (*[]v2api.Server, error)) *MockIaaSClientListServersCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientListServersCall) DoAndReturn(f func(context.Context) (*[]v2api.Server, error)) *MockIaaSClientListServersCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ListSnapshots mocks base method. +func (m *MockIaaSClient) ListSnapshots(ctx context.Context, filters map[string]string) ([]v2api.Snapshot, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListSnapshots", ctx, filters) + ret0, _ := ret[0].([]v2api.Snapshot) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListSnapshots indicates an expected call of ListSnapshots. +func (mr *MockIaaSClientMockRecorder) ListSnapshots(ctx, filters any) *MockIaaSClientListSnapshotsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSnapshots", reflect.TypeOf((*MockIaaSClient)(nil).ListSnapshots), ctx, filters) + return &MockIaaSClientListSnapshotsCall{Call: call} +} + +// MockIaaSClientListSnapshotsCall wrap *gomock.Call +type MockIaaSClientListSnapshotsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientListSnapshotsCall) Return(arg0 []v2api.Snapshot, arg1 error) *MockIaaSClientListSnapshotsCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientListSnapshotsCall) Do(f func(context.Context, map[string]string) ([]v2api.Snapshot, error)) *MockIaaSClientListSnapshotsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientListSnapshotsCall) DoAndReturn(f func(context.Context, map[string]string) ([]v2api.Snapshot, error)) *MockIaaSClientListSnapshotsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ListVolumes mocks base method. +func (m *MockIaaSClient) ListVolumes(ctx context.Context, arg1 int, arg2 string) ([]v2api.Volume, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListVolumes", ctx, arg1, arg2) + ret0, _ := ret[0].([]v2api.Volume) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListVolumes indicates an expected call of ListVolumes. +func (mr *MockIaaSClientMockRecorder) ListVolumes(ctx, arg1, arg2 any) *MockIaaSClientListVolumesCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListVolumes", reflect.TypeOf((*MockIaaSClient)(nil).ListVolumes), ctx, arg1, arg2) + return &MockIaaSClientListVolumesCall{Call: call} +} + +// MockIaaSClientListVolumesCall wrap *gomock.Call +type MockIaaSClientListVolumesCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientListVolumesCall) Return(arg0 []v2api.Volume, arg1 error) *MockIaaSClientListVolumesCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientListVolumesCall) Do(f func(context.Context, int, string) ([]v2api.Volume, error)) *MockIaaSClientListVolumesCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientListVolumesCall) DoAndReturn(f func(context.Context, int, string) ([]v2api.Volume, error)) *MockIaaSClientListVolumesCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// UpdateServer mocks base method. +func (m *MockIaaSClient) UpdateServer(ctx context.Context, serverID string, update v2api.UpdateServerPayload) (*v2api.Server, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateServer", ctx, serverID, update) + ret0, _ := ret[0].(*v2api.Server) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdateServer indicates an expected call of UpdateServer. +func (mr *MockIaaSClientMockRecorder) UpdateServer(ctx, serverID, update any) *MockIaaSClientUpdateServerCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateServer", reflect.TypeOf((*MockIaaSClient)(nil).UpdateServer), ctx, serverID, update) + return &MockIaaSClientUpdateServerCall{Call: call} +} + +// MockIaaSClientUpdateServerCall wrap *gomock.Call +type MockIaaSClientUpdateServerCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientUpdateServerCall) Return(arg0 *v2api.Server, arg1 error) *MockIaaSClientUpdateServerCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientUpdateServerCall) Do(f func(context.Context, string, v2api.UpdateServerPayload) (*v2api.Server, error)) *MockIaaSClientUpdateServerCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientUpdateServerCall) DoAndReturn(f func(context.Context, string, v2api.UpdateServerPayload) (*v2api.Server, error)) *MockIaaSClientUpdateServerCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// WaitBackupReady mocks base method. +func (m *MockIaaSClient) WaitBackupReady(ctx context.Context, backupID string, snapshotSize int64, backupMaxDurationSecondsPerGB int) (*string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitBackupReady", ctx, backupID, snapshotSize, backupMaxDurationSecondsPerGB) + ret0, _ := ret[0].(*string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WaitBackupReady indicates an expected call of WaitBackupReady. +func (mr *MockIaaSClientMockRecorder) WaitBackupReady(ctx, backupID, snapshotSize, backupMaxDurationSecondsPerGB any) *MockIaaSClientWaitBackupReadyCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitBackupReady", reflect.TypeOf((*MockIaaSClient)(nil).WaitBackupReady), ctx, backupID, snapshotSize, backupMaxDurationSecondsPerGB) + return &MockIaaSClientWaitBackupReadyCall{Call: call} +} + +// MockIaaSClientWaitBackupReadyCall wrap *gomock.Call +type MockIaaSClientWaitBackupReadyCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientWaitBackupReadyCall) Return(arg0 *string, arg1 error) *MockIaaSClientWaitBackupReadyCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientWaitBackupReadyCall) Do(f func(context.Context, string, int64, int) (*string, error)) *MockIaaSClientWaitBackupReadyCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientWaitBackupReadyCall) DoAndReturn(f func(context.Context, string, int64, int) (*string, error)) *MockIaaSClientWaitBackupReadyCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// WaitDiskAttached mocks base method. +func (m *MockIaaSClient) WaitDiskAttached(ctx context.Context, instanceID, volumeID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitDiskAttached", ctx, instanceID, volumeID) + ret0, _ := ret[0].(error) + return ret0 +} + +// WaitDiskAttached indicates an expected call of WaitDiskAttached. +func (mr *MockIaaSClientMockRecorder) WaitDiskAttached(ctx, instanceID, volumeID any) *MockIaaSClientWaitDiskAttachedCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitDiskAttached", reflect.TypeOf((*MockIaaSClient)(nil).WaitDiskAttached), ctx, instanceID, volumeID) + return &MockIaaSClientWaitDiskAttachedCall{Call: call} +} + +// MockIaaSClientWaitDiskAttachedCall wrap *gomock.Call +type MockIaaSClientWaitDiskAttachedCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientWaitDiskAttachedCall) Return(arg0 error) *MockIaaSClientWaitDiskAttachedCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientWaitDiskAttachedCall) Do(f func(context.Context, string, string) error) *MockIaaSClientWaitDiskAttachedCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientWaitDiskAttachedCall) DoAndReturn(f func(context.Context, string, string) error) *MockIaaSClientWaitDiskAttachedCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// WaitDiskDetached mocks base method. +func (m *MockIaaSClient) WaitDiskDetached(ctx context.Context, instanceID, volumeID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitDiskDetached", ctx, instanceID, volumeID) + ret0, _ := ret[0].(error) + return ret0 +} + +// WaitDiskDetached indicates an expected call of WaitDiskDetached. +func (mr *MockIaaSClientMockRecorder) WaitDiskDetached(ctx, instanceID, volumeID any) *MockIaaSClientWaitDiskDetachedCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitDiskDetached", reflect.TypeOf((*MockIaaSClient)(nil).WaitDiskDetached), ctx, instanceID, volumeID) + return &MockIaaSClientWaitDiskDetachedCall{Call: call} +} + +// MockIaaSClientWaitDiskDetachedCall wrap *gomock.Call +type MockIaaSClientWaitDiskDetachedCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientWaitDiskDetachedCall) Return(arg0 error) *MockIaaSClientWaitDiskDetachedCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientWaitDiskDetachedCall) Do(f func(context.Context, string, string) error) *MockIaaSClientWaitDiskDetachedCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientWaitDiskDetachedCall) DoAndReturn(f func(context.Context, string, string) error) *MockIaaSClientWaitDiskDetachedCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// WaitSnapshotReady mocks base method. +func (m *MockIaaSClient) WaitSnapshotReady(ctx context.Context, snapshotID string) (*string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitSnapshotReady", ctx, snapshotID) + ret0, _ := ret[0].(*string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// WaitSnapshotReady indicates an expected call of WaitSnapshotReady. +func (mr *MockIaaSClientMockRecorder) WaitSnapshotReady(ctx, snapshotID any) *MockIaaSClientWaitSnapshotReadyCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitSnapshotReady", reflect.TypeOf((*MockIaaSClient)(nil).WaitSnapshotReady), ctx, snapshotID) + return &MockIaaSClientWaitSnapshotReadyCall{Call: call} +} + +// MockIaaSClientWaitSnapshotReadyCall wrap *gomock.Call +type MockIaaSClientWaitSnapshotReadyCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientWaitSnapshotReadyCall) Return(arg0 *string, arg1 error) *MockIaaSClientWaitSnapshotReadyCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientWaitSnapshotReadyCall) Do(f func(context.Context, string) (*string, error)) *MockIaaSClientWaitSnapshotReadyCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientWaitSnapshotReadyCall) DoAndReturn(f func(context.Context, string) (*string, error)) *MockIaaSClientWaitSnapshotReadyCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// WaitVolumeTargetStatus mocks base method. +func (m *MockIaaSClient) WaitVolumeTargetStatus(ctx context.Context, volumeID string, tStatus []string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitVolumeTargetStatus", ctx, volumeID, tStatus) + ret0, _ := ret[0].(error) + return ret0 +} + +// WaitVolumeTargetStatus indicates an expected call of WaitVolumeTargetStatus. +func (mr *MockIaaSClientMockRecorder) WaitVolumeTargetStatus(ctx, volumeID, tStatus any) *MockIaaSClientWaitVolumeTargetStatusCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitVolumeTargetStatus", reflect.TypeOf((*MockIaaSClient)(nil).WaitVolumeTargetStatus), ctx, volumeID, tStatus) + return &MockIaaSClientWaitVolumeTargetStatusCall{Call: call} +} + +// MockIaaSClientWaitVolumeTargetStatusCall wrap *gomock.Call +type MockIaaSClientWaitVolumeTargetStatusCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientWaitVolumeTargetStatusCall) Return(arg0 error) *MockIaaSClientWaitVolumeTargetStatusCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientWaitVolumeTargetStatusCall) Do(f func(context.Context, string, []string) error) *MockIaaSClientWaitVolumeTargetStatusCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientWaitVolumeTargetStatusCall) DoAndReturn(f func(context.Context, string, []string) error) *MockIaaSClientWaitVolumeTargetStatusCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// WaitVolumeTargetStatusWithCustomBackoff mocks base method. +func (m *MockIaaSClient) WaitVolumeTargetStatusWithCustomBackoff(ctx context.Context, volumeID string, tStatus []string, backoff *wait.Backoff) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WaitVolumeTargetStatusWithCustomBackoff", ctx, volumeID, tStatus, backoff) + ret0, _ := ret[0].(error) + return ret0 +} + +// WaitVolumeTargetStatusWithCustomBackoff indicates an expected call of WaitVolumeTargetStatusWithCustomBackoff. +func (mr *MockIaaSClientMockRecorder) WaitVolumeTargetStatusWithCustomBackoff(ctx, volumeID, tStatus, backoff any) *MockIaaSClientWaitVolumeTargetStatusWithCustomBackoffCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitVolumeTargetStatusWithCustomBackoff", reflect.TypeOf((*MockIaaSClient)(nil).WaitVolumeTargetStatusWithCustomBackoff), ctx, volumeID, tStatus, backoff) + return &MockIaaSClientWaitVolumeTargetStatusWithCustomBackoffCall{Call: call} +} + +// MockIaaSClientWaitVolumeTargetStatusWithCustomBackoffCall wrap *gomock.Call +type MockIaaSClientWaitVolumeTargetStatusWithCustomBackoffCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockIaaSClientWaitVolumeTargetStatusWithCustomBackoffCall) Return(arg0 error) *MockIaaSClientWaitVolumeTargetStatusWithCustomBackoffCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockIaaSClientWaitVolumeTargetStatusWithCustomBackoffCall) Do(f func(context.Context, string, []string, *wait.Backoff) error) *MockIaaSClientWaitVolumeTargetStatusWithCustomBackoffCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockIaaSClientWaitVolumeTargetStatusWithCustomBackoffCall) DoAndReturn(f func(context.Context, string, []string, *wait.Backoff) error) *MockIaaSClientWaitVolumeTargetStatusWithCustomBackoffCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/pkg/stackit/client/mock/loadbalancer_mock.go b/pkg/stackit/client/mock/loadbalancer_mock.go new file mode 100644 index 00000000..7598ba63 --- /dev/null +++ b/pkg/stackit/client/mock/loadbalancer_mock.go @@ -0,0 +1,466 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./pkg/stackit/client (interfaces: LoadBalancingClient) +// +// Generated by this command: +// +// mockgen -destination ./pkg/stackit/client/mock/loadbalancer_mock.go -typed -package client ./pkg/stackit/client LoadBalancingClient +// + +// Package client is a generated GoMock package. +package client + +import ( + context "context" + reflect "reflect" + + v2api "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api" + gomock "go.uber.org/mock/gomock" +) + +// MockLoadBalancingClient is a mock of LoadBalancingClient interface. +type MockLoadBalancingClient struct { + ctrl *gomock.Controller + recorder *MockLoadBalancingClientMockRecorder + isgomock struct{} +} + +// MockLoadBalancingClientMockRecorder is the mock recorder for MockLoadBalancingClient. +type MockLoadBalancingClientMockRecorder struct { + mock *MockLoadBalancingClient +} + +// NewMockLoadBalancingClient creates a new mock instance. +func NewMockLoadBalancingClient(ctrl *gomock.Controller) *MockLoadBalancingClient { + mock := &MockLoadBalancingClient{ctrl: ctrl} + mock.recorder = &MockLoadBalancingClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockLoadBalancingClient) EXPECT() *MockLoadBalancingClientMockRecorder { + return m.recorder +} + +// CreateCredentials mocks base method. +func (m *MockLoadBalancingClient) CreateCredentials(ctx context.Context, payload v2api.CreateCredentialsPayload) (*v2api.CreateCredentialsResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateCredentials", ctx, payload) + ret0, _ := ret[0].(*v2api.CreateCredentialsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateCredentials indicates an expected call of CreateCredentials. +func (mr *MockLoadBalancingClientMockRecorder) CreateCredentials(ctx, payload any) *MockLoadBalancingClientCreateCredentialsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCredentials", reflect.TypeOf((*MockLoadBalancingClient)(nil).CreateCredentials), ctx, payload) + return &MockLoadBalancingClientCreateCredentialsCall{Call: call} +} + +// MockLoadBalancingClientCreateCredentialsCall wrap *gomock.Call +type MockLoadBalancingClientCreateCredentialsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockLoadBalancingClientCreateCredentialsCall) Return(arg0 *v2api.CreateCredentialsResponse, arg1 error) *MockLoadBalancingClientCreateCredentialsCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockLoadBalancingClientCreateCredentialsCall) Do(f func(context.Context, v2api.CreateCredentialsPayload) (*v2api.CreateCredentialsResponse, error)) *MockLoadBalancingClientCreateCredentialsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockLoadBalancingClientCreateCredentialsCall) DoAndReturn(f func(context.Context, v2api.CreateCredentialsPayload) (*v2api.CreateCredentialsResponse, error)) *MockLoadBalancingClientCreateCredentialsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// CreateLoadBalancer mocks base method. +func (m *MockLoadBalancingClient) CreateLoadBalancer(ctx context.Context, payload *v2api.CreateLoadBalancerPayload) (*v2api.LoadBalancer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateLoadBalancer", ctx, payload) + ret0, _ := ret[0].(*v2api.LoadBalancer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateLoadBalancer indicates an expected call of CreateLoadBalancer. +func (mr *MockLoadBalancingClientMockRecorder) CreateLoadBalancer(ctx, payload any) *MockLoadBalancingClientCreateLoadBalancerCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLoadBalancer", reflect.TypeOf((*MockLoadBalancingClient)(nil).CreateLoadBalancer), ctx, payload) + return &MockLoadBalancingClientCreateLoadBalancerCall{Call: call} +} + +// MockLoadBalancingClientCreateLoadBalancerCall wrap *gomock.Call +type MockLoadBalancingClientCreateLoadBalancerCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockLoadBalancingClientCreateLoadBalancerCall) Return(arg0 *v2api.LoadBalancer, arg1 error) *MockLoadBalancingClientCreateLoadBalancerCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockLoadBalancingClientCreateLoadBalancerCall) Do(f func(context.Context, *v2api.CreateLoadBalancerPayload) (*v2api.LoadBalancer, error)) *MockLoadBalancingClientCreateLoadBalancerCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockLoadBalancingClientCreateLoadBalancerCall) DoAndReturn(f func(context.Context, *v2api.CreateLoadBalancerPayload) (*v2api.LoadBalancer, error)) *MockLoadBalancingClientCreateLoadBalancerCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// DeleteCredentials mocks base method. +func (m *MockLoadBalancingClient) DeleteCredentials(ctx context.Context, credentialsRef string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteCredentials", ctx, credentialsRef) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteCredentials indicates an expected call of DeleteCredentials. +func (mr *MockLoadBalancingClientMockRecorder) DeleteCredentials(ctx, credentialsRef any) *MockLoadBalancingClientDeleteCredentialsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCredentials", reflect.TypeOf((*MockLoadBalancingClient)(nil).DeleteCredentials), ctx, credentialsRef) + return &MockLoadBalancingClientDeleteCredentialsCall{Call: call} +} + +// MockLoadBalancingClientDeleteCredentialsCall wrap *gomock.Call +type MockLoadBalancingClientDeleteCredentialsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockLoadBalancingClientDeleteCredentialsCall) Return(arg0 error) *MockLoadBalancingClientDeleteCredentialsCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockLoadBalancingClientDeleteCredentialsCall) Do(f func(context.Context, string) error) *MockLoadBalancingClientDeleteCredentialsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockLoadBalancingClientDeleteCredentialsCall) DoAndReturn(f func(context.Context, string) error) *MockLoadBalancingClientDeleteCredentialsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// DeleteLoadBalancer mocks base method. +func (m *MockLoadBalancingClient) DeleteLoadBalancer(ctx context.Context, lbName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteLoadBalancer", ctx, lbName) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteLoadBalancer indicates an expected call of DeleteLoadBalancer. +func (mr *MockLoadBalancingClientMockRecorder) DeleteLoadBalancer(ctx, lbName any) *MockLoadBalancingClientDeleteLoadBalancerCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteLoadBalancer", reflect.TypeOf((*MockLoadBalancingClient)(nil).DeleteLoadBalancer), ctx, lbName) + return &MockLoadBalancingClientDeleteLoadBalancerCall{Call: call} +} + +// MockLoadBalancingClientDeleteLoadBalancerCall wrap *gomock.Call +type MockLoadBalancingClientDeleteLoadBalancerCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockLoadBalancingClientDeleteLoadBalancerCall) Return(arg0 error) *MockLoadBalancingClientDeleteLoadBalancerCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockLoadBalancingClientDeleteLoadBalancerCall) Do(f func(context.Context, string) error) *MockLoadBalancingClientDeleteLoadBalancerCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockLoadBalancingClientDeleteLoadBalancerCall) DoAndReturn(f func(context.Context, string) error) *MockLoadBalancingClientDeleteLoadBalancerCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GetCredentials mocks base method. +func (m *MockLoadBalancingClient) GetCredentials(ctx context.Context, credentialsRef string) (*v2api.GetCredentialsResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCredentials", ctx, credentialsRef) + ret0, _ := ret[0].(*v2api.GetCredentialsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCredentials indicates an expected call of GetCredentials. +func (mr *MockLoadBalancingClientMockRecorder) GetCredentials(ctx, credentialsRef any) *MockLoadBalancingClientGetCredentialsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCredentials", reflect.TypeOf((*MockLoadBalancingClient)(nil).GetCredentials), ctx, credentialsRef) + return &MockLoadBalancingClientGetCredentialsCall{Call: call} +} + +// MockLoadBalancingClientGetCredentialsCall wrap *gomock.Call +type MockLoadBalancingClientGetCredentialsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockLoadBalancingClientGetCredentialsCall) Return(arg0 *v2api.GetCredentialsResponse, arg1 error) *MockLoadBalancingClientGetCredentialsCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockLoadBalancingClientGetCredentialsCall) Do(f func(context.Context, string) (*v2api.GetCredentialsResponse, error)) *MockLoadBalancingClientGetCredentialsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockLoadBalancingClientGetCredentialsCall) DoAndReturn(f func(context.Context, string) (*v2api.GetCredentialsResponse, error)) *MockLoadBalancingClientGetCredentialsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// GetLoadBalancer mocks base method. +func (m *MockLoadBalancingClient) GetLoadBalancer(ctx context.Context, id string) (*v2api.LoadBalancer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLoadBalancer", ctx, id) + ret0, _ := ret[0].(*v2api.LoadBalancer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetLoadBalancer indicates an expected call of GetLoadBalancer. +func (mr *MockLoadBalancingClientMockRecorder) GetLoadBalancer(ctx, id any) *MockLoadBalancingClientGetLoadBalancerCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLoadBalancer", reflect.TypeOf((*MockLoadBalancingClient)(nil).GetLoadBalancer), ctx, id) + return &MockLoadBalancingClientGetLoadBalancerCall{Call: call} +} + +// MockLoadBalancingClientGetLoadBalancerCall wrap *gomock.Call +type MockLoadBalancingClientGetLoadBalancerCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockLoadBalancingClientGetLoadBalancerCall) Return(arg0 *v2api.LoadBalancer, arg1 error) *MockLoadBalancingClientGetLoadBalancerCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockLoadBalancingClientGetLoadBalancerCall) Do(f func(context.Context, string) (*v2api.LoadBalancer, error)) *MockLoadBalancingClientGetLoadBalancerCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockLoadBalancingClientGetLoadBalancerCall) DoAndReturn(f func(context.Context, string) (*v2api.LoadBalancer, error)) *MockLoadBalancingClientGetLoadBalancerCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ListCredentials mocks base method. +func (m *MockLoadBalancingClient) ListCredentials(ctx context.Context) (*v2api.ListCredentialsResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListCredentials", ctx) + ret0, _ := ret[0].(*v2api.ListCredentialsResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListCredentials indicates an expected call of ListCredentials. +func (mr *MockLoadBalancingClientMockRecorder) ListCredentials(ctx any) *MockLoadBalancingClientListCredentialsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCredentials", reflect.TypeOf((*MockLoadBalancingClient)(nil).ListCredentials), ctx) + return &MockLoadBalancingClientListCredentialsCall{Call: call} +} + +// MockLoadBalancingClientListCredentialsCall wrap *gomock.Call +type MockLoadBalancingClientListCredentialsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockLoadBalancingClientListCredentialsCall) Return(arg0 *v2api.ListCredentialsResponse, arg1 error) *MockLoadBalancingClientListCredentialsCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockLoadBalancingClientListCredentialsCall) Do(f func(context.Context) (*v2api.ListCredentialsResponse, error)) *MockLoadBalancingClientListCredentialsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockLoadBalancingClientListCredentialsCall) DoAndReturn(f func(context.Context) (*v2api.ListCredentialsResponse, error)) *MockLoadBalancingClientListCredentialsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// ListLoadBalancers mocks base method. +func (m *MockLoadBalancingClient) ListLoadBalancers(ctx context.Context) ([]v2api.LoadBalancer, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListLoadBalancers", ctx) + ret0, _ := ret[0].([]v2api.LoadBalancer) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListLoadBalancers indicates an expected call of ListLoadBalancers. +func (mr *MockLoadBalancingClientMockRecorder) ListLoadBalancers(ctx any) *MockLoadBalancingClientListLoadBalancersCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListLoadBalancers", reflect.TypeOf((*MockLoadBalancingClient)(nil).ListLoadBalancers), ctx) + return &MockLoadBalancingClientListLoadBalancersCall{Call: call} +} + +// MockLoadBalancingClientListLoadBalancersCall wrap *gomock.Call +type MockLoadBalancingClientListLoadBalancersCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockLoadBalancingClientListLoadBalancersCall) Return(arg0 []v2api.LoadBalancer, arg1 error) *MockLoadBalancingClientListLoadBalancersCall { + c.Call = c.Call.Return(arg0, arg1) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockLoadBalancingClientListLoadBalancersCall) Do(f func(context.Context) ([]v2api.LoadBalancer, error)) *MockLoadBalancingClientListLoadBalancersCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockLoadBalancingClientListLoadBalancersCall) DoAndReturn(f func(context.Context) ([]v2api.LoadBalancer, error)) *MockLoadBalancingClientListLoadBalancersCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// UpdateCredentials mocks base method. +func (m *MockLoadBalancingClient) UpdateCredentials(ctx context.Context, credentialsRef string, payload v2api.UpdateCredentialsPayload) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateCredentials", ctx, credentialsRef, payload) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateCredentials indicates an expected call of UpdateCredentials. +func (mr *MockLoadBalancingClientMockRecorder) UpdateCredentials(ctx, credentialsRef, payload any) *MockLoadBalancingClientUpdateCredentialsCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCredentials", reflect.TypeOf((*MockLoadBalancingClient)(nil).UpdateCredentials), ctx, credentialsRef, payload) + return &MockLoadBalancingClientUpdateCredentialsCall{Call: call} +} + +// MockLoadBalancingClientUpdateCredentialsCall wrap *gomock.Call +type MockLoadBalancingClientUpdateCredentialsCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockLoadBalancingClientUpdateCredentialsCall) Return(arg0 error) *MockLoadBalancingClientUpdateCredentialsCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockLoadBalancingClientUpdateCredentialsCall) Do(f func(context.Context, string, v2api.UpdateCredentialsPayload) error) *MockLoadBalancingClientUpdateCredentialsCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockLoadBalancingClientUpdateCredentialsCall) DoAndReturn(f func(context.Context, string, v2api.UpdateCredentialsPayload) error) *MockLoadBalancingClientUpdateCredentialsCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// UpdateLoadBalancer mocks base method. +func (m *MockLoadBalancingClient) UpdateLoadBalancer(ctx context.Context, lbName string, updates *v2api.UpdateLoadBalancerPayload) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateLoadBalancer", ctx, lbName, updates) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateLoadBalancer indicates an expected call of UpdateLoadBalancer. +func (mr *MockLoadBalancingClientMockRecorder) UpdateLoadBalancer(ctx, lbName, updates any) *MockLoadBalancingClientUpdateLoadBalancerCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateLoadBalancer", reflect.TypeOf((*MockLoadBalancingClient)(nil).UpdateLoadBalancer), ctx, lbName, updates) + return &MockLoadBalancingClientUpdateLoadBalancerCall{Call: call} +} + +// MockLoadBalancingClientUpdateLoadBalancerCall wrap *gomock.Call +type MockLoadBalancingClientUpdateLoadBalancerCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockLoadBalancingClientUpdateLoadBalancerCall) Return(arg0 error) *MockLoadBalancingClientUpdateLoadBalancerCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockLoadBalancingClientUpdateLoadBalancerCall) Do(f func(context.Context, string, *v2api.UpdateLoadBalancerPayload) error) *MockLoadBalancingClientUpdateLoadBalancerCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockLoadBalancingClientUpdateLoadBalancerCall) DoAndReturn(f func(context.Context, string, *v2api.UpdateLoadBalancerPayload) error) *MockLoadBalancingClientUpdateLoadBalancerCall { + c.Call = c.Call.DoAndReturn(f) + return c +} + +// UpdateTargetPool mocks base method. +func (m *MockLoadBalancingClient) UpdateTargetPool(ctx context.Context, name, targetPoolName string, payload v2api.UpdateTargetPoolPayload) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateTargetPool", ctx, name, targetPoolName, payload) + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateTargetPool indicates an expected call of UpdateTargetPool. +func (mr *MockLoadBalancingClientMockRecorder) UpdateTargetPool(ctx, name, targetPoolName, payload any) *MockLoadBalancingClientUpdateTargetPoolCall { + mr.mock.ctrl.T.Helper() + call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTargetPool", reflect.TypeOf((*MockLoadBalancingClient)(nil).UpdateTargetPool), ctx, name, targetPoolName, payload) + return &MockLoadBalancingClientUpdateTargetPoolCall{Call: call} +} + +// MockLoadBalancingClientUpdateTargetPoolCall wrap *gomock.Call +type MockLoadBalancingClientUpdateTargetPoolCall struct { + *gomock.Call +} + +// Return rewrite *gomock.Call.Return +func (c *MockLoadBalancingClientUpdateTargetPoolCall) Return(arg0 error) *MockLoadBalancingClientUpdateTargetPoolCall { + c.Call = c.Call.Return(arg0) + return c +} + +// Do rewrite *gomock.Call.Do +func (c *MockLoadBalancingClientUpdateTargetPoolCall) Do(f func(context.Context, string, string, v2api.UpdateTargetPoolPayload) error) *MockLoadBalancingClientUpdateTargetPoolCall { + c.Call = c.Call.Do(f) + return c +} + +// DoAndReturn rewrite *gomock.Call.DoAndReturn +func (c *MockLoadBalancingClientUpdateTargetPoolCall) DoAndReturn(f func(context.Context, string, string, v2api.UpdateTargetPoolPayload) error) *MockLoadBalancingClientUpdateTargetPoolCall { + c.Call = c.Call.DoAndReturn(f) + return c +} diff --git a/pkg/stackit/client/mock/mock.go b/pkg/stackit/client/mock/mock.go new file mode 100644 index 00000000..aa5cbf0b --- /dev/null +++ b/pkg/stackit/client/mock/mock.go @@ -0,0 +1,72 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./pkg/stackit/client (interfaces: Factory) +// +// Generated by this command: +// +// mockgen -destination ./pkg/stackit/client/mock/mock.go -package client ./pkg/stackit/client Factory +// + +// Package client is a generated GoMock package. +package client + +import ( + reflect "reflect" + + client "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/client" + config "github.com/stackitcloud/stackit-sdk-go/core/config" + gomock "go.uber.org/mock/gomock" +) + +// MockFactory is a mock of Factory interface. +type MockFactory struct { + ctrl *gomock.Controller + recorder *MockFactoryMockRecorder + isgomock struct{} +} + +// MockFactoryMockRecorder is the mock recorder for MockFactory. +type MockFactoryMockRecorder struct { + mock *MockFactory +} + +// NewMockFactory creates a new mock instance. +func NewMockFactory(ctrl *gomock.Controller) *MockFactory { + mock := &MockFactory{ctrl: ctrl} + mock.recorder = &MockFactoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFactory) EXPECT() *MockFactoryMockRecorder { + return m.recorder +} + +// IaaS mocks base method. +func (m *MockFactory) IaaS(options []config.ConfigurationOption) (client.IaaSClient, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IaaS", options) + ret0, _ := ret[0].(client.IaaSClient) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// IaaS indicates an expected call of IaaS. +func (mr *MockFactoryMockRecorder) IaaS(options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IaaS", reflect.TypeOf((*MockFactory)(nil).IaaS), options) +} + +// LoadBalancing mocks base method. +func (m *MockFactory) LoadBalancing(ptions []config.ConfigurationOption) (client.LoadBalancingClient, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LoadBalancing", ptions) + ret0, _ := ret[0].(client.LoadBalancingClient) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LoadBalancing indicates an expected call of LoadBalancing. +func (mr *MockFactoryMockRecorder) LoadBalancing(ptions any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadBalancing", reflect.TypeOf((*MockFactory)(nil).LoadBalancing), ptions) +} diff --git a/pkg/stackit/suite_test.go b/pkg/stackit/client/suite_test.go similarity index 92% rename from pkg/stackit/suite_test.go rename to pkg/stackit/client/suite_test.go index 5450af33..99c512b7 100644 --- a/pkg/stackit/suite_test.go +++ b/pkg/stackit/client/suite_test.go @@ -1,4 +1,4 @@ -package stackit +package client import ( "testing" diff --git a/pkg/stackit/filter.go b/pkg/stackit/client/utils.go similarity index 72% rename from pkg/stackit/filter.go rename to pkg/stackit/client/utils.go index e91e5853..6c91294f 100644 --- a/pkg/stackit/filter.go +++ b/pkg/stackit/client/utils.go @@ -1,36 +1,22 @@ -package stackit +package client import ( + "errors" + "net/http" + + oapiError "github.com/stackitcloud/stackit-sdk-go/core/oapierror" iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" ) -// TODO: Remove this once the IaaS API supports filtering by name, status, and volume ID. - -//nolint:dupl // We don't feel like doing generics to undupe this. -func filterBackups(backups []iaas.Backup, filters map[string]string) []iaas.Backup { - filteredBackups := make([]iaas.Backup, 0) - - if filters == nil { - return backups - } - - for _, obj := range backups { - if val, ok := filters["Status"]; ok && val != obj.GetStatus() { - continue - } - if val, ok := filters["VolumeID"]; ok && val != obj.GetVolumeId() { - continue - } - if val, ok := filters["Name"]; ok && val != obj.GetName() { - continue - } - filteredBackups = append(filteredBackups, obj) +func isOpenAPINotFound(err error) bool { + apiErr := &oapiError.GenericOpenAPIError{} + if !errors.As(err, &apiErr) { + return false } - - return filteredBackups + return apiErr.StatusCode == http.StatusNotFound } -func filterVolumes(volumes []iaas.Volume, filters map[string]string) []iaas.Volume { +func FilterVolumes(volumes []iaas.Volume, filters map[string]string) []iaas.Volume { filteredVolumes := make([]iaas.Volume, 0) if filters == nil { @@ -49,7 +35,7 @@ func filterVolumes(volumes []iaas.Volume, filters map[string]string) []iaas.Volu } //nolint:dupl // We don't feel like doing generics to undupe this. -func filterSnapshots(snapshots []iaas.Snapshot, filters map[string]string) []iaas.Snapshot { +func FilterSnapshots(snapshots []iaas.Snapshot, filters map[string]string) []iaas.Snapshot { filteredSnapshots := make([]iaas.Snapshot, 0) if filters == nil { @@ -71,3 +57,31 @@ func filterSnapshots(snapshots []iaas.Snapshot, filters map[string]string) []iaa return filteredSnapshots } + +//nolint:dupl // We don't feel like doing generics to undupe this. +func FilterBackups(backups []iaas.Backup, filters map[string]string) []iaas.Backup { + filteredBackups := make([]iaas.Backup, 0) + + if filters == nil { + return backups + } + + for _, obj := range backups { + if val, ok := filters["Status"]; ok && val != obj.GetStatus() { + continue + } + if val, ok := filters["VolumeID"]; ok && val != obj.GetVolumeId() { + continue + } + if val, ok := filters["Name"]; ok && val != obj.GetName() { + continue + } + filteredBackups = append(filteredBackups, obj) + } + + return filteredBackups +} + +func IsNotFound(err error) bool { + return errors.Is(err, ErrorNotFound) +} diff --git a/pkg/stackit/filter_test.go b/pkg/stackit/client/utils_test.go similarity index 83% rename from pkg/stackit/filter_test.go rename to pkg/stackit/client/utils_test.go index 13bd5b47..10588bb7 100644 --- a/pkg/stackit/filter_test.go +++ b/pkg/stackit/client/utils_test.go @@ -1,4 +1,4 @@ -package stackit +package client import ( . "github.com/onsi/ginkgo/v2" @@ -7,7 +7,7 @@ import ( ) var _ = Describe("Filter", func() { - Describe("filterBackups", func() { + Describe("FilterBackups", func() { var ( backups []iaas.Backup filters map[string]string @@ -23,13 +23,13 @@ var _ = Describe("Filter", func() { }) It("should return all backups when filters is nil", func() { - result := filterBackups(backups, nil) + result := FilterBackups(backups, nil) Expect(result).To(Equal(backups)) }) It("should filter by Status", func() { filters["Status"] = "available" - result := filterBackups(backups, filters) + result := FilterBackups(backups, filters) Expect(result).To(HaveLen(2)) Expect(*result[0].Name).To(Equal("backup-1")) Expect(*result[1].Name).To(Equal("backup-3")) @@ -37,14 +37,14 @@ var _ = Describe("Filter", func() { It("should filter by VolumeID", func() { filters["VolumeID"] = "vol-2" - result := filterBackups(backups, filters) + result := FilterBackups(backups, filters) Expect(result).To(HaveLen(1)) Expect(*result[0].Name).To(Equal("backup-2")) }) It("should filter by Name", func() { filters["Name"] = "backup-1" - result := filterBackups(backups, filters) + result := FilterBackups(backups, filters) Expect(result).To(HaveLen(1)) Expect(*result[0].Name).To(Equal("backup-1")) }) @@ -52,14 +52,14 @@ var _ = Describe("Filter", func() { It("should filter by multiple criteria", func() { filters["Status"] = "available" filters["VolumeID"] = "vol-1" - result := filterBackups(backups, filters) + result := FilterBackups(backups, filters) Expect(result).To(HaveLen(2)) Expect(*result[0].Name).To(Equal("backup-1")) Expect(*result[1].Name).To(Equal("backup-3")) }) }) - Describe("filterVolumes", func() { + Describe("FilterVolumes", func() { var ( volumes []iaas.Volume filters map[string]string @@ -75,20 +75,20 @@ var _ = Describe("Filter", func() { }) It("should return all volumes when filters is nil", func() { - result := filterVolumes(volumes, nil) + result := FilterVolumes(volumes, nil) Expect(result).To(Equal(volumes)) }) It("should filter by Name", func() { filters["Name"] = "volume-1" - result := filterVolumes(volumes, filters) + result := FilterVolumes(volumes, filters) Expect(result).To(HaveLen(2)) Expect(*result[0].Name).To(Equal("volume-1")) Expect(*result[1].Name).To(Equal("volume-1")) }) }) - Describe("filterSnapshots", func() { + Describe("FilterSnapshots", func() { var ( snapshots []iaas.Snapshot filters map[string]string @@ -104,13 +104,13 @@ var _ = Describe("Filter", func() { }) It("should return all snapshots when filters is nil", func() { - result := filterSnapshots(snapshots, nil) + result := FilterSnapshots(snapshots, nil) Expect(result).To(Equal(snapshots)) }) It("should filter by Status", func() { filters["Status"] = "available" - result := filterSnapshots(snapshots, filters) + result := FilterSnapshots(snapshots, filters) Expect(result).To(HaveLen(2)) Expect(*result[0].Name).To(Equal("snapshot-1")) Expect(*result[1].Name).To(Equal("snapshot-3")) @@ -118,14 +118,14 @@ var _ = Describe("Filter", func() { It("should filter by VolumeID", func() { filters["VolumeID"] = "vol-2" - result := filterSnapshots(snapshots, filters) + result := FilterSnapshots(snapshots, filters) Expect(result).To(HaveLen(1)) Expect(*result[0].Name).To(Equal("snapshot-2")) }) It("should filter by Name", func() { filters["Name"] = "snapshot-1" - result := filterSnapshots(snapshots, filters) + result := FilterSnapshots(snapshots, filters) Expect(result).To(HaveLen(1)) Expect(*result[0].Name).To(Equal("snapshot-1")) }) @@ -133,7 +133,7 @@ var _ = Describe("Filter", func() { It("should filter by multiple criteria", func() { filters["Status"] = "available" filters["VolumeID"] = "vol-1" - result := filterSnapshots(snapshots, filters) + result := FilterSnapshots(snapshots, filters) Expect(result).To(HaveLen(2)) Expect(*result[0].Name).To(Equal("snapshot-1")) Expect(*result[1].Name).To(Equal("snapshot-3")) diff --git a/pkg/stackit/iaas_mock.go b/pkg/stackit/iaas_mock.go deleted file mode 100644 index 397d3cf3..00000000 --- a/pkg/stackit/iaas_mock.go +++ /dev/null @@ -1,411 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./pkg/stackit (interfaces: IaasClient) -// -// Generated by this command: -// -// mockgen -destination ./pkg/stackit/iaas_mock.go -package stackit ./pkg/stackit IaasClient -// - -// Package stackit is a generated GoMock package. -package stackit - -import ( - context "context" - reflect "reflect" - - config "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/config" - v2api "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" - gomock "go.uber.org/mock/gomock" - wait "k8s.io/apimachinery/pkg/util/wait" -) - -// MockIaasClient is a mock of IaasClient interface. -type MockIaasClient struct { - ctrl *gomock.Controller - recorder *MockIaasClientMockRecorder - isgomock struct{} -} - -// MockIaasClientMockRecorder is the mock recorder for MockIaasClient. -type MockIaasClientMockRecorder struct { - mock *MockIaasClient -} - -// NewMockIaasClient creates a new mock instance. -func NewMockIaasClient(ctrl *gomock.Controller) *MockIaasClient { - mock := &MockIaasClient{ctrl: ctrl} - mock.recorder = &MockIaasClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockIaasClient) EXPECT() *MockIaasClientMockRecorder { - return m.recorder -} - -// AttachVolume mocks base method. -func (m *MockIaasClient) AttachVolume(ctx context.Context, instanceID, volumeID string) (string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "AttachVolume", ctx, instanceID, volumeID) - ret0, _ := ret[0].(string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// AttachVolume indicates an expected call of AttachVolume. -func (mr *MockIaasClientMockRecorder) AttachVolume(ctx, instanceID, volumeID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AttachVolume", reflect.TypeOf((*MockIaasClient)(nil).AttachVolume), ctx, instanceID, volumeID) -} - -// CreateBackup mocks base method. -func (m *MockIaasClient) CreateBackup(ctx context.Context, name, volID, snapshotID string, tags map[string]string) (*v2api.Backup, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateBackup", ctx, name, volID, snapshotID, tags) - ret0, _ := ret[0].(*v2api.Backup) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateBackup indicates an expected call of CreateBackup. -func (mr *MockIaasClientMockRecorder) CreateBackup(ctx, name, volID, snapshotID, tags any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBackup", reflect.TypeOf((*MockIaasClient)(nil).CreateBackup), ctx, name, volID, snapshotID, tags) -} - -// CreateSnapshot mocks base method. -func (m *MockIaasClient) CreateSnapshot(ctx context.Context, name, volID string, tags map[string]string) (*v2api.Snapshot, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateSnapshot", ctx, name, volID, tags) - ret0, _ := ret[0].(*v2api.Snapshot) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateSnapshot indicates an expected call of CreateSnapshot. -func (mr *MockIaasClientMockRecorder) CreateSnapshot(ctx, name, volID, tags any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSnapshot", reflect.TypeOf((*MockIaasClient)(nil).CreateSnapshot), ctx, name, volID, tags) -} - -// CreateVolume mocks base method. -func (m *MockIaasClient) CreateVolume(arg0 context.Context, arg1 *v2api.CreateVolumePayload) (*v2api.Volume, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateVolume", arg0, arg1) - ret0, _ := ret[0].(*v2api.Volume) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateVolume indicates an expected call of CreateVolume. -func (mr *MockIaasClientMockRecorder) CreateVolume(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateVolume", reflect.TypeOf((*MockIaasClient)(nil).CreateVolume), arg0, arg1) -} - -// DeleteBackup mocks base method. -func (m *MockIaasClient) DeleteBackup(ctx context.Context, backupID string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteBackup", ctx, backupID) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteBackup indicates an expected call of DeleteBackup. -func (mr *MockIaasClientMockRecorder) DeleteBackup(ctx, backupID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteBackup", reflect.TypeOf((*MockIaasClient)(nil).DeleteBackup), ctx, backupID) -} - -// DeleteSnapshot mocks base method. -func (m *MockIaasClient) DeleteSnapshot(ctx context.Context, snapID string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteSnapshot", ctx, snapID) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteSnapshot indicates an expected call of DeleteSnapshot. -func (mr *MockIaasClientMockRecorder) DeleteSnapshot(ctx, snapID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteSnapshot", reflect.TypeOf((*MockIaasClient)(nil).DeleteSnapshot), ctx, snapID) -} - -// DeleteVolume mocks base method. -func (m *MockIaasClient) DeleteVolume(ctx context.Context, volumeID string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteVolume", ctx, volumeID) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteVolume indicates an expected call of DeleteVolume. -func (mr *MockIaasClientMockRecorder) DeleteVolume(ctx, volumeID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVolume", reflect.TypeOf((*MockIaasClient)(nil).DeleteVolume), ctx, volumeID) -} - -// DetachVolume mocks base method. -func (m *MockIaasClient) DetachVolume(ctx context.Context, instanceID, volumeID string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DetachVolume", ctx, instanceID, volumeID) - ret0, _ := ret[0].(error) - return ret0 -} - -// DetachVolume indicates an expected call of DetachVolume. -func (mr *MockIaasClientMockRecorder) DetachVolume(ctx, instanceID, volumeID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DetachVolume", reflect.TypeOf((*MockIaasClient)(nil).DetachVolume), ctx, instanceID, volumeID) -} - -// ExpandVolume mocks base method. -func (m *MockIaasClient) ExpandVolume(ctx context.Context, volumeID, status string, size int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExpandVolume", ctx, volumeID, status, size) - ret0, _ := ret[0].(error) - return ret0 -} - -// ExpandVolume indicates an expected call of ExpandVolume. -func (mr *MockIaasClientMockRecorder) ExpandVolume(ctx, volumeID, status, size any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExpandVolume", reflect.TypeOf((*MockIaasClient)(nil).ExpandVolume), ctx, volumeID, status, size) -} - -// GetBackupByID mocks base method. -func (m *MockIaasClient) GetBackupByID(ctx context.Context, backupID string) (*v2api.Backup, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBackupByID", ctx, backupID) - ret0, _ := ret[0].(*v2api.Backup) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetBackupByID indicates an expected call of GetBackupByID. -func (mr *MockIaasClientMockRecorder) GetBackupByID(ctx, backupID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBackupByID", reflect.TypeOf((*MockIaasClient)(nil).GetBackupByID), ctx, backupID) -} - -// GetBlockStorageOpts mocks base method. -func (m *MockIaasClient) GetBlockStorageOpts() config.BlockStorageOpts { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetBlockStorageOpts") - ret0, _ := ret[0].(config.BlockStorageOpts) - return ret0 -} - -// GetBlockStorageOpts indicates an expected call of GetBlockStorageOpts. -func (mr *MockIaasClientMockRecorder) GetBlockStorageOpts() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockStorageOpts", reflect.TypeOf((*MockIaasClient)(nil).GetBlockStorageOpts)) -} - -// GetInstanceByID mocks base method. -func (m *MockIaasClient) GetInstanceByID(ctx context.Context, instanceID string) (*v2api.Server, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetInstanceByID", ctx, instanceID) - ret0, _ := ret[0].(*v2api.Server) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetInstanceByID indicates an expected call of GetInstanceByID. -func (mr *MockIaasClientMockRecorder) GetInstanceByID(ctx, instanceID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInstanceByID", reflect.TypeOf((*MockIaasClient)(nil).GetInstanceByID), ctx, instanceID) -} - -// GetSnapshotByID mocks base method. -func (m *MockIaasClient) GetSnapshotByID(ctx context.Context, snapshotID string) (*v2api.Snapshot, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetSnapshotByID", ctx, snapshotID) - ret0, _ := ret[0].(*v2api.Snapshot) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetSnapshotByID indicates an expected call of GetSnapshotByID. -func (mr *MockIaasClientMockRecorder) GetSnapshotByID(ctx, snapshotID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSnapshotByID", reflect.TypeOf((*MockIaasClient)(nil).GetSnapshotByID), ctx, snapshotID) -} - -// GetVolume mocks base method. -func (m *MockIaasClient) GetVolume(ctx context.Context, volumeID string) (*v2api.Volume, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolume", ctx, volumeID) - ret0, _ := ret[0].(*v2api.Volume) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetVolume indicates an expected call of GetVolume. -func (mr *MockIaasClientMockRecorder) GetVolume(ctx, volumeID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolume", reflect.TypeOf((*MockIaasClient)(nil).GetVolume), ctx, volumeID) -} - -// GetVolumeByName mocks base method. -func (m *MockIaasClient) GetVolumeByName(ctx context.Context, name string) (*v2api.Volume, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumeByName", ctx, name) - ret0, _ := ret[0].(*v2api.Volume) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetVolumeByName indicates an expected call of GetVolumeByName. -func (mr *MockIaasClientMockRecorder) GetVolumeByName(ctx, name any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumeByName", reflect.TypeOf((*MockIaasClient)(nil).GetVolumeByName), ctx, name) -} - -// GetVolumesByName mocks base method. -func (m *MockIaasClient) GetVolumesByName(ctx context.Context, name string) ([]v2api.Volume, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetVolumesByName", ctx, name) - ret0, _ := ret[0].([]v2api.Volume) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetVolumesByName indicates an expected call of GetVolumesByName. -func (mr *MockIaasClientMockRecorder) GetVolumesByName(ctx, name any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVolumesByName", reflect.TypeOf((*MockIaasClient)(nil).GetVolumesByName), ctx, name) -} - -// ListBackups mocks base method. -func (m *MockIaasClient) ListBackups(ctx context.Context, filters map[string]string) ([]v2api.Backup, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListBackups", ctx, filters) - ret0, _ := ret[0].([]v2api.Backup) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListBackups indicates an expected call of ListBackups. -func (mr *MockIaasClientMockRecorder) ListBackups(ctx, filters any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBackups", reflect.TypeOf((*MockIaasClient)(nil).ListBackups), ctx, filters) -} - -// ListSnapshots mocks base method. -func (m *MockIaasClient) ListSnapshots(ctx context.Context, filters map[string]string) ([]v2api.Snapshot, string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListSnapshots", ctx, filters) - ret0, _ := ret[0].([]v2api.Snapshot) - ret1, _ := ret[1].(string) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ListSnapshots indicates an expected call of ListSnapshots. -func (mr *MockIaasClientMockRecorder) ListSnapshots(ctx, filters any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListSnapshots", reflect.TypeOf((*MockIaasClient)(nil).ListSnapshots), ctx, filters) -} - -// ListVolumes mocks base method. -func (m *MockIaasClient) ListVolumes(ctx context.Context, limit int, startingToken string) ([]v2api.Volume, string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListVolumes", ctx, limit, startingToken) - ret0, _ := ret[0].([]v2api.Volume) - ret1, _ := ret[1].(string) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// ListVolumes indicates an expected call of ListVolumes. -func (mr *MockIaasClientMockRecorder) ListVolumes(ctx, limit, startingToken any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListVolumes", reflect.TypeOf((*MockIaasClient)(nil).ListVolumes), ctx, limit, startingToken) -} - -// WaitBackupReady mocks base method. -func (m *MockIaasClient) WaitBackupReady(ctx context.Context, backupID string, snapshotSize int64, backupMaxDurationSecondsPerGB int) (*string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitBackupReady", ctx, backupID, snapshotSize, backupMaxDurationSecondsPerGB) - ret0, _ := ret[0].(*string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// WaitBackupReady indicates an expected call of WaitBackupReady. -func (mr *MockIaasClientMockRecorder) WaitBackupReady(ctx, backupID, snapshotSize, backupMaxDurationSecondsPerGB any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitBackupReady", reflect.TypeOf((*MockIaasClient)(nil).WaitBackupReady), ctx, backupID, snapshotSize, backupMaxDurationSecondsPerGB) -} - -// WaitDiskAttached mocks base method. -func (m *MockIaasClient) WaitDiskAttached(ctx context.Context, instanceID, volumeID string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitDiskAttached", ctx, instanceID, volumeID) - ret0, _ := ret[0].(error) - return ret0 -} - -// WaitDiskAttached indicates an expected call of WaitDiskAttached. -func (mr *MockIaasClientMockRecorder) WaitDiskAttached(ctx, instanceID, volumeID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitDiskAttached", reflect.TypeOf((*MockIaasClient)(nil).WaitDiskAttached), ctx, instanceID, volumeID) -} - -// WaitDiskDetached mocks base method. -func (m *MockIaasClient) WaitDiskDetached(ctx context.Context, instanceID, volumeID string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitDiskDetached", ctx, instanceID, volumeID) - ret0, _ := ret[0].(error) - return ret0 -} - -// WaitDiskDetached indicates an expected call of WaitDiskDetached. -func (mr *MockIaasClientMockRecorder) WaitDiskDetached(ctx, instanceID, volumeID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitDiskDetached", reflect.TypeOf((*MockIaasClient)(nil).WaitDiskDetached), ctx, instanceID, volumeID) -} - -// WaitSnapshotReady mocks base method. -func (m *MockIaasClient) WaitSnapshotReady(ctx context.Context, snapshotID string) (*string, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitSnapshotReady", ctx, snapshotID) - ret0, _ := ret[0].(*string) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// WaitSnapshotReady indicates an expected call of WaitSnapshotReady. -func (mr *MockIaasClientMockRecorder) WaitSnapshotReady(ctx, snapshotID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitSnapshotReady", reflect.TypeOf((*MockIaasClient)(nil).WaitSnapshotReady), ctx, snapshotID) -} - -// WaitVolumeTargetStatus mocks base method. -func (m *MockIaasClient) WaitVolumeTargetStatus(ctx context.Context, volumeID string, tStatus []string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitVolumeTargetStatus", ctx, volumeID, tStatus) - ret0, _ := ret[0].(error) - return ret0 -} - -// WaitVolumeTargetStatus indicates an expected call of WaitVolumeTargetStatus. -func (mr *MockIaasClientMockRecorder) WaitVolumeTargetStatus(ctx, volumeID, tStatus any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitVolumeTargetStatus", reflect.TypeOf((*MockIaasClient)(nil).WaitVolumeTargetStatus), ctx, volumeID, tStatus) -} - -// WaitVolumeTargetStatusWithCustomBackoff mocks base method. -func (m *MockIaasClient) WaitVolumeTargetStatusWithCustomBackoff(ctx context.Context, volumeID string, targetStatus []string, backoff *wait.Backoff) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "WaitVolumeTargetStatusWithCustomBackoff", ctx, volumeID, targetStatus, backoff) - ret0, _ := ret[0].(error) - return ret0 -} - -// WaitVolumeTargetStatusWithCustomBackoff indicates an expected call of WaitVolumeTargetStatusWithCustomBackoff. -func (mr *MockIaasClientMockRecorder) WaitVolumeTargetStatusWithCustomBackoff(ctx, volumeID, targetStatus, backoff any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitVolumeTargetStatusWithCustomBackoff", reflect.TypeOf((*MockIaasClient)(nil).WaitVolumeTargetStatusWithCustomBackoff), ctx, volumeID, targetStatus, backoff) -} diff --git a/pkg/stackit/instances.go b/pkg/stackit/instances.go deleted file mode 100644 index 0c22b83b..00000000 --- a/pkg/stackit/instances.go +++ /dev/null @@ -1,25 +0,0 @@ -package stackit - -import ( - "context" - "net/http" - - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/stackiterrors" - "github.com/stackitcloud/stackit-sdk-go/core/runtime" - iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" - wait "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api/wait" -) - -func (os *iaasClient) GetInstanceByID(ctx context.Context, instanceID string) (*iaas.Server, error) { - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - server, err := os.iaas.GetServer(ctxWithHTTPResp, os.projectID, os.region, instanceID).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(wait.XRequestIDHeader) - return nil, stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return nil, err - } - return server, nil -} diff --git a/pkg/stackit/loadbalancer.go b/pkg/stackit/loadbalancer.go deleted file mode 100644 index 6820c60a..00000000 --- a/pkg/stackit/loadbalancer.go +++ /dev/null @@ -1,76 +0,0 @@ -package stackit - -import ( - "context" - - "github.com/google/uuid" - loadbalancer "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api" -) - -func (cl lbClient) GetLoadBalancer(ctx context.Context, projectID, name string) (*loadbalancer.LoadBalancer, error) { - lb, err := cl.client.GetLoadBalancer(ctx, projectID, cl.region, name).Execute() - if isOpenAPINotFound(err) { - return lb, ErrorNotFound - } - return lb, err -} - -// DeleteLoadBalancer returns no error if the load balancer doesn't exist. -func (cl lbClient) DeleteLoadBalancer(ctx context.Context, projectID, name string) error { - _, err := cl.client.DeleteLoadBalancer(ctx, projectID, cl.region, name).Execute() - return err -} - -// CreateLoadBalancer returns ErrorNotFound if the project is not enabled. -func (cl lbClient) CreateLoadBalancer(ctx context.Context, projectID string, create *loadbalancer.CreateLoadBalancerPayload) (*loadbalancer.LoadBalancer, error) { - lb, err := cl.client.CreateLoadBalancer(ctx, projectID, cl.region). - CreateLoadBalancerPayload(*create). - XRequestID(uuid.NewString()). - Execute() - if isOpenAPINotFound(err) { - return lb, ErrorNotFound - } - return lb, err -} - -func (cl lbClient) UpdateLoadBalancer(ctx context.Context, projectID, name string, update *loadbalancer.UpdateLoadBalancerPayload) ( - *loadbalancer.LoadBalancer, error, -) { - return cl.client.UpdateLoadBalancer(ctx, projectID, cl.region, name). - UpdateLoadBalancerPayload(*update). - Execute() -} - -func (cl lbClient) UpdateTargetPool(ctx context.Context, projectID, name, targetPoolName string, payload loadbalancer.UpdateTargetPoolPayload) error { - _, err := cl.client.UpdateTargetPool(ctx, projectID, cl.region, name, targetPoolName). - UpdateTargetPoolPayload(payload). - Execute() - return err -} - -func (cl lbClient) ListCredentials(ctx context.Context, projectID string) (*loadbalancer.ListCredentialsResponse, error) { - return cl.client.ListCredentials(ctx, projectID, cl.region).Execute() -} - -func (cl lbClient) GetCredentials(ctx context.Context, projectID, credentialsRef string) (*loadbalancer.GetCredentialsResponse, error) { - return cl.client.GetCredentials(ctx, projectID, cl.region, credentialsRef).Execute() -} - -func (cl lbClient) CreateCredentials(ctx context.Context, projectID string, payload loadbalancer.CreateCredentialsPayload) (*loadbalancer.CreateCredentialsResponse, error) { //nolint:lll // looks weird when shortened - return cl.client.CreateCredentials(ctx, projectID, cl.region). - CreateCredentialsPayload(payload). - XRequestID(uuid.NewString()). - Execute() -} - -func (cl lbClient) UpdateCredentials(ctx context.Context, projectID, credentialsRef string, payload loadbalancer.UpdateCredentialsPayload) error { - _, err := cl.client.UpdateCredentials(ctx, projectID, cl.region, credentialsRef). - UpdateCredentialsPayload(payload). - Execute() - return err -} - -func (cl lbClient) DeleteCredentials(ctx context.Context, projectID, credentialsRef string) error { - _, err := cl.client.DeleteCredentials(ctx, projectID, cl.region, credentialsRef).Execute() - return err -} diff --git a/pkg/stackit/loadbalancer_mock.go b/pkg/stackit/loadbalancer_mock.go deleted file mode 100644 index 18dbf72a..00000000 --- a/pkg/stackit/loadbalancer_mock.go +++ /dev/null @@ -1,188 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./pkg/stackit (interfaces: LoadbalancerClient) -// -// Generated by this command: -// -// mockgen -destination ./pkg/stackit/loadbalancer_mock.go -package stackit ./pkg/stackit LoadbalancerClient -// - -// Package stackit is a generated GoMock package. -package stackit - -import ( - context "context" - reflect "reflect" - - v2api "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api" - gomock "go.uber.org/mock/gomock" -) - -// MockLoadbalancerClient is a mock of LoadbalancerClient interface. -type MockLoadbalancerClient struct { - ctrl *gomock.Controller - recorder *MockLoadbalancerClientMockRecorder - isgomock struct{} -} - -// MockLoadbalancerClientMockRecorder is the mock recorder for MockLoadbalancerClient. -type MockLoadbalancerClientMockRecorder struct { - mock *MockLoadbalancerClient -} - -// NewMockLoadbalancerClient creates a new mock instance. -func NewMockLoadbalancerClient(ctrl *gomock.Controller) *MockLoadbalancerClient { - mock := &MockLoadbalancerClient{ctrl: ctrl} - mock.recorder = &MockLoadbalancerClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockLoadbalancerClient) EXPECT() *MockLoadbalancerClientMockRecorder { - return m.recorder -} - -// CreateCredentials mocks base method. -func (m *MockLoadbalancerClient) CreateCredentials(ctx context.Context, projectID string, payload v2api.CreateCredentialsPayload) (*v2api.CreateCredentialsResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateCredentials", ctx, projectID, payload) - ret0, _ := ret[0].(*v2api.CreateCredentialsResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateCredentials indicates an expected call of CreateCredentials. -func (mr *MockLoadbalancerClientMockRecorder) CreateCredentials(ctx, projectID, payload any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateCredentials", reflect.TypeOf((*MockLoadbalancerClient)(nil).CreateCredentials), ctx, projectID, payload) -} - -// CreateLoadBalancer mocks base method. -func (m *MockLoadbalancerClient) CreateLoadBalancer(ctx context.Context, projectID string, loadbalancer *v2api.CreateLoadBalancerPayload) (*v2api.LoadBalancer, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateLoadBalancer", ctx, projectID, loadbalancer) - ret0, _ := ret[0].(*v2api.LoadBalancer) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateLoadBalancer indicates an expected call of CreateLoadBalancer. -func (mr *MockLoadbalancerClientMockRecorder) CreateLoadBalancer(ctx, projectID, loadbalancer any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLoadBalancer", reflect.TypeOf((*MockLoadbalancerClient)(nil).CreateLoadBalancer), ctx, projectID, loadbalancer) -} - -// DeleteCredentials mocks base method. -func (m *MockLoadbalancerClient) DeleteCredentials(ctx context.Context, projectID, credentialRef string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteCredentials", ctx, projectID, credentialRef) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteCredentials indicates an expected call of DeleteCredentials. -func (mr *MockLoadbalancerClientMockRecorder) DeleteCredentials(ctx, projectID, credentialRef any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCredentials", reflect.TypeOf((*MockLoadbalancerClient)(nil).DeleteCredentials), ctx, projectID, credentialRef) -} - -// DeleteLoadBalancer mocks base method. -func (m *MockLoadbalancerClient) DeleteLoadBalancer(ctx context.Context, projectID, name string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteLoadBalancer", ctx, projectID, name) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteLoadBalancer indicates an expected call of DeleteLoadBalancer. -func (mr *MockLoadbalancerClientMockRecorder) DeleteLoadBalancer(ctx, projectID, name any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteLoadBalancer", reflect.TypeOf((*MockLoadbalancerClient)(nil).DeleteLoadBalancer), ctx, projectID, name) -} - -// GetCredentials mocks base method. -func (m *MockLoadbalancerClient) GetCredentials(ctx context.Context, projectID, credentialRef string) (*v2api.GetCredentialsResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetCredentials", ctx, projectID, credentialRef) - ret0, _ := ret[0].(*v2api.GetCredentialsResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetCredentials indicates an expected call of GetCredentials. -func (mr *MockLoadbalancerClientMockRecorder) GetCredentials(ctx, projectID, credentialRef any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCredentials", reflect.TypeOf((*MockLoadbalancerClient)(nil).GetCredentials), ctx, projectID, credentialRef) -} - -// GetLoadBalancer mocks base method. -func (m *MockLoadbalancerClient) GetLoadBalancer(ctx context.Context, projectID, name string) (*v2api.LoadBalancer, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetLoadBalancer", ctx, projectID, name) - ret0, _ := ret[0].(*v2api.LoadBalancer) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetLoadBalancer indicates an expected call of GetLoadBalancer. -func (mr *MockLoadbalancerClientMockRecorder) GetLoadBalancer(ctx, projectID, name any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLoadBalancer", reflect.TypeOf((*MockLoadbalancerClient)(nil).GetLoadBalancer), ctx, projectID, name) -} - -// ListCredentials mocks base method. -func (m *MockLoadbalancerClient) ListCredentials(ctx context.Context, projectID string) (*v2api.ListCredentialsResponse, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListCredentials", ctx, projectID) - ret0, _ := ret[0].(*v2api.ListCredentialsResponse) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListCredentials indicates an expected call of ListCredentials. -func (mr *MockLoadbalancerClientMockRecorder) ListCredentials(ctx, projectID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListCredentials", reflect.TypeOf((*MockLoadbalancerClient)(nil).ListCredentials), ctx, projectID) -} - -// UpdateCredentials mocks base method. -func (m *MockLoadbalancerClient) UpdateCredentials(ctx context.Context, projectID, credentialRef string, payload v2api.UpdateCredentialsPayload) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateCredentials", ctx, projectID, credentialRef, payload) - ret0, _ := ret[0].(error) - return ret0 -} - -// UpdateCredentials indicates an expected call of UpdateCredentials. -func (mr *MockLoadbalancerClientMockRecorder) UpdateCredentials(ctx, projectID, credentialRef, payload any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCredentials", reflect.TypeOf((*MockLoadbalancerClient)(nil).UpdateCredentials), ctx, projectID, credentialRef, payload) -} - -// UpdateLoadBalancer mocks base method. -func (m *MockLoadbalancerClient) UpdateLoadBalancer(ctx context.Context, projectID, name string, update *v2api.UpdateLoadBalancerPayload) (*v2api.LoadBalancer, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateLoadBalancer", ctx, projectID, name, update) - ret0, _ := ret[0].(*v2api.LoadBalancer) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// UpdateLoadBalancer indicates an expected call of UpdateLoadBalancer. -func (mr *MockLoadbalancerClientMockRecorder) UpdateLoadBalancer(ctx, projectID, name, update any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateLoadBalancer", reflect.TypeOf((*MockLoadbalancerClient)(nil).UpdateLoadBalancer), ctx, projectID, name, update) -} - -// UpdateTargetPool mocks base method. -func (m *MockLoadbalancerClient) UpdateTargetPool(ctx context.Context, projectID, name, targetPoolName string, payload v2api.UpdateTargetPoolPayload) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateTargetPool", ctx, projectID, name, targetPoolName, payload) - ret0, _ := ret[0].(error) - return ret0 -} - -// UpdateTargetPool indicates an expected call of UpdateTargetPool. -func (mr *MockLoadbalancerClientMockRecorder) UpdateTargetPool(ctx, projectID, name, targetPoolName, payload any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTargetPool", reflect.TypeOf((*MockLoadbalancerClient)(nil).UpdateTargetPool), ctx, projectID, name, targetPoolName, payload) -} diff --git a/pkg/stackit/loadbalancer_test.go b/pkg/stackit/loadbalancer_test.go deleted file mode 100644 index 96a83f91..00000000 --- a/pkg/stackit/loadbalancer_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package stackit - -import ( - "context" - "net/http" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - loadbalancer "github.com/stackitcloud/stackit-sdk-go/services/loadbalancer/v2api" - "go.uber.org/mock/gomock" - "k8s.io/utils/ptr" - - mock "github.com/stackitcloud/cloud-provider-stackit/pkg/mock/loadbalancer" -) - -var _ = Describe("LBAPI Client", func() { - var ( - region = "eu01" - - mockCtrl *gomock.Controller - mockAPI *mock.MockDefaultAPI - lbClient LoadbalancerClient - ) - - BeforeEach(func() { - mockCtrl = gomock.NewController(GinkgoT()) - mockAPI = mock.NewMockDefaultAPI(mockCtrl) - - var err error - lbClient, err = NewLoadbalancerClient(mockAPI, region) - Expect(err).ToNot(HaveOccurred()) - }) - - Describe("GetLoadBalancer", func() { - It("should return the received load balancer instance", func() { - expectedName := "test LB instance" - expectedLB := &loadbalancer.LoadBalancer{Name: new(expectedName)} - mockAPI.EXPECT().GetLoadBalancer(gomock.Any(), "projectID", gomock.Any(), expectedName). - Return(loadbalancer.ApiGetLoadBalancerRequest{ApiService: mockAPI}).Times(1) - mockAPI.EXPECT().GetLoadBalancerExecute(gomock.Any()).Return(expectedLB, nil).Times(1) - - actualLB, err := lbClient.GetLoadBalancer(context.Background(), "projectID", expectedName) - Expect(err).ToNot(HaveOccurred()) - Expect(actualLB).To(Equal(expectedLB)) - actualName := ptr.Deref(actualLB.Name, "") - Expect(actualName).To(Equal(expectedName)) - }) - - It("should use the configured STACKIT region", func() { - mockAPI.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any(), region, gomock.Any()). - Return(loadbalancer.ApiGetLoadBalancerRequest{ApiService: mockAPI}).Times(1) - mockAPI.EXPECT().GetLoadBalancerExecute(gomock.Any()).Return(&loadbalancer.LoadBalancer{}, nil).Times(1) - - _, err := lbClient.GetLoadBalancer(context.Background(), "projectID", "name") - Expect(err).ToNot(HaveOccurred()) - }) - - It("should return ErrorNotFound if a GenericOpenAPIError with status 404 occurs", func() { - mockAPI.EXPECT().GetLoadBalancer(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - Return(loadbalancer.ApiGetLoadBalancerRequest{ApiService: mockAPI}).Times(1) - mockAPI.EXPECT().GetLoadBalancerExecute(gomock.Any()).Return(nil, &oapierror.GenericOpenAPIError{StatusCode: http.StatusNotFound}).Times(1) - - actualLB, err := lbClient.GetLoadBalancer(context.Background(), "projectID", "name") - Expect(actualLB).To(BeNil()) - Expect(err).To(MatchError(ErrorNotFound)) - }) - }) -}) diff --git a/pkg/stackit/server.go b/pkg/stackit/server.go deleted file mode 100644 index 69444f33..00000000 --- a/pkg/stackit/server.go +++ /dev/null @@ -1,39 +0,0 @@ -package stackit - -import ( - "context" - - iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" -) - -func (cl nodeClient) GetServer(ctx context.Context, projectID, region, serverID string) (*iaas.Server, error) { - server, err := cl.client.DefaultAPI.GetServer(ctx, projectID, region, serverID).Details(true).Execute() - if isOpenAPINotFound(err) { - return server, ErrorNotFound - } - return server, err -} - -func (cl nodeClient) DeleteServer(ctx context.Context, projectID, region, serverID string) error { - return cl.client.DefaultAPI.DeleteServer(ctx, projectID, region, serverID).Execute() -} - -func (cl nodeClient) CreateServer(ctx context.Context, projectID, region string, create *iaas.CreateServerPayload) (*iaas.Server, error) { - server, err := cl.client.DefaultAPI.CreateServer(ctx, projectID, region).CreateServerPayload(*create).Execute() - if isOpenAPINotFound(err) { - return server, ErrorNotFound - } - return server, err -} - -func (cl nodeClient) UpdateServer(ctx context.Context, projectID, region, serverID string, update *iaas.UpdateServerPayload) (*iaas.Server, error) { - return cl.client.DefaultAPI.UpdateServer(ctx, projectID, region, serverID).UpdateServerPayload(*update).Execute() -} - -func (cl nodeClient) ListServers(ctx context.Context, projectID, region string) (*[]iaas.Server, error) { - resp, err := cl.client.DefaultAPI.ListServers(ctx, projectID, region).Details(true).Execute() - if err != nil { - return nil, err - } - return &resp.Items, nil -} diff --git a/pkg/stackit/server_mock.go b/pkg/stackit/server_mock.go deleted file mode 100644 index 0de0902c..00000000 --- a/pkg/stackit/server_mock.go +++ /dev/null @@ -1,116 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ./pkg/stackit (interfaces: NodeClient) -// -// Generated by this command: -// -// mockgen -destination ./pkg/stackit/server_mock.go -package stackit ./pkg/stackit NodeClient -// - -// Package stackit is a generated GoMock package. -package stackit - -import ( - context "context" - reflect "reflect" - - v2api "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" - gomock "go.uber.org/mock/gomock" -) - -// MockNodeClient is a mock of NodeClient interface. -type MockNodeClient struct { - ctrl *gomock.Controller - recorder *MockNodeClientMockRecorder - isgomock struct{} -} - -// MockNodeClientMockRecorder is the mock recorder for MockNodeClient. -type MockNodeClientMockRecorder struct { - mock *MockNodeClient -} - -// NewMockNodeClient creates a new mock instance. -func NewMockNodeClient(ctrl *gomock.Controller) *MockNodeClient { - mock := &MockNodeClient{ctrl: ctrl} - mock.recorder = &MockNodeClientMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockNodeClient) EXPECT() *MockNodeClientMockRecorder { - return m.recorder -} - -// CreateServer mocks base method. -func (m *MockNodeClient) CreateServer(ctx context.Context, projectID, region string, create *v2api.CreateServerPayload) (*v2api.Server, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateServer", ctx, projectID, region, create) - ret0, _ := ret[0].(*v2api.Server) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// CreateServer indicates an expected call of CreateServer. -func (mr *MockNodeClientMockRecorder) CreateServer(ctx, projectID, region, create any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateServer", reflect.TypeOf((*MockNodeClient)(nil).CreateServer), ctx, projectID, region, create) -} - -// DeleteServer mocks base method. -func (m *MockNodeClient) DeleteServer(ctx context.Context, projectID, region, serverID string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteServer", ctx, projectID, region, serverID) - ret0, _ := ret[0].(error) - return ret0 -} - -// DeleteServer indicates an expected call of DeleteServer. -func (mr *MockNodeClientMockRecorder) DeleteServer(ctx, projectID, region, serverID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteServer", reflect.TypeOf((*MockNodeClient)(nil).DeleteServer), ctx, projectID, region, serverID) -} - -// GetServer mocks base method. -func (m *MockNodeClient) GetServer(ctx context.Context, projectID, region, serverID string) (*v2api.Server, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetServer", ctx, projectID, region, serverID) - ret0, _ := ret[0].(*v2api.Server) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetServer indicates an expected call of GetServer. -func (mr *MockNodeClientMockRecorder) GetServer(ctx, projectID, region, serverID any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetServer", reflect.TypeOf((*MockNodeClient)(nil).GetServer), ctx, projectID, region, serverID) -} - -// ListServers mocks base method. -func (m *MockNodeClient) ListServers(ctx context.Context, projectID, region string) (*[]v2api.Server, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ListServers", ctx, projectID, region) - ret0, _ := ret[0].(*[]v2api.Server) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// ListServers indicates an expected call of ListServers. -func (mr *MockNodeClientMockRecorder) ListServers(ctx, projectID, region any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListServers", reflect.TypeOf((*MockNodeClient)(nil).ListServers), ctx, projectID, region) -} - -// UpdateServer mocks base method. -func (m *MockNodeClient) UpdateServer(ctx context.Context, projectID, region, serverID string, update *v2api.UpdateServerPayload) (*v2api.Server, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "UpdateServer", ctx, projectID, region, serverID, update) - ret0, _ := ret[0].(*v2api.Server) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// UpdateServer indicates an expected call of UpdateServer. -func (mr *MockNodeClientMockRecorder) UpdateServer(ctx, projectID, region, serverID, update any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateServer", reflect.TypeOf((*MockNodeClient)(nil).UpdateServer), ctx, projectID, region, serverID, update) -} diff --git a/pkg/stackit/snapshots.go b/pkg/stackit/snapshots.go deleted file mode 100644 index 3dd8b5dd..00000000 --- a/pkg/stackit/snapshots.go +++ /dev/null @@ -1,133 +0,0 @@ -package stackit - -import ( - "context" - "fmt" - "net/http" - "time" - - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/stackiterrors" - "github.com/stackitcloud/stackit-sdk-go/core/runtime" - iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" - sdkWait "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api/wait" - "k8s.io/apimachinery/pkg/util/wait" -) - -const ( - SnapshotReadyStatus = "AVAILABLE" - snapReadyDuration = 1 * time.Second - snapReadyFactor = 1.2 - snapReadySteps = 10 - - SnapshotType = "type" - SnapshotAvailabilityZone = "availability" -) - -func (os *iaasClient) CreateSnapshot(ctx context.Context, name, volID string, tags map[string]string) (*iaas.Snapshot, error) { - opts := iaas.CreateSnapshotPayload{ - VolumeId: volID, - Name: new(name), - } - if tags != nil { - opts.Labels = labelsFromTags(tags) - } - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - snapshot, err := os.iaas.CreateSnapshot(ctxWithHTTPResp, os.projectID, os.region).CreateSnapshotPayload(opts).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return nil, stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return nil, err - } - - return snapshot, nil -} - -func (os *iaasClient) ListSnapshots(ctx context.Context, filters map[string]string) ([]iaas.Snapshot, string, error) { - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - // TODO: Add API filter once available. - snaps, err := os.iaas.ListSnapshotsInProject(ctxWithHTTPResp, os.projectID, os.region).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return nil, "", stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return nil, "", err - } - - filteredSnaps := filterSnapshots(snaps.Items, filters) - return filteredSnaps, "", nil -} - -func (os *iaasClient) DeleteSnapshot(ctx context.Context, snapID string) error { - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - err := os.iaas.DeleteSnapshot(ctxWithHTTPResp, os.projectID, os.region, snapID).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return err - } - return nil -} - -func (os *iaasClient) GetSnapshotByID(ctx context.Context, snapshotID string) (*iaas.Snapshot, error) { - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - snap, err := os.iaas.GetSnapshot(ctxWithHTTPResp, os.projectID, os.region, snapshotID).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return nil, stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return nil, err - } - return snap, nil -} - -func (os *iaasClient) WaitSnapshotReady(ctx context.Context, snapshotID string) (*string, error) { - backoff := wait.Backoff{ - Duration: snapReadyDuration, - Factor: snapReadyFactor, - Steps: snapReadySteps, - } - - err := wait.ExponentialBackoff(backoff, func() (bool, error) { - ready, err := os.snapshotIsReady(ctx, snapshotID) - if err != nil { - return false, err - } - return ready, nil - }) - - if wait.Interrupted(err) { - err = fmt.Errorf("timeout, Snapshot %s is still not Ready %v", snapshotID, err) - } - - snap, _ := os.GetSnapshotByID(ctx, snapshotID) - - if snap != nil { - return snap.Status, err - } - return new("Failed to get snapshot status"), err -} - -func (os *iaasClient) snapshotIsReady(ctx context.Context, snapshotID string) (bool, error) { - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - snap, err := os.iaas.GetSnapshot(ctxWithHTTPResp, os.projectID, os.region, snapshotID).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return false, stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return false, err - } - - return *snap.Status == SnapshotReadyStatus, nil -} diff --git a/pkg/stackit/snapshots_test.go b/pkg/stackit/snapshots_test.go deleted file mode 100644 index 017201d5..00000000 --- a/pkg/stackit/snapshots_test.go +++ /dev/null @@ -1,124 +0,0 @@ -package stackit - -import ( - "context" - "os" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - stackitconfig "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/config" - iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" - "go.uber.org/mock/gomock" - - mock "github.com/stackitcloud/cloud-provider-stackit/pkg/mock/iaas" -) - -var _ = Describe("Snapshot", func() { - var ( - err error - mockCtrl *gomock.Controller - mockAPI *mock.MockDefaultAPI - stackitClient IaasClient - config *stackitconfig.CSIConfig - ) - const projectID = "project-id" - const region = "eu01" - - BeforeEach(func() { - t := GinkgoT() - mockCtrl = gomock.NewController(t) - mockAPI = mock.NewMockDefaultAPI(mockCtrl) - t.Setenv("STACKIT_REGION", region) - Expect(os.Getenv("STACKIT_REGION")).To(Equal(region)) - }) - - Context("ListSnapshot", func() { - - snapShotListResponse := iaas.SnapshotListResponse{ - Items: []iaas.Snapshot{ - { - Id: new("fake-snapshot"), - Name: new("fake-snapshot"), - VolumeId: "some-special-volume", - Status: new("ERROR"), - }, - { - Id: new("fake-snapshot2"), - Name: new("fake-snapshot2"), - VolumeId: "some-special-volume", - Status: new("AVAILABLE"), - }, - { - Id: new("wrong snapshot"), - Name: new("wrong snapshot"), - VolumeId: "another-special-volume", - Status: new("AVAILABLE"), - }, - }, - } - - BeforeEach(func() { - config = &stackitconfig.CSIConfig{ - Global: stackitconfig.GlobalOpts{ - ProjectID: projectID, - }, - } - stackitClient, err = CreateSTACKITProvider(mockAPI, config) - Expect(err).ToNot(HaveOccurred()) - }) - - DescribeTable("should return a filtered list of snapshots", - func(filters map[string]string, expectedSnaps []iaas.Snapshot) { - mockAPI.EXPECT().ListSnapshotsInProject(gomock.Any(), config.Global.ProjectID, region).Return(iaas.ApiListSnapshotsInProjectRequest{ApiService: mockAPI}) - mockAPI.EXPECT().ListSnapshotsInProjectExecute(gomock.Any()).Return(&snapShotListResponse, nil) - - snaps, _, err := stackitClient.ListSnapshots(context.Background(), filters) - Expect(err).ToNot(HaveOccurred()) - Expect(snaps).To(Equal(expectedSnaps)) - }, - Entry("filter by VolumeID", - map[string]string{"VolumeID": "some-special-volume"}, - []iaas.Snapshot{ - { - Id: new("fake-snapshot"), - Name: new("fake-snapshot"), - VolumeId: "some-special-volume", - Status: new("ERROR"), - }, - { - Id: new("fake-snapshot2"), - Name: new("fake-snapshot2"), - VolumeId: "some-special-volume", - Status: new("AVAILABLE"), - }, - }, - ), - Entry("filter by name", - map[string]string{"Name": "fake-snapshot"}, - []iaas.Snapshot{ - { - Id: new("fake-snapshot"), - Name: new("fake-snapshot"), - VolumeId: "some-special-volume", - Status: new("ERROR"), - }, - }, - ), - Entry("filter by status and name", - map[string]string{"Name": "fake-snapshot2", "Status": "AVAILABLE"}, - []iaas.Snapshot{ - { - Id: new("fake-snapshot2"), - Name: new("fake-snapshot2"), - VolumeId: "some-special-volume", - Status: new("AVAILABLE"), - }, - }, - ), - Entry("no filters", - map[string]string{}, - snapShotListResponse.Items, - ), - ) - }) -}) diff --git a/pkg/stackit/types.go b/pkg/stackit/types.go deleted file mode 100644 index 1dc9c44b..00000000 --- a/pkg/stackit/types.go +++ /dev/null @@ -1,9 +0,0 @@ -package stackit - -type VolumeSourceTypes string - -const ( - VolumeSource VolumeSourceTypes = "volume" - SnapshotSource VolumeSourceTypes = "snapshot" - BackupSource VolumeSourceTypes = "backup" -) diff --git a/pkg/stackit/volumes.go b/pkg/stackit/volumes.go deleted file mode 100644 index 5813e54a..00000000 --- a/pkg/stackit/volumes.go +++ /dev/null @@ -1,344 +0,0 @@ -package stackit - -import ( - "context" - "fmt" - "net/http" - "slices" - "time" - - "github.com/stackitcloud/cloud-provider-stackit/pkg/stackit/stackiterrors" - "github.com/stackitcloud/stackit-sdk-go/core/runtime" - iaas "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api" - sdkWait "github.com/stackitcloud/stackit-sdk-go/services/iaas/v2api/wait" - "k8s.io/apimachinery/pkg/util/wait" - "k8s.io/klog/v2" - "k8s.io/utils/ptr" -) - -const ( - VolumeAvailableStatus = "AVAILABLE" - VolumeAttachedStatus = "ATTACHED" - operationFinishInitDelay = 1 * time.Second - operationFinishFactor = 1.1 - operationFinishSteps = 10 - diskAttachInitDelay = 1 * time.Second - diskAttachFactor = 1.2 - diskAttachSteps = 15 - diskDetachInitDelay = 1 * time.Second - diskDetachFactor = 1.2 - diskDetachSteps = 13 - VolumeDescription = "Created by STACKIT CSI driver" -) - -var volumeErrorStates = [...]string{"ERROR", "ERROR_RESIZING", "ERROR_DELETING"} - -func (os *iaasClient) CreateVolume(ctx context.Context, payload *iaas.CreateVolumePayload) (*iaas.Volume, error) { - payload.Description = new(VolumeDescription) - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - req, err := os.iaas.CreateVolume(ctxWithHTTPResp, os.projectID, os.region).CreateVolumePayload(*payload).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return nil, stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return nil, err - } - - return req, nil -} - -func (os *iaasClient) DeleteVolume(ctx context.Context, volumeID string) error { - used, err := os.diskIsUsed(ctx, volumeID) - if err != nil { - return err - } - if used { - return fmt.Errorf("cannot delete the volume %q, it's still attached to a node", volumeID) - } - - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - err = os.iaas.DeleteVolume(ctxWithHTTPResp, os.projectID, os.region, volumeID).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return err - } - - return err -} - -func (os *iaasClient) AttachVolume(ctx context.Context, instanceID, volumeID string) (string, error) { - volume, err := os.GetVolume(ctx, volumeID) - if err != nil { - return "", err - } - - if volume.ServerId != nil && instanceID == *volume.ServerId { - klog.V(4).Infof("Disk %s is already attached to instance %s", volumeID, instanceID) - return *volume.Id, nil - } - payload := iaas.AddVolumeToServerPayload{ - DeleteOnTermination: new(false), - } - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - _, err = os.iaas.AddVolumeToServer(ctxWithHTTPResp, os.projectID, os.region, instanceID, volumeID).AddVolumeToServerPayload(payload).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return "", stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return "", err - } - return *volume.Id, err -} - -// WaitVolumeTargetStatusWithCustomBackoff waits for volume to be in target state with custom backoff -func (os *iaasClient) WaitVolumeTargetStatusWithCustomBackoff(ctx context.Context, volumeID string, tStatus []string, backoff *wait.Backoff) error { - waitErr := wait.ExponentialBackoff(*backoff, func() (bool, error) { - vol, err := os.GetVolume(ctx, volumeID) - if err != nil { - return false, err - } - if slices.Contains(tStatus, *vol.Status) { - return true, nil - } - for _, eState := range volumeErrorStates { - if *vol.Status == eState { - return false, fmt.Errorf("volume is in error state: %s", *vol.Status) - } - } - return false, nil - }) - - if wait.Interrupted(waitErr) { - waitErr = fmt.Errorf("timeout on waiting for volume %s status to be in %v", volumeID, tStatus) - } - - return waitErr -} - -func (os *iaasClient) ListVolumes(ctx context.Context, _ int, _ string) ([]iaas.Volume, string, error) { - // TODO: Add support for pagination when IaaS adds it - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - volumes, err := os.iaas.ListVolumes(ctxWithHTTPResp, os.projectID, os.region).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return nil, "", stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return nil, "", err - } - - return volumes.Items, "", err -} - -func (os *iaasClient) WaitDiskAttached(ctx context.Context, instanceID, volumeID string) error { - backoff := wait.Backoff{ - Duration: diskAttachInitDelay, - Factor: diskAttachFactor, - Steps: diskAttachSteps, - } - - err := wait.ExponentialBackoff(backoff, func() (bool, error) { - attached, err := os.diskIsAttached(ctx, instanceID, volumeID) - if err != nil && !stackiterrors.IsNotFound(err) { - // if this is a race condition indicate the volume is deleted - // during sleep phase, ignore the error and return attach=false - return false, err - } - return attached, nil - }) - - if wait.Interrupted(err) { - err = fmt.Errorf("volume %q failed to be attached within the allowed time", volumeID) - } - - return err -} - -func (os *iaasClient) DetachVolume(ctx context.Context, instanceID, volumeID string) error { - volume, err := os.GetVolume(ctx, volumeID) - if err != nil { - return err - } - if *volume.Status == VolumeAvailableStatus { - klog.V(2).Infof("Volume: %s has been detached from compute: %s ", *volume.Id, instanceID) - return nil - } - - if *volume.Status != VolumeAttachedStatus { - return fmt.Errorf("can not detach volume %s, its status is %s", *volume.Name, *volume.Status) - } - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - if volume.ServerId != nil && *volume.ServerId == instanceID { - err = os.iaas.RemoveVolumeFromServer(ctxWithHTTPResp, os.projectID, os.region, instanceID, volumeID).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return stackiterrors.WrapErrorWithResponseID(fmt.Errorf("failed to detach volume %s from compute %s : %v", *volume.Id, instanceID, err), reqID) - } - return err - } - klog.V(2).Infof("Successfully detached volume: %s from compute: %s", *volume.Id, instanceID) - return nil - } - - // Disk has no attachments or not attached to provided compute - return nil -} - -func (os *iaasClient) WaitDiskDetached(ctx context.Context, instanceID, volumeID string) error { - backoff := wait.Backoff{ - Duration: diskDetachInitDelay, - Factor: diskDetachFactor, - Steps: diskDetachSteps, - } - - err := wait.ExponentialBackoff(backoff, func() (bool, error) { - attached, err := os.diskIsAttached(ctx, instanceID, volumeID) - if err != nil { - return false, err - } - return !attached, nil - }) - - if wait.Interrupted(err) { - err = fmt.Errorf("volume %q failed to detach within the allowed time", volumeID) - } - - return err -} - -// diskIsUsed returns true whether a disk is attached to any node -func (os *iaasClient) diskIsUsed(ctx context.Context, volumeID string) (bool, error) { - volume, err := os.GetVolume(ctx, volumeID) - if err != nil { - return false, err - } - - diskUsed := volume.ServerId != nil && *volume.ServerId != "" - - return diskUsed, nil -} - -// diskIsAttached queries if a volume is attached to a compute instance -func (os *iaasClient) diskIsAttached(ctx context.Context, instanceID, volumeID string) (bool, error) { - volume, err := os.GetVolume(ctx, volumeID) - if err != nil { - return false, err - } - - if volume.ServerId != nil && *volume.ServerId == instanceID { - return true, nil - } - return false, nil -} - -func (os *iaasClient) GetVolume(ctx context.Context, volumeID string) (*iaas.Volume, error) { - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - vol, err := os.iaas.GetVolume(ctxWithHTTPResp, os.projectID, os.region, volumeID).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return nil, stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return nil, err - } - return vol, nil -} - -func (os *iaasClient) GetVolumesByName(ctx context.Context, volName string) ([]iaas.Volume, error) { - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - // TODO: Add API filter once available. - volumes, err := os.iaas.ListVolumes(ctxWithHTTPResp, os.projectID, os.region).Execute() - if err != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return nil, stackiterrors.WrapErrorWithResponseID(err, reqID) - } - return nil, err - } - - filterMap := map[string]string{"Name": volName} - filteredVolumes := filterVolumes(volumes.Items, filterMap) - - return filteredVolumes, nil -} - -func (os *iaasClient) GetVolumeByName(ctx context.Context, name string) (*iaas.Volume, error) { - vols, err := os.GetVolumesByName(ctx, name) - if err != nil { - return nil, err - } - - if len(vols) == 0 { - return nil, stackiterrors.ErrNotFound - } - - if len(vols) > 1 { - return nil, fmt.Errorf("found %d volumes with name %q", len(vols), name) - } - - return &vols[0], nil -} - -func (os *iaasClient) WaitVolumeTargetStatus(ctx context.Context, volumeID string, tStatus []string) error { - backoff := wait.Backoff{ - Duration: operationFinishInitDelay, - Factor: operationFinishFactor, - Steps: operationFinishSteps, - } - - waitErr := wait.ExponentialBackoff(backoff, func() (bool, error) { - vol, err := os.GetVolume(ctx, volumeID) - if err != nil { - return false, err - } - if slices.Contains(tStatus, *vol.Status) { - return true, nil - } - for _, eState := range volumeErrorStates { - if *vol.Status == eState { - return false, fmt.Errorf("volume is in Error State : %s", ptr.Deref(vol.Status, "")) - } - } - return false, nil - }) - - if wait.Interrupted(waitErr) { - waitErr = fmt.Errorf("timeout on waiting for volume %s status to be in %v", volumeID, tStatus) - } - - return waitErr -} - -func (os *iaasClient) ExpandVolume(ctx context.Context, volumeID, volumeStatus string, newSize int64) error { - extendOpts := iaas.ResizeVolumePayload{Size: newSize} - var httpResp *http.Response - ctxWithHTTPResp := runtime.WithCaptureHTTPResponse(ctx, &httpResp) - - switch volumeStatus { - case VolumeAttachedStatus, VolumeAvailableStatus: - resizeErr := os.iaas.ResizeVolume(ctxWithHTTPResp, os.projectID, os.region, volumeID).ResizeVolumePayload(extendOpts).Execute() - if resizeErr != nil { - if httpResp != nil { - reqID := httpResp.Header.Get(sdkWait.XRequestIDHeader) - return stackiterrors.WrapErrorWithResponseID(resizeErr, reqID) - } - return resizeErr - } - return nil - default: - return fmt.Errorf("volume cannot be resized, when status is %s", volumeStatus) - } -}